From fd139652e69fe078bf2bc5d9b88cc8225e9a7dc3 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 5 Jun 2024 10:19:26 -0500 Subject: [PATCH 01/12] test mirror sync --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e4cbdc..0d3945b 100644 --- a/README.md +++ b/README.md @@ -47,4 +47,4 @@ or to run everything including the guardrails-api -The last option is useful when checking that everything will work as planned in a more productionized environment. When developing, it's generally faster to just run the minimum infrastructure you need via docker and run the api on a bare process with the `make start` command. \ No newline at end of file +The last option is useful when checking that everything will work as planned in a more productionized environment. When developing, it's generally faster to just run the minimum infrastructure you need via docker and run the api on a bare process with the `make start` command. From 5211a2ebf9ca10ee154717d5d61996dd27a35e9a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 5 Jun 2024 10:32:19 -0500 Subject: [PATCH 02/12] cleanup --- .flake8 | 4 - .gitignore | 1 + ApiClientLibraries.md | 93 - Dockerfile.dev | 51 - Dockerfile.heavy | 48 - Dockerfile.lite | 55 - Dockerfile.prod | 41 - Makefile | 2 +- buildspecs/build.sh | 53 - buildspecs/deploy.yml | 8 - buildspecs/heavy-build.sh | 5 - buildspecs/hub-install.sh | 14 - cdktf/.gitignore | 11 - cdktf/__tests__/main-test.ts | 89 - cdktf/help | 51 - cdktf/index.ts | 1 - cdktf/package-lock.json | 4449 ----------------- cdktf/package.json | 34 - cdktf/src/configs/allow-all-egress.ts | 13 - cdktf/src/configs/base-construct-config.ts | 11 - cdktf/src/configs/index.ts | 3 - cdktf/src/configs/open-search-config.ts | 24 - cdktf/src/constructs/compute-service.ts | 133 - cdktf/src/constructs/deployment-pipeline.ts | 351 -- cdktf/src/constructs/gateway.ts | 99 - cdktf/src/constructs/index.ts | 2 - cdktf/src/constructs/rds-postgres.ts | 199 - cdktf/src/constructs/task.ts | 185 - cdktf/src/index.ts | 2 - cdktf/src/stacks/index.ts | 1 - cdktf/src/stacks/main.ts | 14 - cdktf/src/substacks/index.ts | 1 - cdktf/src/substacks/main.ts | 340 -- cdktf/src/utils/index.ts | 1 - cdktf/src/utils/truncate.ts | 24 - cdktf/tsconfig.json | 35 - custom-install/install.py | 113 - .../manifests/competitor-check.json | 46 - custom-install/manifests/detect-pii.json | 49 - custom-install/manifests/jailbreak.json | 49 - custom-install/manifests/provenance-llm.json | 49 - .../manifests/restrict-to-topic.json | 49 - default.env | 7 + dev-build.sh | 12 - dev-run.sh | 3 - dev.sh | 16 - diagrams/GuardRails Infra - Option 1.drawio | 166 - diagrams/GuardRails Infra - Option 1.png | Bin 133374 -> 0 bytes diagrams/GuardRails Infra - Option 2.drawio | 176 - diagrams/GuardRails Infra - Option 2.png | Bin 153773 -> 0 bytes guard-rail-spec.xml | 18 - guard.json | 65 - heavy-run.sh | 3 - hub-requirements.txt | 6 - ingestion-service-spec.yml | 117 - lite-build.sh | 12 - lite-run.sh | 3 - local.sh | 52 - name-guard.json | 41 - object-string-guard.json | 21 - prod-build.sh | 18 - prod-run.sh | 8 - refresh.sh | 18 - requests.http | 16 - sdk-test.py | 7 - start.sh | 13 + string-guard-rail-spec.xml | 11 - string-guard.json | 19 - test.ipynb | 826 --- 69 files changed, 22 insertions(+), 8435 deletions(-) delete mode 100644 .flake8 delete mode 100644 ApiClientLibraries.md delete mode 100644 Dockerfile.dev delete mode 100644 Dockerfile.heavy delete mode 100644 Dockerfile.lite delete mode 100644 Dockerfile.prod delete mode 100644 buildspecs/build.sh delete mode 100644 buildspecs/deploy.yml delete mode 100644 buildspecs/heavy-build.sh delete mode 100644 buildspecs/hub-install.sh delete mode 100644 cdktf/.gitignore delete mode 100644 cdktf/__tests__/main-test.ts delete mode 100644 cdktf/help delete mode 100644 cdktf/index.ts delete mode 100644 cdktf/package-lock.json delete mode 100644 cdktf/package.json delete mode 100644 cdktf/src/configs/allow-all-egress.ts delete mode 100644 cdktf/src/configs/base-construct-config.ts delete mode 100644 cdktf/src/configs/index.ts delete mode 100644 cdktf/src/configs/open-search-config.ts delete mode 100644 cdktf/src/constructs/compute-service.ts delete mode 100644 cdktf/src/constructs/deployment-pipeline.ts delete mode 100644 cdktf/src/constructs/gateway.ts delete mode 100644 cdktf/src/constructs/index.ts delete mode 100644 cdktf/src/constructs/rds-postgres.ts delete mode 100644 cdktf/src/constructs/task.ts delete mode 100644 cdktf/src/index.ts delete mode 100644 cdktf/src/stacks/index.ts delete mode 100644 cdktf/src/stacks/main.ts delete mode 100644 cdktf/src/substacks/index.ts delete mode 100644 cdktf/src/substacks/main.ts delete mode 100644 cdktf/src/utils/index.ts delete mode 100644 cdktf/src/utils/truncate.ts delete mode 100644 cdktf/tsconfig.json delete mode 100644 custom-install/install.py delete mode 100644 custom-install/manifests/competitor-check.json delete mode 100644 custom-install/manifests/detect-pii.json delete mode 100644 custom-install/manifests/jailbreak.json delete mode 100644 custom-install/manifests/provenance-llm.json delete mode 100644 custom-install/manifests/restrict-to-topic.json create mode 100644 default.env delete mode 100644 dev-build.sh delete mode 100644 dev-run.sh delete mode 100644 dev.sh delete mode 100644 diagrams/GuardRails Infra - Option 1.drawio delete mode 100644 diagrams/GuardRails Infra - Option 1.png delete mode 100644 diagrams/GuardRails Infra - Option 2.drawio delete mode 100644 diagrams/GuardRails Infra - Option 2.png delete mode 100644 guard-rail-spec.xml delete mode 100644 guard.json delete mode 100644 heavy-run.sh delete mode 100644 hub-requirements.txt delete mode 100644 ingestion-service-spec.yml delete mode 100755 lite-build.sh delete mode 100755 lite-run.sh delete mode 100755 local.sh delete mode 100644 name-guard.json delete mode 100644 object-string-guard.json delete mode 100644 prod-build.sh delete mode 100644 prod-run.sh delete mode 100644 refresh.sh delete mode 100644 requests.http delete mode 100644 sdk-test.py create mode 100755 start.sh delete mode 100644 string-guard-rail-spec.xml delete mode 100644 string-guard.json delete mode 100644 test.ipynb diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 599bafc..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -count = True -statistics = True -max-line-length = 88 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 35218b2..0853ece 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ guardrails-custom-validators guardrails-api-client guardrails *.env +!default.env opentelemetry-lambda-layer open-api-spec.json open-api-spec.yml diff --git a/ApiClientLibraries.md b/ApiClientLibraries.md deleted file mode 100644 index df38e81..0000000 --- a/ApiClientLibraries.md +++ /dev/null @@ -1,93 +0,0 @@ -# API Client Libraries -Comparing our options for auto-generating client libraries from our OpenAPI Spec. - -## Auto-rest -Homepage: https://github.com/Azure/autorest/blob/main/docs/readme.md - -See setup here: https://github.com/tinystacks/guardrails-poc/blob/autorest-sdk/build-sdk.sh -and usage here: https://github.com/tinystacks/guardrails-poc/blob/autorest-sdk/sdk-test.py -### Summary - - Open source (MIT License) - - Developed by Microsoft - - Uses mostly native libraries with a few Azure libraries - - Runs in Node - -### Pros - - Lightweight: can be run via npx in any Node environment >= 12.x - - Supports most OpenAPI Spec features - - Supported by Microsoft since 2016 - - Simple setup and config - - Can generate base setup.py file for packaging -### Cons - - Requires some Azure libraries - - Forces use of Azure credential classes for setting auth headers - - Requires custom property on parameters to prevent them from being considered global - - Does not generate pyproject.toml - - Responses are generic JSON, not strongly typed models - -## OpenAPITools openapi-generator -Homepage: https://openapi-generator.tech/ - -See setup here: https://github.com/tinystacks/guardrails-poc/blob/open-api-generator/build-sdk.sh -and usage here: https://github.com/tinystacks/guardrails-poc/blob/open-api-generator/sdk-test.py -### Summary - - Open source (Apache License) - - Fork of Swagger Codegen - - Runs in JVM - -### Pros - - Extensive support of OpenAPI Specification - - Highly configurable - - Likely the most mature OpenAPI code generator - - Generates usage docs in markdown for the client - -### Cons - - Requires JVM to run - - Specifically requires Java 11 for generating python clients - - Historically, migrating between major version is toilsome - - Documenation is extensive but difficult to navigate - - Does not generate pyproject.toml - - Responses are not easily serializeable - - Boilerplate for making a call is excessive - - Passing parameters is unintuitive and not well typed - - !!! Cannot install alongside the guardrails-ai package !!! See error below - -### Issues -The client generated with the latest version of openapi-generator has clashing dependencies with the guardrails-ai package. - -```bash -ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. -guardrails-api-client 1.0.0 requires typing_extensions~=4.3.0, but you have typing-extensions 4.7.1 which is incompatible. -``` - -This might be resolvable by downgrading to a previous version but that is not a desireable outcome. - - -## openapi-python-client -Homepage: https://github.com/openapi-generators/openapi-python-client#openapi-python-client - -See setup here: https://github.com/tinystacks/guardrails-poc/blob/openapi-python-client/build-sdk.sh -and usage here: https://github.com/tinystacks/guardrails-poc/blob/openapi-python-client/sdk-test.py -### Summary - - Open source (MIT License) - - Runs in Python - -### Pros - - Python native - - Can use Poetry or Setuptools to package - - Generates pyproject.toml - - Generates Models along with client - - Models have builtin `to_dict` methods - -### Cons - - Doesn't support "default" responses key (expects an HTTP Code/number and therefore generates build time warning) - - Strict about schema contract (i.e. not required !== nullable) - - -## Conclusion -Given the options above, I would currently suggest using `openapi-python-client` for the following reasons: - - Python native and doesn't require other runtimes to perform code generation - - Output is packageable by default - - Non-opinionated auth setup - - Models can be easily converted to dictionaries and therefore json serializeable - - Can be installed adjacent to the existing guardrails-ai package \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev deleted file mode 100644 index 3fc7a6c..0000000 --- a/Dockerfile.dev +++ /dev/null @@ -1,51 +0,0 @@ -FROM public.ecr.aws/docker/library/python:3.11.6-slim - -ARG GITHUB_TOKEN -ARG HF_TOKEN - -# COPY .guardrailsrc /root/.guardrailsrc - -# Create app directory -WORKDIR /app - -# check the version -RUN python3 --version -# start the virtual environment -RUN python3 -m venv /opt/venv - -# Enable venv -ENV PATH="/opt/venv/bin:$PATH" - -# Install gcc -RUN apt-get update -RUN apt-get install -y git gcc - -# COPY ./guardrails-sdk ./guardrails-sdk - -# COPY ./guard-rails-api-client ./guard-rails-api-client - -# COPY ./guardrails-custom-validators ./guardrails-custom-validators - -# Copy the requirements file -COPY requirements*.txt . - -# RUN pip install ./guardrails-sdk -# RUN pip install ./guard-rails-api-client - -# Install app dependencies -RUN pip install -r requirements-lock.txt - -# Download punkt data -RUN python -m nltk.downloader -d /opt/nltk_data punkt - -RUN guardrails hub install hub://guardrails/valid_length - -# Freeze dependencies -RUN pip freeze > requirements-lock.txt - -# Copy the whole folder inside the Image filesystem -COPY . . - -EXPOSE 8000 - -CMD gunicorn --bind 0.0.0.0:8000 --timeout=90 --threads=10 "app:create_app()" diff --git a/Dockerfile.heavy b/Dockerfile.heavy deleted file mode 100644 index 02273f4..0000000 --- a/Dockerfile.heavy +++ /dev/null @@ -1,48 +0,0 @@ -FROM public.ecr.aws/docker/library/python:3.11.6-slim - -ARG GITHUB_TOKEN -ARG HF_TOKEN - -# COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter -# COPY ./opentelemetry-lambda-layer /opt - -# Create app directory -WORKDIR /app - -# check the version -RUN python3 --version -# start the virtual environment -RUN python3 -m venv /opt/venv - -# Enable venv -ENV PATH="/opt/venv/bin:$PATH" - -# Install git and curl -RUN apt-get update -RUN apt-get install -y git curl gcc jq - -# Copy the requirements file -COPY requirements*.txt . - -RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem - -# Install app dependencies -RUN pip install -r requirements-lock.txt - -# Download punkt data -RUN python -m nltk.downloader -d /opt/nltk_data punkt - -COPY buildspecs/hub-install.sh ./buildspecs/hub-install.sh -RUN chmod +x ./buildspecs/hub-install.sh -RUN ./buildspecs/hub-install.sh - -# Freeze dependencies -RUN pip freeze > requirements-lock.txt - -# Copy the whole folder inside the Image filesystem -COPY . . - -EXPOSE 8000 - -CMD gunicorn --bind 0.0.0.0:8000 --timeout=90 --threads=10 "app:create_app()" -# CMD gunicorn --forwarded-allow-ips="*" --bind 0.0.0.0:8000 --timeout=60 --threads=10 "app:create_app()" diff --git a/Dockerfile.lite b/Dockerfile.lite deleted file mode 100644 index 59094d6..0000000 --- a/Dockerfile.lite +++ /dev/null @@ -1,55 +0,0 @@ -FROM public.ecr.aws/docker/library/python:3.11.6-slim - -ARG GITHUB_TOKEN -ARG HF_TOKEN - -COPY .guardrailsrc /root/.guardrailsrc - -# COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter -# COPY ./opentelemetry-lambda-layer /opt - -# Create app directory -WORKDIR /app - -# check the version -RUN python3 --version -# start the virtual environment -RUN python3 -m venv /opt/venv - -# Enable venv -ENV PATH="/opt/venv/bin:$PATH" - -# Install git and curl -RUN apt-get update -RUN apt-get install -y git curl gcc jq - -# Copy the requirements file -COPY requirements*.txt . - -RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem - -# Install app dependencies -RUN pip install -r requirements-lock.txt - -# Download punkt data -RUN python -m nltk.downloader -d /opt/nltk_data punkt - -# RUN guardrails hub install hub://guardrails/profanity_free -RUN guardrails hub install hub://guardrails/valid_length -RUN guardrails hub install hub://guardrails/lowercase -RUN guardrails hub install hub://guardrails/regex_match - -COPY ./custom-install ./custom-install - -RUN python ./custom-install/install.py - -# Freeze dependencies -RUN pip freeze > requirements-lock.txt - -# Copy the whole folder inside the Image filesystem -COPY . . - -EXPOSE 8000 - -CMD gunicorn --bind 0.0.0.0:8000 --timeout=90 --threads=10 --limit-request-line=0 --limit-request-fields=1000 --limit-request-field_size=0 "app:create_app()" -# CMD gunicorn --forwarded-allow-ips="*" --bind 0.0.0.0:8000 --timeout=60 --threads=10 "app:create_app()" diff --git a/Dockerfile.prod b/Dockerfile.prod deleted file mode 100644 index cbd4e41..0000000 --- a/Dockerfile.prod +++ /dev/null @@ -1,41 +0,0 @@ -FROM public.ecr.aws/docker/library/python:3.11.6-slim - -# COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter -# COPY ./opentelemetry-lambda-layer /opt - -# Create app directory -WORKDIR /app - -# check the version -RUN python3 --version -# start the virtual environment -RUN python3 -m venv /opt/venv - -# Enable venv -ENV PATH="/opt/venv/bin:$PATH" - -# Install git and curl -RUN apt-get update -RUN apt-get install -y git curl gcc - -# Copy the requirements file -COPY requirements*.txt . - -RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem - -# Install app dependencies -RUN pip install -r requirements-lock.txt - -# Download punkt data -RUN python -m nltk.downloader -d /opt/nltk_data punkt - -# Freeze dependencies -RUN pip freeze > requirements-lock.txt - -# Copy the whole folder inside the Image filesystem -COPY . . - -EXPOSE 8000 - -CMD gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" -# CMD gunicorn --forwarded-allow-ips="*" --bind 0.0.0.0:8000 --timeout=60 --threads=10 "app:create_app()" diff --git a/Makefile b/Makefile index 4113a87..fc5dda8 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ install-lock: pip install -r requirements-lock.txt start: - bash ./local.sh + bash start.sh infra: docker compose --profile infra up --build diff --git a/buildspecs/build.sh b/buildspecs/build.sh deleted file mode 100644 index 1c1df36..0000000 --- a/buildspecs/build.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -callerIdentity=$(aws sts get-caller-identity); -accountId=$(jq -r .Account <<< $callerIdentity); -imageName="${IMAGE_NAME:-validation-service}"; -repoName="${ECR_REPO_NAME:-guardrails-validation-service-test}"; -commitSha=$(git rev-parse HEAD); -region="${AWS_DEFAULT_REGION:-us-east-1}"; -dockerfile="${DOCKERFILE:-Dockerfile.prod}"; -defaultEcrEndpoint="${accountId}.dkr.ecr.${region}.amazonaws.com"; -ecrEndpoint="${ECR_ENDPOINT:-$defaultEcrEndpoint}" -ecrImageUrl="${ecrEndpoint}/${repoName}"; - -# Fetch latest API Specification -curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml -# Dereference API Spec to JSON -npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - - -# Setup unpublished api client -# echo "Building api client..." -# bash build-sdk.sh - -# Building OTEL Collector extension -# if [ -d "opentelemetry-lambda-layer" ]; then -# rm -rf opentelemetry-lambda-layer -# fi - -# curl $(aws lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-collector-arm64-0_2_0:1 --query 'Content.Location' --output text) --output otel-collector.zip -# unzip otel-collector.zip -d ./opentelemetry-lambda-layer -# rm otel-collector.zip - -echo "Performing docker build" -docker login -u AWS -p $(aws ecr get-login-password --region $region) $ecrEndpoint; - -docker buildx build \ - --platform linux/arm64 \ - --progress plain \ - --no-cache \ - --build-arg CACHEBUST="$(date)" \ - --build-arg GITHUB_TOKEN="$GITHUB_TOKEN" \ - --build-arg HF_TOKEN="$HF_TOKEN" \ - -f "$DOCKERFILE" \ - -t "$imageName:$commitSha" \ - -t "$imageName:latest" . \ - || exit 1; - # > ./out.log 2>&1 \ - -docker image tag "$imageName:$commitSha" "$ecrImageUrl:$commitSha"; -docker image tag "$imageName:latest" "$ecrImageUrl:latest"; - -# echo "Publishing to ECR" -docker push $ecrImageUrl --all-tags; \ No newline at end of file diff --git a/buildspecs/deploy.yml b/buildspecs/deploy.yml deleted file mode 100644 index a52b100..0000000 --- a/buildspecs/deploy.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 0.2 - -phases: - build: - commands: - - echo "Deploying $ECR_ENDPOINT:$IMAGE_VERSION_TAG to ECS Service $CLUSTER_ARN:$SERVICE_NAME" - - aws ecs update-service --service $SERVICE_NAME --cluster $CLUSTER_ARN --desired-count $TASK_COUNT --force-new-deployment - - echo "ECS Deployment started..." \ No newline at end of file diff --git a/buildspecs/heavy-build.sh b/buildspecs/heavy-build.sh deleted file mode 100644 index 67242f6..0000000 --- a/buildspecs/heavy-build.sh +++ /dev/null @@ -1,5 +0,0 @@ -export IMAGE_NAME="graas-heavy" -export ECR_REPO_NAME="guardrails-as-a-service-heavy-test" -export DOCKERFILE="Dockerfile.heavy" - -bash buildspecs/build.sh \ No newline at end of file diff --git a/buildspecs/hub-install.sh b/buildspecs/hub-install.sh deleted file mode 100644 index 43353ea..0000000 --- a/buildspecs/hub-install.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -echo " !!!!!!!!!!!! START HUB INSTALL !!!!!!!!!!!!" - -index_json=$(curl -H "Authorization: token $GITHUB_TOKEN" https://raw.githubusercontent.com/guardrails-ai/guardrails-hub/main/index.json) - -jq -r '.[].id' <<< $index_json | while read -r validator_id; do - # Cannot use wiki_provenance bc chromadb doesn't install pysqlite3 correctly - if [ "$validator_id" != "guardrails/wiki_provenance" ]; then - guardrails hub install "hub://${validator_id}" || exit 1 - fi -done - -echo " !!!!!!!!!!!! END HUB INSTALL !!!!!!!!!!!!" \ No newline at end of file diff --git a/cdktf/.gitignore b/cdktf/.gitignore deleted file mode 100644 index 1dfae30..0000000 --- a/cdktf/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.d.ts -*.js -node_modules -cdktf.out -cdktf.log -*terraform.*.tfstate* -.gen -.terraform -tsconfig.tsbuildinfo -!jest.config.js -!setup.js \ No newline at end of file diff --git a/cdktf/__tests__/main-test.ts b/cdktf/__tests__/main-test.ts deleted file mode 100644 index 5498de8..0000000 --- a/cdktf/__tests__/main-test.ts +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) HashiCorp, Inc -// SPDX-License-Identifier: MPL-2.0 -import "cdktf/lib/testing/adapters/jest"; // Load types for expect matchers -// import { Testing } from "cdktf"; - -describe("My CDKTF Application", () => { - // The tests below are example tests, you can find more information at - // https://cdk.tf/testing - it.todo("should be tested"); - - // // All Unit tests test the synthesised terraform code, it does not create real-world resources - // describe("Unit testing using assertions", () => { - // it("should contain a resource", () => { - // // import { Image,Container } from "./.gen/providers/docker" - // expect( - // Testing.synthScope((scope) => { - // new MyApplicationsAbstraction(scope, "my-app", {}); - // }) - // ).toHaveResource(Container); - - // expect( - // Testing.synthScope((scope) => { - // new MyApplicationsAbstraction(scope, "my-app", {}); - // }) - // ).toHaveResourceWithProperties(Image, { name: "ubuntu:latest" }); - // }); - // }); - - // describe("Unit testing using snapshots", () => { - // it("Tests the snapshot", () => { - // const app = Testing.app(); - // const stack = new TerraformStack(app, "test"); - - // new TestProvider(stack, "provider", { - // accessKey: "1", - // }); - - // new TestResource(stack, "test", { - // name: "my-resource", - // }); - - // expect(Testing.synth(stack)).toMatchSnapshot(); - // }); - - // it("Tests a combination of resources", () => { - // expect( - // Testing.synthScope((stack) => { - // new TestDataSource(stack, "test-data-source", { - // name: "foo", - // }); - - // new TestResource(stack, "test-resource", { - // name: "bar", - // }); - // }) - // ).toMatchInlineSnapshot(); - // }); - // }); - - // describe("Checking validity", () => { - // it("check if the produced terraform configuration is valid", () => { - // const app = Testing.app(); - // const stack = new TerraformStack(app, "test"); - - // new TestDataSource(stack, "test-data-source", { - // name: "foo", - // }); - - // new TestResource(stack, "test-resource", { - // name: "bar", - // }); - // expect(Testing.fullSynth(app)).toBeValidTerraform(); - // }); - - // it("check if this can be planned", () => { - // const app = Testing.app(); - // const stack = new TerraformStack(app, "test"); - - // new TestDataSource(stack, "test-data-source", { - // name: "foo", - // }); - - // new TestResource(stack, "test-resource", { - // name: "bar", - // }); - // expect(Testing.fullSynth(app)).toPlanSuccessfully(); - // }); - // }); -}); diff --git a/cdktf/help b/cdktf/help deleted file mode 100644 index 716be8d..0000000 --- a/cdktf/help +++ /dev/null @@ -1,51 +0,0 @@ -======================================================================================================== - - Your CDKTF TypeScript project is ready! - - cat help Print this message - - Compile: - npm run get Import/update Terraform providers and modules (you should check-in this directory) - npm run compile Compile typescript code to javascript (or "npm run watch") - npm run watch Watch for changes and compile typescript in the background - npm run build Compile typescript - - Synthesize: - cdktf synth [stack] Synthesize Terraform resources from stacks to cdktf.out/ (ready for 'terraform apply') - - Diff: - cdktf diff [stack] Perform a diff (terraform plan) for the given stack - - Deploy: - cdktf deploy [stack] Deploy the given stack - - Destroy: - cdktf destroy [stack] Destroy the stack - - Test: - npm run test Runs unit tests (edit __tests__/main-test.ts to add your own tests) - npm run test:watch Watches the tests and reruns them on change - - Upgrades: - npm run upgrade Upgrade cdktf modules to latest version - npm run upgrade:next Upgrade cdktf modules to latest "@next" version (last commit) - - Use Providers: - - You can add prebuilt providers (if available) or locally generated ones using the add command: - - cdktf provider add "aws@~>3.0" null kreuzwerker/docker - - You can find all prebuilt providers on npm: https://www.npmjs.com/search?q=keywords:cdktf - You can also install these providers directly through npm: - - npm install @cdktf/provider-aws - npm install @cdktf/provider-google - npm install @cdktf/provider-azurerm - npm install @cdktf/provider-docker - npm install @cdktf/provider-github - npm install @cdktf/provider-null - - You can also build any module or provider locally. Learn more https://cdk.tf/modules-and-providers - -======================================================================================================== diff --git a/cdktf/index.ts b/cdktf/index.ts deleted file mode 100644 index 8879d7a..0000000 --- a/cdktf/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './src'; \ No newline at end of file diff --git a/cdktf/package-lock.json b/cdktf/package-lock.json deleted file mode 100644 index 00d7626..0000000 --- a/cdktf/package-lock.json +++ /dev/null @@ -1,4449 +0,0 @@ -{ - "name": "cdktf", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cdktf", - "version": "1.0.0", - "license": "MPL-2.0", - "dependencies": { - "cdktf": "^0.19.1", - "constructs": "^10.3.0" - }, - "devDependencies": { - "@types/jest": "^29.5.8", - "@types/node": "^20.9.0", - "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "^5.2.2" - }, - "engines": { - "node": ">=18.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "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.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "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, - "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, - "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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "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, - "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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "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, - "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, - "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, - "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, - "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, - "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, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.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, - "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.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "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 - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "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 - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "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, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "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 - }, - "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 - }, - "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 - }, - "node_modules/@types/babel__core": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", - "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", - "dev": true, - "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.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "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, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "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, - "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 - }, - "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, - "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, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "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 - }, - "node_modules/@types/yargs": { - "version": "17.0.31", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", - "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", - "dev": true, - "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 - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", - "dev": true, - "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, - "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, - "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, - "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, - "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 - }, - "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, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "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, - "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, - "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, - "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, - "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.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@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-top-level-await": "^7.8.3" - }, - "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, - "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 - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "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" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - }, - "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, - "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, - "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 - }, - "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, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001561", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", - "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", - "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" - } - ] - }, - "node_modules/cdktf": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/cdktf/-/cdktf-0.19.1.tgz", - "integrity": "sha512-scZhp2+FEgNUd+l5vaDCHABdwFApB1Lcknn2+dUw8aYwNsMoYT0tWs4AzPg22Z4jQFOIQLIXmBxifhr+RahdRg==", - "bundleDependencies": [ - "archiver", - "json-stable-stringify", - "semver" - ], - "dependencies": { - "archiver": "5.3.2", - "json-stable-stringify": "^1.0.2", - "semver": "^7.5.4" - }, - "peerDependencies": { - "constructs": "^10.0.25" - } - }, - "node_modules/cdktf/node_modules/archiver": { - "version": "5.3.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^2.1.0", - "async": "^3.2.4", - "buffer-crc32": "^0.2.1", - "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^2.2.0", - "zip-stream": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cdktf/node_modules/archiver-utils": { - "version": "2.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cdktf/node_modules/archiver-utils/node_modules/readable-stream": { - "version": "2.3.7", - "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/cdktf/node_modules/archiver-utils/node_modules/string_decoder": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/cdktf/node_modules/async": { - "version": "3.2.4", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/balanced-match": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/base64-js": { - "version": "1.5.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/bl": { - "version": "4.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/cdktf/node_modules/brace-expansion": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cdktf/node_modules/buffer": { - "version": "5.7.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/cdktf/node_modules/buffer-crc32": { - "version": "0.2.13", - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/cdktf/node_modules/compress-commons": { - "version": "4.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^4.0.2", - "normalize-path": "^3.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cdktf/node_modules/concat-map": { - "version": "0.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/core-util-is": { - "version": "1.0.3", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/crc-32": { - "version": "1.2.2", - "inBundle": true, - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cdktf/node_modules/crc32-stream": { - "version": "4.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "crc-32": "^1.2.0", - "readable-stream": "^3.4.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cdktf/node_modules/end-of-stream": { - "version": "1.4.4", - "inBundle": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/cdktf/node_modules/fs-constants": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/fs.realpath": { - "version": "1.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/cdktf/node_modules/glob": { - "version": "7.2.3", - "inBundle": 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/cdktf/node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/cdktf/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/cdktf/node_modules/graceful-fs": { - "version": "4.2.10", - "inBundle": true, - "license": "ISC" - }, - "node_modules/cdktf/node_modules/ieee754": { - "version": "1.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "BSD-3-Clause" - }, - "node_modules/cdktf/node_modules/inflight": { - "version": "1.0.6", - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/cdktf/node_modules/inherits": { - "version": "2.0.4", - "inBundle": true, - "license": "ISC" - }, - "node_modules/cdktf/node_modules/isarray": { - "version": "1.0.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/json-stable-stringify": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonify": "^0.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cdktf/node_modules/jsonify": { - "version": "0.0.1", - "inBundle": true, - "license": "Public Domain", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cdktf/node_modules/lazystream": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "readable-stream": "^2.0.5" - }, - "engines": { - "node": ">= 0.6.3" - } - }, - "node_modules/cdktf/node_modules/lazystream/node_modules/readable-stream": { - "version": "2.3.7", - "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/cdktf/node_modules/lazystream/node_modules/string_decoder": { - "version": "1.1.1", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/cdktf/node_modules/lodash.defaults": { - "version": "4.2.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/lodash.difference": { - "version": "4.5.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/lodash.flatten": { - "version": "4.4.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/lodash.isplainobject": { - "version": "4.0.6", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/lodash.union": { - "version": "4.6.0", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/lru-cache": { - "version": "6.0.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cdktf/node_modules/minimatch": { - "version": "5.1.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cdktf/node_modules/normalize-path": { - "version": "3.0.0", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cdktf/node_modules/once": { - "version": "1.4.0", - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/cdktf/node_modules/path-is-absolute": { - "version": "1.0.1", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cdktf/node_modules/process-nextick-args": { - "version": "2.0.1", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/readable-stream": { - "version": "3.6.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cdktf/node_modules/readdir-glob": { - "version": "1.1.3", - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/cdktf/node_modules/safe-buffer": { - "version": "5.1.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/semver": { - "version": "7.5.4", - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cdktf/node_modules/string_decoder": { - "version": "1.3.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/cdktf/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/tar-stream": { - "version": "2.2.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cdktf/node_modules/util-deprecate": { - "version": "1.0.2", - "inBundle": true, - "license": "MIT" - }, - "node_modules/cdktf/node_modules/wrappy": { - "version": "1.0.2", - "inBundle": true, - "license": "ISC" - }, - "node_modules/cdktf/node_modules/yallist": { - "version": "4.0.0", - "inBundle": true, - "license": "ISC" - }, - "node_modules/cdktf/node_modules/zip-stream": { - "version": "4.1.0", - "inBundle": true, - "license": "MIT", - "dependencies": { - "archiver-utils": "^2.1.0", - "compress-commons": "^4.1.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "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, - "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, - "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" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "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, - "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, - "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 - }, - "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, - "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 - }, - "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 - }, - "node_modules/constructs": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", - "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", - "engines": { - "node": ">= 16.14.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 - }, - "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, - "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 - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "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, - "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, - "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, - "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, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.578", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.578.tgz", - "integrity": "sha512-V0ZhSu1BQZKfG0yNEL6Dadzik8E1vAzfpVOapdSiT9F6yapEJ3Bk+4tZ4SMPdWiUchCgnM/ByYtBzp5ntzDMIA==", - "dev": true - }, - "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, - "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 - }, - "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, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "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, - "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, - "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, - "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, - "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 - }, - "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, - "dependencies": { - "bser": "2.1.1" - } - }, - "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==", - "dev": true, - "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, - "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 - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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==", - "dev": true, - "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, - "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 - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "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 - }, - "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, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "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, - "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==", - "dev": true, - "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 - }, - "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 - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "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, - "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, - "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, - "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, - "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 - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/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==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/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==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "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, - "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, - "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.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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/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==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/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==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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 - }, - "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, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "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 - }, - "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, - "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, - "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, - "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 - }, - "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, - "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 - }, - "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, - "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, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/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==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/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==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "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 - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "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 - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "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, - "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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "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 - }, - "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 - }, - "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 - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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 - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "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, - "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, - "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, - "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, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "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, - "bin": { - "semver": "bin/semver.js" - } - }, - "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, - "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, - "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 - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "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, - "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, - "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 - }, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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, - "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 - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/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==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/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==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "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, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "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" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "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 - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", - "dev": true, - "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, - "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, - "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, - "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 - }, - "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, - "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, - "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 - }, - "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, - "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, - "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, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/cdktf/package.json b/cdktf/package.json deleted file mode 100644 index a9a5a0c..0000000 --- a/cdktf/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "guardrails-validation-cdktf", - "version": "1.0.0", - "main": "index.ts", - "types": "index.ts", - "license": "MPL-2.0", - "private": true, - "scripts": { - "get": "cdktf get", - "build": "tsc", - "synth": "cdktf synth", - "compile": "tsc --pretty", - "watch": "tsc -w", - "test": "jest", - "test:watch": "jest --watch", - "upgrade": "npm i cdktf@latest cdktf-cli@latest", - "upgrade:next": "npm i cdktf@next cdktf-cli@next" - }, - "engines": { - "node": ">=18.0" - }, - "dependencies": { - "cdktf": "^0.19.1", - "constructs": "^10.3.0" - }, - "devDependencies": { - "@types/jest": "^29.5.8", - "@types/node": "^20.9.0", - "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "ts-node": "^10.9.1", - "typescript": "^5.2.2" - } -} diff --git a/cdktf/src/configs/allow-all-egress.ts b/cdktf/src/configs/allow-all-egress.ts deleted file mode 100644 index d2450a8..0000000 --- a/cdktf/src/configs/allow-all-egress.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { - securityGroup as securityGroupLib -} from '@cdktf/provider-aws'; - -import SecurityGroupEgress = securityGroupLib.SecurityGroupEgress; - -export const ALLOW_ALL_EGRESS: SecurityGroupEgress = { - fromPort: 0, - toPort: 0, - protocol: '-1', - cidrBlocks: ['0.0.0.0/0'], - ipv6CidrBlocks: ['::/0'] -}; \ No newline at end of file diff --git a/cdktf/src/configs/base-construct-config.ts b/cdktf/src/configs/base-construct-config.ts deleted file mode 100644 index c8b5c0f..0000000 --- a/cdktf/src/configs/base-construct-config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type BaseConstructConfig = { - environment: string; - profile: string; - accountId?: string; - region?: string; -} - -export type DefaultedBaseConstructConfig = BaseConstructConfig & { - accountId: string; - region: string; -} \ No newline at end of file diff --git a/cdktf/src/configs/index.ts b/cdktf/src/configs/index.ts deleted file mode 100644 index a4b0057..0000000 --- a/cdktf/src/configs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './allow-all-egress'; -export * from './base-construct-config'; -export * from './open-search-config'; \ No newline at end of file diff --git a/cdktf/src/configs/open-search-config.ts b/cdktf/src/configs/open-search-config.ts deleted file mode 100644 index 6b6faac..0000000 --- a/cdktf/src/configs/open-search-config.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - opensearchDomain as opensearchDomainLib, - secretsmanagerSecret as secretsmanagerSecretLib -} from '@cdktf/provider-aws'; - -import OpensearchDomain = opensearchDomainLib.OpensearchDomain; -import SecretsmanagerSecret = secretsmanagerSecretLib.SecretsmanagerSecret; - -export type OpenSearchConfig = { - opensearchDomain: OpensearchDomain; - credentials: SecretsmanagerSecret; - traceIngestionPipeline: { - endpoint: string; - arn: string; - }; - logIngestionPipeline: { - endpoint: string; - arn: string; - }; - metricIngestionPipeline: { - endpoint: string; - arn: string; - }; -} \ No newline at end of file diff --git a/cdktf/src/constructs/compute-service.ts b/cdktf/src/constructs/compute-service.ts deleted file mode 100644 index d189189..0000000 --- a/cdktf/src/constructs/compute-service.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { Construct } from 'constructs'; -import { CloudwatchLogGroup } from '@cdktf/provider-aws/lib/cloudwatch-log-group'; -import { EcsService } from '@cdktf/provider-aws/lib/ecs-service'; -import { EcsCluster } from '@cdktf/provider-aws/lib/ecs-cluster'; -import { Vpc } from '@cdktf/provider-aws/lib/vpc'; -import { Subnet } from '@cdktf/provider-aws/lib/subnet'; -import { SecurityGroup } from '@cdktf/provider-aws/lib/security-group'; -import { Apigatewayv2VpcLink } from '@cdktf/provider-aws/lib/apigatewayv2-vpc-link'; -import { ServiceDiscoveryPrivateDnsNamespace } from '@cdktf/provider-aws/lib/service-discovery-private-dns-namespace'; -import { Gateway } from './gateway'; -import { Task, TaskConfig as TaskConfigRequired } from './task'; - -export type TaskConfig = Omit - -export type ComputeServiceConfig = TaskConfig & { - cluster: EcsCluster; - subnets: Subnet[]; - vpc: Vpc; - vpcLink: Apigatewayv2VpcLink; - serviceDiscoveryNamespace: ServiceDiscoveryPrivateDnsNamespace; - taskCount?: number; -}; - - -export class ComputeService extends Construct { - private _gateway: Gateway; - private _logGroup: CloudwatchLogGroup; - private _securityGroup: SecurityGroup; - private _service: EcsService; - private _task: Task; - - constructor (scope: Construct, id: string, config: ComputeServiceConfig) { - super(scope, id); - - const { - containers, - cluster, - environment, - serviceName, - subnets, - vpc, - vpcLink, - serviceDiscoveryNamespace, - taskCount = 0, - profile - } = config; - - this._logGroup = new CloudwatchLogGroup(this, `${id}-logs`, { - name: `/${serviceName}/${environment}`, - retentionInDays: 180 - }); - - this._securityGroup = new SecurityGroup(this, `${id}-security-group`, { - vpcId: vpc.id, - ingress: [ - // only allow incoming traffic from our VPC - { - protocol: 'TCP', - fromPort: containers.at(0)?.port, - toPort: containers.at(0)?.port, - securityGroups: [vpc.defaultSecurityGroupId] - } - ], - egress: [ - // allow all outgoing traffic - { - fromPort: 0, - toPort: 0, - protocol: '-1', - cidrBlocks: ['0.0.0.0/0'], - ipv6CidrBlocks: ['::/0'] - } - ] - } - ); - - this._gateway = new Gateway(this, `${id}-gateway`, { - profile, - environment, - serviceDiscoveryNamespace, - serviceName, - vpcLink - }); - - const taskConfig = { ...config }; - taskConfig.containers.forEach((container) => { - container.environmentVariables?.push({ - name: 'SELF_ENDPOINT', - value: this.gateway.api.apiEndpoint - }); - }); - - this._task = new Task(this, `${id}-task`, { - ...config, - logGroup: this._logGroup - }); - - this._service = new EcsService(this, `${id}-ecs-service`, { - name: serviceName, - taskDefinition: this._task.taskDefinition.arn, - launchType: 'FARGATE', - cluster: cluster.arn, - desiredCount: taskCount, - networkConfiguration: { - subnets: subnets.map(s => s.id), - securityGroups: [vpc.defaultSecurityGroupId, this.securityGroup.id], - assignPublicIp: false - }, - enableExecuteCommand: true, - serviceRegistries: { - registryArn: this._gateway.discoveryService.arn, - containerName: containers.at(0)?.name, - containerPort: containers.at(0)?.port - } - }); - } - - public get gateway (): Gateway { - return this._gateway; - } - public get logGroup (): CloudwatchLogGroup { - return this._logGroup; - } - public get securityGroup (): SecurityGroup { - return this._securityGroup; - } - public get service (): EcsService { - return this._service; - } - public get task (): Task { - return this._task; - } -} \ No newline at end of file diff --git a/cdktf/src/constructs/deployment-pipeline.ts b/cdktf/src/constructs/deployment-pipeline.ts deleted file mode 100644 index 00d49c7..0000000 --- a/cdktf/src/constructs/deployment-pipeline.ts +++ /dev/null @@ -1,351 +0,0 @@ -import path = require('path'); -import { readFileSync } from 'fs'; -import { - cloudwatchLogGroup as cloudwatchLogGroupLib, - cloudwatchEventRule, - cloudwatchEventTarget, - codebuildProject as codebuildProjectLib, - ecrRepository, - iamRole, - subnet, - vpc as vpcLib -} from '@cdktf/provider-aws'; -import { Construct } from 'constructs'; -import { DefaultedBaseConstructConfig } from '../configs'; -import { truncate } from '../utils'; - -import CloudwatchEventRule = cloudwatchEventRule.CloudwatchEventRule; -import CloudwatchEventTarget = cloudwatchEventTarget.CloudwatchEventTarget; -import CodebuildProject = codebuildProjectLib.CodebuildProject; -import CloudwatchLogGroup = cloudwatchLogGroupLib.CloudwatchLogGroup; -import EcrRepository = ecrRepository.EcrRepository; -import IamRole = iamRole.IamRole; -import Subnet = subnet.Subnet; -import Vpc = vpcLib.Vpc; - - -/** - * Config for the DeploymentPipeline construct - */ -export type DeploymentPipelineConfig = { - /** - * Whether to deploy the newly built image immediately after it is built. - * @default - false - */ - deployAfterBuild?: boolean; - /** - * The ECR repository holding the image to deploy. - */ - ecrRepo: EcrRepository; - /** - * Arn of the ECS Cluster that hosts the target service. - */ - clusterArn: string; - /** - * Name of the ECS Cluster to deploy changes to. - */ - clusterName: string; - /** - * Name of the ECS Service to deploy changes to. - */ - serviceName: string; - /** - * The private subnets with a NAT to launch the codebuild jobs in. - */ - subnets: Subnet[]; - /** - * The VPC to launch the codebuild jobs in. - */ - vpc: Vpc; - /** - * The number of tasks to set on the service on update. - */ - taskCount?: number; -}; - -export class DeploymentPipeline extends Construct { - private _deployRole: IamRole; - private _deployLogs: CloudwatchLogGroup; - private _deploy: CodebuildProject; - - constructor (scope: Construct, id: string, baseConfig: DefaultedBaseConstructConfig, deploymentPipelineConfig: DeploymentPipelineConfig) { - super(scope, id); - - const { - region, - accountId, - environment - } = baseConfig; - - const { - deployAfterBuild = false, - ecrRepo, - clusterArn, - clusterName, - serviceName, - subnets, - vpc, - taskCount = 0 - } = deploymentPipelineConfig; - - this._deployLogs = new CloudwatchLogGroup(this, `${id}-deploy-logs`, { - name: `/${serviceName}/${environment}/deploy`, - retentionInDays: 180 - }); - - const roleNamePostfix = `-${environment}-deploy-role`; - const namePrefix = truncate(`${serviceName}`, (64 - roleNamePostfix.length)); - const roleName = `${namePrefix}${roleNamePostfix}`; - this._deployRole = new IamRole(this, `${id}-deploy-role`, { - name: roleName, - assumeRolePolicy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Principal: { - Service: 'codebuild.amazonaws.com' - }, - Action: 'sts:AssumeRole' - }] - }), - inlinePolicy: [ - { - name: 'ecs-deploy-access', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Action: [ - 'ecs:UpdateService' - ], - Resource: [ - `arn:aws:ecs:${region}:${accountId}:service/${clusterName}/${serviceName}` - ] - }] - }) - }, - { - name: 'cloudwatchlogs-access', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Action: [ - 'logs:CreateLogStream', - 'logs:PutLogEvents' - ], - Resource: [ - this.deployLogs.arn, - `${this.deployLogs.arn}/*`, - `${this.deployLogs.arn}:*` - ] - }] - }) - }, - { - name: 'ecr-read-write-access', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Action: [ - 'ecr:BatchCheckLayerAvailability', - 'ecr:CompleteLayerUpload', - 'ecr:GetAuthorizationToken', - 'ecr:InitiateLayerUpload', - 'ecr:PutImage', - 'ecr:UploadLayerPart' - ], - Resource: [ - ecrRepo.arn - ] - }] - }) - }, - { - name: 'vpc-access', - policy: JSON.stringify({ - 'Version': '2012-10-17', - 'Statement': [ - { - 'Effect': 'Allow', - 'Action': [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeDhcpOptions', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DeleteNetworkInterface', - 'ec2:DescribeSubnets', - 'ec2:DescribeSecurityGroups', - 'ec2:DescribeVpcs' - ], - 'Resource': '*' - }, - { - 'Effect': 'Allow', - 'Action': [ - 'ec2:CreateNetworkInterfacePermission' - ], - 'Resource': `arn:aws:ec2:${region}:${accountId}:network-interface/*`, - 'Condition': { - 'StringEquals': { - 'ec2:AuthorizedService': 'codebuild.amazonaws.com' - }, - 'ArnEquals': { - 'ec2:Subnet': subnets.map(s => s.arn) - } - } - } - ] - }) - } - ], - description: `Role used by CodeBuild to deploy image updates to the ECS Service ${serviceName}` - }); - - /** - * NOTE: You escape `${` in HCL with `$${`. - * However in JS regex, you have to additionally escape `$$` with `$$$`. - * My guess is they're using groovy under the hood or something, - * not important; but that's why the escape replaces looks odd. - */ - const buildspec = readFileSync( - path.resolve( - 'guardrails-api/buildspecs/deploy.yml' - ) - ).toString().replace(/\$\{/g, '$$${'); - this._deploy = new CodebuildProject(this, `${id}-deploy`, { - name: `${serviceName}-${environment}-deploy`, - queuedTimeout: 5, - buildTimeout: 5, - source: { - buildspec, - type: 'NO_SOURCE' - }, - artifacts: { - type: 'NO_ARTIFACTS' - }, - environment: { - computeType: 'BUILD_GENERAL1_SMALL', - image: 'aws/codebuild/amazonlinux2-aarch64-standard:3.0', - type: 'ARM_CONTAINER', - // TODO: Update with Env Vars for deploy-spec - environmentVariable: [ - { - name: 'ECR_ENDPOINT', - value: ecrRepo.repositoryUrl - }, - { - name: 'IMAGE_VERSION_TAG', - value: 'latest' - }, - { - name: 'CLUSTER_ARN', - value: clusterArn - }, - { - name: 'CLUSTER_NAME', - value: clusterName - }, - { - name: 'SERVICE_NAME', - value: serviceName - }, - { - name: 'TASK_COUNT', - value: taskCount.toString() - } - ] - }, - serviceRole: this.deployRole.arn, - logsConfig: { - cloudwatchLogs: { - groupName: this.deployLogs.name, - status: 'ENABLED' - } - }, - vpcConfig: { - vpcId: vpc.id, - securityGroupIds: [vpc.defaultSecurityGroupId], - subnets: subnets.map(s => s.id) - } - }); - - const onPublishServiceRole = new IamRole(this, `${id}-deploy-pub-role`, { - name: truncate(`${serviceName}-${environment}-deploy-rule-role`, 64), - assumeRolePolicy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Principal: { - Service: 'events.amazonaws.com' - }, - Action: 'sts:AssumeRole' - }] - }), - inlinePolicy: [ - { - name: 'deploy-job-access', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Action: [ - 'codebuild:StartBuild' - ], - Resource: [ - this.deploy.arn - ] - } - ] - }) - } - ], - description: `Role used by EventBridge to trigger actions for ${serviceName}-${environment}-deploy-rule` - }); - const onPublishRule = new CloudwatchEventRule(this, `${id}-deploy-pub-rule`, { - name: truncate(`${serviceName}-${environment}-deploy-rule`, 64), - description: `Starts ${serviceName}-${environment}-deploy when an image is published to ${ecrRepo.name}`, - isEnabled: deployAfterBuild, - eventPattern: JSON.stringify({ - 'detail-type': [ - 'ECR Image Action' - ], - 'source': [ - 'aws.ecr' - ], - 'detail': { - 'action-type': [ - 'PUSH' - ], - 'image-tag': [ - 'latest' - ], - 'repository-name': [ - ecrRepo.name - ], - 'result': [ - 'SUCCESS' - ] - } - }), - roleArn: onPublishServiceRole.arn - }); - new CloudwatchEventTarget(this, `${id}-deploy-pub-target`, { - arn: this.deploy.arn, - rule: onPublishRule.name, - roleArn: onPublishServiceRole.arn - }); - } - - public get deployRole (): IamRole { - return this._deployRole; - } - public get deployLogs (): CloudwatchLogGroup { - return this._deployLogs; - } - public get deploy (): CodebuildProject { - return this._deploy; - } - -} \ No newline at end of file diff --git a/cdktf/src/constructs/gateway.ts b/cdktf/src/constructs/gateway.ts deleted file mode 100644 index d609968..0000000 --- a/cdktf/src/constructs/gateway.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Construct } from 'constructs'; -import { Apigatewayv2VpcLink } from '@cdktf/provider-aws/lib/apigatewayv2-vpc-link'; -import { Apigatewayv2Api } from '@cdktf/provider-aws/lib/apigatewayv2-api'; -import { Apigatewayv2Integration } from '@cdktf/provider-aws/lib/apigatewayv2-integration'; -import { ServiceDiscoveryPrivateDnsNamespace } from '@cdktf/provider-aws/lib/service-discovery-private-dns-namespace'; -import { ServiceDiscoveryService } from '@cdktf/provider-aws/lib/service-discovery-service'; -import { Apigatewayv2Route } from '@cdktf/provider-aws/lib/apigatewayv2-route'; -import { TerraformOutput } from 'cdktf'; -import { Apigatewayv2Stage } from '@cdktf/provider-aws/lib/apigatewayv2-stage'; -import { BaseConstructConfig } from '../../../../src/configs'; -import { truncate } from '../utils'; - -export type GatewayConfig = BaseConstructConfig & { - serviceName: string; - vpcLink: Apigatewayv2VpcLink; - serviceDiscoveryNamespace: ServiceDiscoveryPrivateDnsNamespace; -}; - -export class Gateway extends Construct { - private _api: Apigatewayv2Api; - private _discoveryService: ServiceDiscoveryService; - private _integration: Apigatewayv2Integration; - private _route: Apigatewayv2Route; - private _stage: Apigatewayv2Stage; - - constructor (scope: Construct, id: string, config: GatewayConfig) { - super(scope, id); - - const { - serviceName, - vpcLink, - serviceDiscoveryNamespace - } = config; - - this._api = new Apigatewayv2Api(this, `${id}-http-api`, { - name: `${serviceName}-gateway`, - description: 'Integration between apigw and Fargate Service', - protocolType: 'HTTP' - }); - - this._discoveryService = new ServiceDiscoveryService(this, `${id}-service-discovery-service`, { - name: `${truncate(serviceName, (61 - '-discovery-service'.length))}-discovery-service`, - dnsConfig: { - namespaceId: serviceDiscoveryNamespace.id, - dnsRecords: [ - { - ttl: 10, - type: 'SRV' - } - ], - routingPolicy: 'MULTIVALUE' - } - }); - - this._integration = new Apigatewayv2Integration(this, `${id}-api-integration`, { - apiId: this._api.id, - connectionId: vpcLink.id, - connectionType: 'VPC_LINK', - description: 'API Integration with AWS Fargate Service', - integrationMethod: 'ANY', // for GET and POST, use ANY - integrationType: 'HTTP_PROXY', - integrationUri: this._discoveryService.arn, - payloadFormatVersion: '1.0' // supported values for Lambda proxy integrations are 1.0 and 2.0. For all other integrations, 1.0 is the only supported value - }); - - this._route = new Apigatewayv2Route(this, `${id}-route`, { - apiId: this._api.id, - routeKey: 'ANY /{proxy+}', - target: `integrations/${this._integration.id}` - }); - - this._stage = new Apigatewayv2Stage(this, `${id}-default-stage`, { - apiId: this._api.id, - name: '$default', - autoDeploy: true - }); - - new TerraformOutput(this, `${id}-apig-url`, { - value: this._api.apiEndpoint, - description: 'API Gateway URL to access the endpoint' - }); - } - - public get api (): Apigatewayv2Api { - return this._api; - } - public get discoveryService (): ServiceDiscoveryService { - return this._discoveryService; - } - public get integration (): Apigatewayv2Integration { - return this._integration; - } - public get route (): Apigatewayv2Route { - return this._route; - } - public get stage (): Apigatewayv2Stage { - return this._stage; - } -} \ No newline at end of file diff --git a/cdktf/src/constructs/index.ts b/cdktf/src/constructs/index.ts deleted file mode 100644 index 66d7ea4..0000000 --- a/cdktf/src/constructs/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './deployment-pipeline'; -export * from './rds-postgres'; \ No newline at end of file diff --git a/cdktf/src/constructs/rds-postgres.ts b/cdktf/src/constructs/rds-postgres.ts deleted file mode 100644 index 6d94709..0000000 --- a/cdktf/src/constructs/rds-postgres.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { - dbInstance as dbInstanceLib, - dbSubnetGroup as dbSubnetGroupLib, - securityGroup as securityGroupLib, - subnet, - vpc as vpcLib -} from '@cdktf/provider-aws'; -import { Construct } from "constructs"; -import { ALLOW_ALL_EGRESS } from "../configs"; - -import DbInstance = dbInstanceLib.DbInstance; -import DbSubnetGroup = dbSubnetGroupLib.DbSubnetGroup; -import SecurityGroup = securityGroupLib.SecurityGroup; -import Subnet = subnet.Subnet; -import Vpc = vpcLib.Vpc; - -export type RdsPostgresConfig = { - /** - * @default 5 - */ - allocatedStorage?: number; - /** - * @default false - */ - allowMajorVersionUpgrade?: boolean; - /** - * @default true - */ - autoMinorVersionUpgrade?: boolean; - availabilityZone?: string; - /** - * Backup retention in days - * @default 7 - */ - backupRetentionPeriod?: number - /** - * Where backups are stored - * Options - 'region' | 'outposts' - * @default region - */ - backupTarget?: string; - /** - * Daily time range (in UTC) for backups - * Must not overlap with maintenanceWindow - * @default '09:00-09:30' UTC (1:00 AM Pacific) - */ - backupWindow?: string; - /** - * @default 'rds-ca-rsa4096-g1' - */ - caCertIdentifier?: string; - /** - * @default '15.4' - */ - engineVersion?: string; - /** - * @default 'db.t4g.micro' - */ - instanceClass?: string; - /** - * The window to perform maintenance in. - * Syntax: "ddd:hh24:mi-ddd:hh24:mi". - * Eg: "Mon:00:00-Mon:03:00". - * @default 'Sun:10:00-Sun:13:00' UTC (Sundays 2:00 - 5:00 AM Pacific) - */ - maintenanceWindow?: string; - /** - * @default true - */ - multiAz?: boolean; - /** - * Name for the Postgres database - */ - name: string; - /** - * @default 5432 - */ - port?: number; - /** - * @default 'gp2' - */ - storageType?: string; - /** - * The private subnets with NAT for the Postgres DB to be created in. - */ - subnets: Subnet[]; - /** - * The VPC to launch the Postgres DB in. - */ - vpc: Vpc; -}; - -export class RdsPostgres extends Construct { - private _instance: DbInstance; - private _secretArn: string; - private _securityGroup: SecurityGroup; - private _subnetGroup: DbSubnetGroup; - - constructor (scope: Construct, id: string, rdsPostgresConfig: RdsPostgresConfig) { - super(scope, id); - - const { - allocatedStorage = 5, - availabilityZone, - allowMajorVersionUpgrade = false, - autoMinorVersionUpgrade = true, - backupRetentionPeriod = 7, - backupTarget = 'region', - backupWindow = '09:00-09:30', - caCertIdentifier = 'rds-ca-rsa4096-g1', - engineVersion = '15.4', - name: identifier, - instanceClass = 'db.t4g.micro', - maintenanceWindow = 'Sun:10:00-Sun:13:00', - multiAz, - port = 5432, - storageType = 'gp2', - subnets, - vpc - } = rdsPostgresConfig; - - this._subnetGroup = new DbSubnetGroup(this, `${id}-db-subnet-group`, { - namePrefix: identifier, - description: `Subnet group for ${identifier}; contains private subnets from ${vpc.id}`, - subnetIds: subnets.map(s => s.id) - }); - - this._securityGroup = new SecurityGroup(this, `${id}-sg`, { - name: `${identifier}-sg`, - description: 'Allow traffic from within the vpc', - vpcId: vpc.id, - ingress: [ - { - description: 'TLS from VPC', - fromPort: 443, - toPort: 443, - protocol: 'tcp', - cidrBlocks: [vpc.cidrBlock] - }, - { - description: 'psql from VPC', - fromPort: 5432, - toPort: 5432, - protocol: 'tcp', - cidrBlocks: [vpc.cidrBlock] - } - ], - egress: [ALLOW_ALL_EGRESS] - }); - - this._instance = new DbInstance(this, `${id}-db-instance`, { - allocatedStorage, - allowMajorVersionUpgrade, - autoMinorVersionUpgrade, - availabilityZone, - backupRetentionPeriod, - backupTarget, - backupWindow, - blueGreenUpdate: { - enabled: false // note supported for Postgres - }, - caCertIdentifier, - copyTagsToSnapshot: true, - dbName: 'postgres', - dbSubnetGroupName: this.subnetGroup.name, - deleteAutomatedBackups: true, - deletionProtection: false, - engine: 'postgres', - engineVersion, - identifier, - instanceClass, - maintenanceWindow, - manageMasterUserPassword: true, - multiAz, - port, - publiclyAccessible: false, - username: 'postgres', - skipFinalSnapshot: true, - storageEncrypted: true, - storageType, - vpcSecurityGroupIds: [this.securityGroup.id] - }); - - this._secretArn = this.instance.masterUserSecret.get(0).secretArn; - } - - public get secretArn(): string { - return this._secretArn; - } - public get instance(): DbInstance { - return this._instance; - } - public get securityGroup(): SecurityGroup { - return this._securityGroup; - } - public get subnetGroup(): DbSubnetGroup { - return this._subnetGroup; - } -} \ No newline at end of file diff --git a/cdktf/src/constructs/task.ts b/cdktf/src/constructs/task.ts deleted file mode 100644 index c388393..0000000 --- a/cdktf/src/constructs/task.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Construct } from 'constructs'; -import { CloudwatchLogGroup } from '@cdktf/provider-aws/lib/cloudwatch-log-group'; -import { IamRole, IamRoleInlinePolicy } from '@cdktf/provider-aws/lib/iam-role'; -import { EcrRepository } from '@cdktf/provider-aws/lib/ecr-repository'; -import { EcsTaskDefinition } from '@cdktf/provider-aws/lib/ecs-task-definition'; -import { BaseConstructConfig } from '../../../../src/configs'; -import { truncate } from '../utils'; - -export type NameValue = { - name: string; - value: string; -} - -export type SecretRef = { - name: string; - valueFrom: string; -} - -export type ContainerConfig = { - image: string; - name: string; - port: number; - environmentVariables?: NameValue[]; - secrets?: SecretRef[]; -} - -export type TaskConfig = BaseConstructConfig & { - containers: ContainerConfig[]; - ecrRepo: EcrRepository; - logGroup: CloudwatchLogGroup; - serviceName: string; - cpu?: number; - memory?: number; - taskPolicies?: IamRoleInlinePolicy[]; - executionPolicies?: IamRoleInlinePolicy[]; -}; - -export class Task extends Construct { - private _executionRole: IamRole; - private _taskRole: IamRole; - private _taskDefinition: EcsTaskDefinition; - - constructor (scope: Construct, id: string, config: TaskConfig) { - super(scope, id); - - const { - cpu = 256, - ecrRepo, - containers, - logGroup, - memory = 512, - region, - serviceName, - taskPolicies = [], - executionPolicies = [] - } = config; - - // Used by ECS to create tasks - this._executionRole = new IamRole(this, `${id}-execution-role`, { - name: `${truncate(`${serviceName}`, (64 - '-execution-role'.length))}-execution-role`, - inlinePolicy: [ - ...executionPolicies, - { - name: 'allow-ecr-pull', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Action: [ - 'ecr:GetAuthorizationToken', - 'ecr:BatchCheckLayerAvailability', - 'ecr:GetDownloadUrlForLayer', - 'ecr:BatchGetImage', - 'logs:CreateLogStream', - 'logs:PutLogEvents' - ], - Resource: [ - ecrRepo.arn - ] - } - ] - }) - } - ], - assumeRolePolicy: JSON.stringify({ - Version: '2012-10-17', - Statement: [ - { - Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { - Service: 'ecs-tasks.amazonaws.com' - } - } - ] - }), - managedPolicyArns: [ - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' - ] - }); - - // Used by tasks to do stuff in the app - this._taskRole = new IamRole(this, 'task-role', { - name: `${truncate(`${serviceName}`, (64 - '-task-role'.length))}-task-role`, - inlinePolicy: [ - ...taskPolicies, - { - name: 'allow-logs', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Action: ['logs:CreateLogStream', 'logs:PutLogEvents'], - Resource: [ - logGroup.arn, - `${logGroup.arn}/*`, - `${logGroup.arn}:*` - ] - } - ] - }) - } - ], - assumeRolePolicy: JSON.stringify({ - Version: '2012-10-17', - Statement: [ - { - Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { - Service: 'ecs-tasks.amazonaws.com' - } - } - ] - }) - }); - - const containerDefinitions = containers.map(c => ({ - name: c.name, - image: c.image, - portMappings: [ - { - containerPort: c.port - } - ], - logConfiguration: { - logDriver: 'awslogs', - options: { - 'awslogs-region': region, - 'awslogs-group': logGroup.name, - 'awslogs-stream-prefix': c.name - } - }, - environment: c.environmentVariables, - secrets: c.secrets - })); - - this._taskDefinition = new EcsTaskDefinition(this, `${id}-task-definition`, { - containerDefinitions: JSON.stringify(containerDefinitions), - family: serviceName, - cpu: cpu.toString(), - memory: memory.toString(), - requiresCompatibilities: ['FARGATE'], - executionRoleArn: this._executionRole.arn, - taskRoleArn: this._taskRole.arn, - networkMode: 'awsvpc', - runtimePlatform: { - cpuArchitecture: 'ARM64', - operatingSystemFamily: 'LINUX' - } - }); - } - - public get executionRole (): IamRole { - return this._executionRole; - } - public get taskRole (): IamRole { - return this._taskRole; - } - public get taskDefinition (): EcsTaskDefinition { - return this._taskDefinition; - } -} \ No newline at end of file diff --git a/cdktf/src/index.ts b/cdktf/src/index.ts deleted file mode 100644 index d56efdf..0000000 --- a/cdktf/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './stacks'; -export * from './substacks'; \ No newline at end of file diff --git a/cdktf/src/stacks/index.ts b/cdktf/src/stacks/index.ts deleted file mode 100644 index 3d80728..0000000 --- a/cdktf/src/stacks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './main'; \ No newline at end of file diff --git a/cdktf/src/stacks/main.ts b/cdktf/src/stacks/main.ts deleted file mode 100644 index 827dd06..0000000 --- a/cdktf/src/stacks/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Construct } from 'constructs'; -import { TerraformStack } from 'cdktf'; -import { GuardrailsValidationServiceSubStack, GuardrailsValidationServiceSubStackConfig } from '..'; - -export type GuardrailsValidationServiceStackConfig = GuardrailsValidationServiceSubStackConfig; - -// This is for if we want to do a multi-stack deployment -export class GuardrailsValidationServiceStack extends TerraformStack { - constructor (scope: Construct, id: string, config: GuardrailsValidationServiceStackConfig) { - super(scope, id); - - new GuardrailsValidationServiceSubStack(this, `${id}-substack`, config); - } -} \ No newline at end of file diff --git a/cdktf/src/substacks/index.ts b/cdktf/src/substacks/index.ts deleted file mode 100644 index 3d80728..0000000 --- a/cdktf/src/substacks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './main'; \ No newline at end of file diff --git a/cdktf/src/substacks/main.ts b/cdktf/src/substacks/main.ts deleted file mode 100644 index 5b684e5..0000000 --- a/cdktf/src/substacks/main.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { Construct } from 'constructs'; -import { - dataAwsCallerIdentity, - dataAwsRegion, - subnet, - vpc as vpcLib -} from '@cdktf/provider-aws'; -import { DefaultedBaseConstructConfig } from '../configs'; -import { - DeploymentPipeline, - DeploymentPipelineConfig as DeploymentPipelineConfigRequired, - RdsPostgres, - RdsPostgresConfig as RdsPostgresConfigRequired -} from '../constructs'; -import { truncate } from '../utils'; - -import AwsCallerIdentity = dataAwsCallerIdentity.DataAwsCallerIdentity; -import AwsRegion = dataAwsRegion.DataAwsRegion -import Subnet = subnet.Subnet; -import Vpc = vpcLib.Vpc; -import { EcsCluster } from '@cdktf/provider-aws/lib/ecs-cluster'; -import { Apigatewayv2VpcLink } from '@cdktf/provider-aws/lib/apigatewayv2-vpc-link'; -import { ServiceDiscoveryPrivateDnsNamespace } from '@cdktf/provider-aws/lib/service-discovery-private-dns-namespace'; -import { EcrRepository } from '@cdktf/provider-aws/lib/ecr-repository'; -import { ComputeService, ComputeServiceConfig } from '../constructs/compute-service'; -import { IamRoleInlinePolicy } from '@cdktf/provider-aws/lib/iam-role'; -import { NameValue } from '../constructs/task'; - -export type RdsPostgresConfig = Omit -export type DeploymentPipelineConfig = Omit< - DeploymentPipelineConfigRequired, - 'ecrRepo' | - 'subnets' | - 'vpc' | - 'clusterArn' | - 'clusterName' | - 'serviceName' -> - -export type OtelConfig = { - /** - * Sets the environment variable `OTEL_PYTHON_TRACER_PROVIDER` - * Defaults to `sdk_tracer_provider` - */ - pythonTracerProvider?: string; - /** - * Sets the environment variable `OTEL_SERVICE_NAME` - * Defaults to `guardrails-api` - */ - serviceName?: string; - /** - * Sets the environment variable `OTEL_TRACES_EXPORTER` - * Defaults to `otlp` - */ - tracesExporter?: string; - /** - * Sets the environment variable `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST` - * Defaults to `Accept-Encoding,User-Agent,Referer` - */ - instrumentationHttpCaptureHeadersServerRequest?: string; - /** - * Sets the environment variable `OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE` - * Defaults to `Last-Modified,Content-Type` - */ - instrumentationHttpCaptureHeadersServerResponse?: string; - /** - * Sets the environment variable `OTEL_METRICS_EXPORTER` - * Defaults to `none` - */ - metricsExporter?: string; - /** - * Sets the environment variable `OTEL_EXPORTER_OTLP_PROTOCOL` - * Defaults to `http/protobuf` - */ - otlpProtocol?: string; - /** - * Sets the environment variable `OTEL_EXPORTER_OTLP_ENDPOINT` - * Defaults to `http://localhost:4317` - */ - otlpEndpoint?: string; - /** - * Sets the environment variable `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` - * Defaults to `${otlpEndpoint}/v1/traces` - */ - traceSink?: string; - /** - * Sets the environment variable `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` - * Defaults to `${otlpEndpoint}/v1/metrics` - */ - metricsSink?: string; - /** - * Sets the environment variable `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` - * Defaults to `${otlpEndpoint}/v1/logs` - */ - logsSink?: string; -} - -export type ServerConfig = Omit - -export type GuardrailsValidationServiceSubStackConfig = ServerConfig & { - deploymentPipelineConfig: DeploymentPipelineConfig; - ecrRepo: EcrRepository; - rdsPostgresConfig?: RdsPostgresConfig; - subnets: Subnet[]; - vpc: Vpc; - cluster: EcsCluster; - vpcLink: Apigatewayv2VpcLink; - serviceDiscoveryNamespace: ServiceDiscoveryPrivateDnsNamespace; - otelConfig?: OtelConfig; - environmentVariables?: NameValue[]; -} - -export class GuardrailsValidationServiceSubStack extends Construct { - private _pgDatabase: RdsPostgres; - private _deploymentPipeline: DeploymentPipeline; - private _server: ComputeService; - - constructor (scope: Construct, id: string, config: GuardrailsValidationServiceSubStackConfig) { - super(scope, id); - - const currentIdentity = new AwsCallerIdentity(this, `${id}-aws-identity`); - const defaultAccount: string = currentIdentity.accountId; - const currentRegion = new AwsRegion(this, `${id}-aws-region`); - const defaultRegion: string = currentRegion.name; - - const { - accountId = defaultAccount, - cluster, - deploymentPipelineConfig, - environment, - ecrRepo, - rdsPostgresConfig = {}, - region = defaultRegion, - subnets, - vpc, - profile, - environmentVariables = [], - taskPolicies = [], - memory = 1024, - otelConfig = {} - } = config; - - - const { - pythonTracerProvider = 'sdk_tracer_provider', - serviceName: otelServiceName = 'guardrails-api', - tracesExporter = 'otlp', - instrumentationHttpCaptureHeadersServerRequest = 'Accept-Encoding,User-Agent,Referer', - instrumentationHttpCaptureHeadersServerResponse = 'Last-Modified,Content-Type', - metricsExporter = 'none', - otlpProtocol = 'http/protobuf', - otlpEndpoint = 'http://localhost:4317', - } = otelConfig; - const { - traceSink = `${otlpEndpoint}/v1/traces`, - metricsSink = `${otlpEndpoint}/v1/metrics`, - logsSink = `${otlpEndpoint}/v1/logs`, - } = otelConfig; - - const baseConfig: DefaultedBaseConstructConfig = { - accountId, - environment, - profile, - region - }; - - const baseName = `${id}-guardrails-validation-service`; - const serviceName = truncate(`${baseName}-api-${environment}`, 59); // Allow 5 characters for appending `-role` - const dbName = truncate(`${baseName}-postgres-db`, 59); // 63 -sg - - const deploymentPipelineConfigRequired: DeploymentPipelineConfigRequired = { - ...deploymentPipelineConfig, - ecrRepo, - clusterArn: cluster.arn, - clusterName: cluster.name, - serviceName, - vpc, - subnets - }; - const rdsPostgresConfigRequired: RdsPostgresConfigRequired = { - ...rdsPostgresConfig, - name: dbName, - vpc, - subnets - }; - - this._deploymentPipeline = new DeploymentPipeline(this, `${id}-ci-cd`, baseConfig, deploymentPipelineConfigRequired); - - this._pgDatabase = new RdsPostgres(this, `${id}-pg`, rdsPostgresConfigRequired) - - const pgSecretsManagerAccess: IamRoleInlinePolicy = { - name: 'pg-secrets-manager-access', - policy: JSON.stringify({ - Version: '2012-10-17', - Statement: [{ - Effect: 'Allow', - Action: [ - 'secretsmanager:GetSecretValue' - ], - Resource: [ - this.pgDatabase.secretArn - ] - }] - }) - }; - - taskPolicies.push( - pgSecretsManagerAccess - ); - const mandatoryEnvVars = [ - { - name: 'APP_ENVIRONMENT', - value: environment - }, - { - name: 'NODE_ENV', - value: 'production' - }, - { - name: 'NLTK_DATA', - value: '/opt/nltk_data' - }, - { - name: 'PGPORT', - value: this.pgDatabase.instance.port.toString() - }, - { - name: 'PGDATABASE', - value: this.pgDatabase.instance.dbName - }, - { - name: 'PGHOST', - value: this.pgDatabase.instance.endpoint - }, - { - name: 'PGUSER', - value: 'postgres' - }, - { - name: 'PGPASSWORD_SECRET_ARN', - value: this.pgDatabase.secretArn - }, - { - name: 'PORT', - value: '8000' - }, - { - name: 'AWS_EXECUTION_ENV', - value: 'AWS_ECS_Fargate' - } - ]; - const optionalEnvVars = [ - { - name: 'PYTHONUNBUFFERED', - value: '1' - }, - { - name: 'OTEL_PYTHON_TRACER_PROVIDER', - value: pythonTracerProvider - }, - { - name: 'OTEL_SERVICE_NAME', - value: otelServiceName - }, - { - name: 'OTEL_TRACES_EXPORTER', - value: tracesExporter - }, - { - name: 'OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST', - value: instrumentationHttpCaptureHeadersServerRequest - }, - { - name: 'OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE', - value: instrumentationHttpCaptureHeadersServerResponse - }, - { - name: 'OTEL_METRICS_EXPORTER', - value: metricsExporter - }, - { - name: 'OTEL_EXPORTER_OTLP_PROTOCOL', - value: otlpProtocol - }, - { - name: 'OTEL_EXPORTER_OTLP_ENDPOINT', - value: otlpEndpoint - }, - { - name: 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', - value: traceSink - }, - { - name: 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', - value: metricsSink - }, - { - name: 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', - value: logsSink - }, - { - name: 'LOG_LEVEL', - value: 'DEBUG' - } - ]; - - const mandatoryEnvVarNames: string[] = mandatoryEnvVars.map(ev => ev.name); - const userEnvVarNames: string[] = environmentVariables.map(ev => ev.name); - mandatoryEnvVars.push( - ...environmentVariables.filter((envVar: NameValue) => !mandatoryEnvVarNames.includes(envVar.name)), - ...optionalEnvVars.filter((envVar: NameValue) => !userEnvVarNames.includes(envVar.name)) - ); - environmentVariables; - this._server = new ComputeService(this, `${id}-compute-service`, { - ...config, - containers: [{ - image: ecrRepo.repositoryUrl, - name: ecrRepo.name, - port: 8000, - environmentVariables: mandatoryEnvVars - }], - memory, - taskPolicies, - serviceName - }); - } - - public get pgDatabase(): RdsPostgres { - return this._pgDatabase; - } - public get deploymentPipeline (): DeploymentPipeline { - return this._deploymentPipeline; - } - public get server (): ComputeService { - return this._server; - } -} \ No newline at end of file diff --git a/cdktf/src/utils/index.ts b/cdktf/src/utils/index.ts deleted file mode 100644 index 32942a1..0000000 --- a/cdktf/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './truncate'; \ No newline at end of file diff --git a/cdktf/src/utils/truncate.ts b/cdktf/src/utils/truncate.ts deleted file mode 100644 index b7ca935..0000000 --- a/cdktf/src/utils/truncate.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createHash } from 'crypto'; - -/** - * Takes a name and a maximum length and returns a string of that length. - * If the name parameter's length is greater than the specified max length - * it will be truncated and the last 8 characters will be replaced with the - * last 8 characters of the md5 hash of the original value for name. - * @param name - string - * @param maxLength - number - * @returns string - */ -export function truncate (name: string, maxLength: number): string { - if (name.length > maxLength) { - const hash = createHash('md5').update(name).digest('hex'); - - // last 8 characters yields a 0.000005 chance of collision - const semiHash = hash.substring(hash.length - 8); - - const truncName = name.substring(0, maxLength - 8); - return `${truncName}${semiHash}`; - } else { - return name; - } -} \ No newline at end of file diff --git a/cdktf/tsconfig.json b/cdktf/tsconfig.json deleted file mode 100644 index 27659f5..0000000 --- a/cdktf/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "alwaysStrict": true, - "declaration": true, - "experimentalDecorators": true, - "inlineSourceMap": true, - "inlineSources": true, - "lib": [ - "es2018" - ], - "module": "CommonJS", - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "stripInternal": true, - "target": "ES2018", - "incremental": true, - "skipLibCheck": true - }, - "include": [ - "**/*.ts" - ], - "exclude": [ - "node_modules", - "cdktf.out" - ] -} \ No newline at end of file diff --git a/custom-install/install.py b/custom-install/install.py deleted file mode 100644 index 85ea608..0000000 --- a/custom-install/install.py +++ /dev/null @@ -1,113 +0,0 @@ -import os -import sys -import logging -import json -from typing import Any, Dict -from rich.console import Console -from guardrails.cli.hub.install import ( - get_site_packages_location, - install_hub_module, - run_post_install, - add_to_hub_inits -) -from guardrails.cli.server.module_manifest import ModuleManifest -from string import Template - -console = Console() - -os.environ[ - "COLOREDLOGS_LEVEL_STYLES" -] = "spam=white,faint;success=green,bold;debug=magenta;verbose=blue;notice=cyan,bold;warning=yellow;error=red;critical=background=red" # noqa -LEVELS = { - "SPAM": 5, - "VERBOSE": 15, - "NOTICE": 25, - "SUCCESS": 35, -} -for key in LEVELS: - logging.addLevelName(LEVELS.get(key), key) # type: ignore -logger = logging.getLogger("custom-install") - - -def load_manifest(fileName: str) -> Dict[str, Any]: - with open(f"custom-install/manifests/{fileName}") as manifest_file: - content = manifest_file.read() - return json.loads(content) - -custom_manifests = { - "guardrails/provenance_llm": load_manifest("provenance-llm.json"), - "guardrails/detect_pii": load_manifest("detect-pii.json"), - "guardrails/competitor_check": load_manifest("competitor-check.json"), - "guardrails/many_shot_jailbreak": load_manifest("jailbreak.json"), - "tryolabs/restricttotopic": load_manifest("restrict-to-topic.json"), -} - -def get_validator_manifest(module_name) -> ModuleManifest: - manifest = custom_manifests.get(module_name, {}) - return ModuleManifest.from_dict(manifest) - -def custom_install(package_uri: str): - """Install a validator from the Hub.""" - if not package_uri.startswith("hub://"): - logger.error("Invalid URI!") - sys.exit(1) - - console.print(f"\nInstalling {package_uri}...\n") - logger.log( - level=LEVELS.get("SPAM"), msg=f"Installing {package_uri}..." # type: ignore - ) - - # Validation - module_name = package_uri.replace("hub://", "") - - # Prep - with console.status("Fetching manifest", spinner="bouncingBar"): - module_manifest = get_validator_manifest(module_name) - site_packages = get_site_packages_location() - - # Install - with console.status("Downloading dependencies", spinner="bouncingBar"): - install_hub_module(module_manifest, site_packages) - - # Post-install - with console.status("Running post-install setup", spinner="bouncingBar"): - run_post_install(module_manifest, site_packages) - add_to_hub_inits(module_manifest, site_packages) - - success_message_cli = Template( - """✅Successfully installed ${module_name}! - -[bold]Import validator:[/bold] -from guardrails.hub import ${export} - -[bold]Get more info:[/bold] -https://hub.guardrailsai.com/validator/${id} -""" - ).safe_substitute( - module_name=package_uri, - id=module_manifest.id, - export=module_manifest.exports[0], - ) - success_message_logger = Template( - """✅Successfully installed ${module_name}! - -Import validator: -from guardrails.hub import ${export} - -Get more info: -https://hub.guardrailsai.com/validator/${id} -""" - ).safe_substitute( - module_name=package_uri, - id=module_manifest.id, - export=module_manifest.exports[0], - ) - console.print(success_message_cli) # type: ignore - logger.log(level=LEVELS.get("SPAM"), msg=success_message_logger) # type: ignore - - -# custom_install("hub://guardrails/provenance_llm") -custom_install("hub://tryolabs/restricttotopic") -# custom_install("hub://guardrails/detect_pii") -# custom_install("hub://guardrails/competitor_check") -# custom_install("hub://guardrails/many_shot_jailbreak") diff --git a/custom-install/manifests/competitor-check.json b/custom-install/manifests/competitor-check.json deleted file mode 100644 index b936fa2..0000000 --- a/custom-install/manifests/competitor-check.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "Competitor Check", - "author": { - "name": "Guardrails AI", - "email": "contact@guardrailsai.com" - }, - "maintainers": [{ - "name": "Karan Acharya", - "email": "karan@guardrailsai.com" - }], - "repository": { - "url": "https://github.com/guardrails-ai/competitor_check.git", - "branch": "frontend_demo" - }, - "index": "./__init__.py", - "exports": [ - "CompetitorCheck" - ], - "tags": { - "language": [ - "en" - ], - "certification": [ - "Guardrails Certified" - ], - "contentType": [ - "string" - ], - "infrastructureRequirements": [ - "ML" - ], - "riskCategory": [ - "Brand risk" - ], - "useCases": [ - "Chatbots", - "Customer Support" - ] - }, - "id": "guardrails/competitor_check", - "namespace": "guardrails", - "packageName": "competitor_check", - "moduleName": "validator", - "requiresAuth": false, - "postInstall": "post-install.py" -} \ No newline at end of file diff --git a/custom-install/manifests/detect-pii.json b/custom-install/manifests/detect-pii.json deleted file mode 100644 index c0c37ad..0000000 --- a/custom-install/manifests/detect-pii.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "Detect PII", - "author": { - "name": "Guardrails AI", - "email": "contact@guardrailsai.com" - }, - "maintainers": [{ - "name": "Caleb Courier", - "email": "caleb@guardrailsai.com" - }], - "repository": { - "url": "https://github.com/guardrails-ai/detect_pii.git", - "branch": "frontend_demo" - }, - "index": "./__init__.py", - "exports": [ - "DetectPII" - ], - "tags": { - "language": [ - "en" - ], - "certification": [ - "Guardrails Certified" - ], - "contentType": [ - "string" - ], - "infrastructureRequirements": [ - "ML" - ], - "riskCategory": [ - "Data Leakage" - ], - "useCases": [ - "Chatbots", - "RAG", - "CodeGen", - "Structured data", - "Customer Support" - ] - }, - "id": "guardrails/detect_pii", - "namespace": "guardrails", - "packageName": "detect_pii", - "moduleName": "validator", - "requiresAuth": false, - "postInstall": "post-install.py" -} \ No newline at end of file diff --git a/custom-install/manifests/jailbreak.json b/custom-install/manifests/jailbreak.json deleted file mode 100644 index f461dc0..0000000 --- a/custom-install/manifests/jailbreak.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "Detect Many Shot Jailbreak", - "author": { - "name": "Guardrails AI", - "email": "contact@guardrailsai.com" - }, - "maintainers": [{ - "name": "Wyatt Lansford", - "email": "wyatt@guardrailsai.com" - }], - "repository": { - "url": "https://github.com/guardrails-ai/jailbreak.git", - "branch": "main" - }, - "index": "./__init__.py", - "exports": [ - "DetectManyShotJailbreak" - ], - "tags": { - "language": [ - "en" - ], - "certification": [ - "Guardrails Certified" - ], - "contentType": [ - "string" - ], - "infrastructureRequirements": [ - "ML" - ], - "riskCategory": [ - "Data Leakage" - ], - "useCases": [ - "Chatbots", - "RAG", - "CodeGen", - "Structured data", - "Customer Support" - ] - }, - "id": "guardrails/many_shot_jailbreak", - "namespace": "guardrails", - "packageName": "many_shot_jailbreak", - "moduleName": "validator", - "requiresAuth": false, - "postInstall": "post-install.py" -} \ No newline at end of file diff --git a/custom-install/manifests/provenance-llm.json b/custom-install/manifests/provenance-llm.json deleted file mode 100644 index aa9f950..0000000 --- a/custom-install/manifests/provenance-llm.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "Provenance LLM", - "author": { - "name": "Guardrails AI", - "email": "contact@guardrailsai.com" - }, - "maintainers": [{ - "name": "Caleb Courier", - "email": "caleb@guardrailsai.com" - }], - "repository": { - "url": "https://github.com/guardrails-ai/provenance_llm.git", - "branch": "default-embed-func" - }, - "index": "./__init__.py", - "exports": [ - "ProvenanceLLM" - ], - "tags": { - "language": [ - "en" - ], - "certification": [ - "Guardrails Certified" - ], - "contentType": [ - "string" - ], - "infrastructureRequirements": [ - "ML", - "LLM" - ], - "riskCategory": [ - "Factuality", - "Brand risk" - ], - "useCases": [ - "Chatbots", - "RAG", - "Customer Support" - ] - }, - "id": "guardrails/provenance_llm", - "namespace": "guardrails", - "packageName": "provenance_llm", - "moduleName": "validator", - "requiresAuth": false, - "postInstall": "post-install.py" -} \ No newline at end of file diff --git a/custom-install/manifests/restrict-to-topic.json b/custom-install/manifests/restrict-to-topic.json deleted file mode 100644 index 3f55972..0000000 --- a/custom-install/manifests/restrict-to-topic.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "Restrict to Topic", - "author": { - "name": "Tryolabs", - "email": "hello@tryolabs.com" - }, - "maintainers": [{ - "name": "Paz", - "email": "paz@tyrolabs.com" - }], - "repository": { - "url": "https://github.com/guardrails-ai/restricttotopic.git", - "branch": "streaming_demo" - }, - "index": "./__init__.py", - "exports": [ - "RestrictToTopic" - ], - "tags": { - "language": [ - "en" - ], - "certification": [ - "Guardrails Certified" - ], - "contentType": [ - "string" - ], - "infrastructureRequirements": [ - "LLM", - "ML" - ], - "riskCategory": [ - "Etiquette", - "Jailbreaking", - "Brand risk" - ], - "useCases": [ - "Chatbots", - "Customer Support" - ] - }, - "id": "tryolabs/restricttotopic", - "namespace": "tryolabs", - "packageName": "restricttotopic", - "moduleName": "validator", - "requiresAuth": false, - "postInstall": "post-install.py" -} \ No newline at end of file diff --git a/default.env b/default.env new file mode 100644 index 0000000..674906a --- /dev/null +++ b/default.env @@ -0,0 +1,7 @@ +APP_ENVIRONMENT=local +PYTHONUNBUFFERED=1 +LOGLEVEL="INFO" +GUARDRAILS_LOG_LEVEL="INFO" +GUARDRAILS_PROCESS_COUNT=1 +SELF_ENDPOINT=http://localhost:8000 +OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES \ No newline at end of file diff --git a/dev-build.sh b/dev-build.sh deleted file mode 100644 index fd4483e..0000000 --- a/dev-build.sh +++ /dev/null @@ -1,12 +0,0 @@ -curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml - -npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - -docker build \ - -f Dockerfile.dev \ - --progress=plain \ - --no-cache \ - --build-arg CACHEBUST="$(date)" \ - --build-arg GITHUB_TOKEN="$GITHUB_TOKEN" \ - --build-arg HF_TOKEN="$HF_TOKEN" \ - -t "guardrails-api:dev" .; diff --git a/dev-run.sh b/dev-run.sh deleted file mode 100644 index 2370bdf..0000000 --- a/dev-run.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker stop guardrails-api-dev || true -docker rm guardrails-api-dev || true -docker run -p 8000:8000 --env-file local.env --name guardrails-api-dev -it guardrails-api:dev \ No newline at end of file diff --git a/dev.sh b/dev.sh deleted file mode 100644 index 711755e..0000000 --- a/dev.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# pip uninstall -y guardrails-ai -# rm -rf ./guardrails-sdk -# cp -r ../guardrails ./guardrails-sdk -# pip install ../guardrails - -# cp -r ../guardrails-custom-validators ./guardrails-custom-validators - -mkdir -p ./pgadmin-data - -cp ./pgadmin-dev-server.json ./pgadmin-data/servers.json -PG_PASSWORD="${PGPASSWORD:-changeme}" -echo "$PG_PASSWORD" > ./pgadmin-data/passfile - -bash dev-build.sh -bash dev-run.sh \ No newline at end of file diff --git a/diagrams/GuardRails Infra - Option 1.drawio b/diagrams/GuardRails Infra - Option 1.drawio deleted file mode 100644 index b26aca8..0000000 --- a/diagrams/GuardRails Infra - Option 1.drawio +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/diagrams/GuardRails Infra - Option 1.png b/diagrams/GuardRails Infra - Option 1.png deleted file mode 100644 index 678c4dc309237e99417b59e1e26286d69b0899f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133374 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

^T}Goe?MXP=jBaGs%FTm)))tJD5Bl|PM?3Rc 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%0A4(}ru)`K)LKnfGOJ16A}Ei;uH&W*Srpt5R3u99 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/diagrams/GuardRails Infra - Option 2.png b/diagrams/GuardRails Infra - Option 2.png deleted file mode 100644 index e4153a83c72a1f7eda562af700b46001973dd9f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153773 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 Z|pE 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 diff --git a/guard-rail-spec.xml b/guard-rail-spec.xml deleted file mode 100644 index 03c2154..0000000 --- a/guard-rail-spec.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - Generate a dataset of fake user orders. Each row of the dataset should be valid. - - ${gr.complete_json_suffix} - - \ No newline at end of file diff --git a/guard.json b/guard.json deleted file mode 100644 index 3c874f0..0000000 --- a/guard.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "guard-1", - "railspec": { - "outputSchema": { - "schema": { - "user_orders": { - "element": { - "type": "list", - "name": "user_orders", - "description": "Generate a list of user, and how many orders they have placed in the past.", - "onFail": "noop" - }, - "formatters": ["length: 1 10"], - "children": { - "item": { - "user_id": { - "element": { - "type": "string", - "name": "user_id", - "description": "The user's id." - }, - "formatters": [] - }, - "user_name": { - "element": { - "type": "string", - "name": "user_name", - "description": "The user's first name and last name" - }, - "formatters": ["two-words"] - }, - "num_orders": { - "element": { - "type": "integer", - "name": "num_orders", - "description": "The number of orders the user has placed" - }, - "formatters": ["valid-range: 0 50"] - }, - "city": { - "element": { - "type": "string", - "name": "city", - "description": "City where user lives" - }, - "formatters": ["valid-choices: {['Chicago', 'SF', 'NY']}"] - }, - "last_order_date": { - "element": { - "type": "date", - "name": "last_order_date", - "description": "Date of last order" - }, - "formatters": [] - } - } - } - } - } - }, - "prompt": "Generate a dataset of fake user orders. Each row of the dataset should be valid.\n\n${gr.complete_json_suffix}", - "version": "0.1" - }, - "numReasks": 1 -} \ No newline at end of file diff --git a/heavy-run.sh b/heavy-run.sh deleted file mode 100644 index b9c1094..0000000 --- a/heavy-run.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker stop guardrails-api-heavy || true -docker rm guardrails-api-heavy || true -docker run -p 8000:8000 --env-file local.env --name guardrails-api-heavy -it graas-heavy:latest \ No newline at end of file diff --git a/hub-requirements.txt b/hub-requirements.txt deleted file mode 100644 index c117ad4..0000000 --- a/hub-requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -hub://guardrails/ends_with -hub://guardrails/extracted_summary_sentences_match -hub://guardrails/llm_critic -hub://guardrails/provenance_embeddings -hub://guardrails/valid_length -hub://guardrails/regex_match \ No newline at end of file diff --git a/ingestion-service-spec.yml b/ingestion-service-spec.yml deleted file mode 100644 index 0afb15e..0000000 --- a/ingestion-service-spec.yml +++ /dev/null @@ -1,117 +0,0 @@ -openapi: 3.0.0 -info: - title: Ingestion Service API - description: Ingestion and Retrieval APIs for Ingestion Service - version: 0.1.9 -paths: - /ingest: - post: - operationId: ingest - summary: Creates and ingests embeddings - requestBody: - required: true - content: - text/plain: - schema: - $ref: '#/components/schemas/IngestionPayload' - responses: - '200': # status code - description: uniqie id for the created ingestion object - content: - application/json: - schema: - type: string - /embeddings/{uuid}: - get: - operationId: getEmbeddings - summary: Returns a ingestion object for given uuid - parameters: - - $ref: '#/components/parameters/uuid' - responses: - '200': # status code - description: the ingestion object - content: - application/json: - schema: - $ref: '#/components/schemas/Ingestion' - put: - operationId: updateEmbeddings - summary: Updates embeddings for a given uuid - parameters: - - $ref: '#/components/parameters/uuid' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/EmbeddingsPayload' - responses: - '200': # status code - description: The updated ingestion object - content: - application/json: - schema: - $ref: '#/components/schemas/Ingestion' - delete: - operationId: deleteEmbeddings - summary: Deletes embeddings for a given uuid - parameters: - - $ref: '#/components/parameters/uuid' - responses: - '200': # status code - description: The deleted ingestion - content: - application/json: - schema: - $ref: '#/components/schemas/Ingestion' -components: - parameters: - uuid: - name: uuid - in: path - description: unqiue id for embeddings - required: true - schema: - type: string - schemas: - Ingestion: - type: object - properties: - uuid: - type: string - gaurdId: - type: string - embeddings: - type: array - items: - type: array - items: - type: number - metadata: - type: object - ingestionStatus: - type: string - validatorId: - type: string - IngestionPayload: - type: object - properties: - articles: - type: array - items: - type: string - gaurdId: - type: string - metadata: - type: object - validatorId: - type: string - EmbeddingsPayload: - allOf: - - $ref: '#/components/schemas/IngestionPayload' - - type: object - properties: - uuid: - type: string - - \ No newline at end of file diff --git a/lite-build.sh b/lite-build.sh deleted file mode 100755 index 9fb9508..0000000 --- a/lite-build.sh +++ /dev/null @@ -1,12 +0,0 @@ -curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml - -npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - -docker build \ - -f Dockerfile.lite \ - --progress=plain \ - --no-cache \ - --build-arg CACHEBUST="$(date)" \ - --build-arg GITHUB_TOKEN="$GITHUB_TOKEN" \ - --build-arg HF_TOKEN="$HF_TOKEN" \ - -t "guardrails-api:lite" .; diff --git a/lite-run.sh b/lite-run.sh deleted file mode 100755 index a6086a3..0000000 --- a/lite-run.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker stop guardrails-api-lite || true -docker rm guardrails-api-lite || true -docker run -p 8000:8000 --env-file local.env --name guardrails-api-lite -it guardrails-api:lite \ No newline at end of file diff --git a/local.sh b/local.sh deleted file mode 100755 index f8a671a..0000000 --- a/local.sh +++ /dev/null @@ -1,52 +0,0 @@ -export APP_ENVIRONMENT=local -# export NODE_ENV=production -export AWS_PROFILE=dev -export AWS_DEFAULT_REGION=us-east-1 -export PGPORT=5432 -export PGDATABASE=postgres -export PGHOST=localhost -export PGUSER=${PGUSER:-postgres} -export PGPASSWORD=${PGPASSWORD:-changeme} - -# export AWS_EXECUTION_ENV=AWS_ECS_Fargate - -export PYTHONUNBUFFERED=1 -export OTEL_PYTHON_TRACER_PROVIDER=sdk_tracer_provider -export OTEL_SERVICE_NAME=guardrails-api -export OTEL_TRACES_EXPORTER=otlp #,console -export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="Accept-Encoding,User-Agent,Referer" -export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="Last-Modified,Content-Type" -export OTEL_METRICS_EXPORTER=none #otlp #,console - -# export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf -# export OTEL_EXPORTER_OTLP_ENDPOINT=https://hty0gc1ok3.execute-api.us-east-1.amazonaws.com -export OTEL_EXPORTER_OTLP_PROTOCOL=grpc -export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 - -export OTEL_SDK_DISABLED=true - -# export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://hty0gc1ok3.execute-api.us-east-1.amazonaws.com/v1/traces -# export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://hty0gc1ok3.execute-api.us-east-1.amazonaws.com/v1/metrics -# export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://hty0gc1ok3.execute-api.us-east-1.amazonaws.com/v1/logs -export LOGLEVEL="INFO" -export GUARDRAILS_LOG_LEVEL="INFO" -export GUARDRAILS_PROCESS_COUNT=1 -export SELF_ENDPOINT=http://localhost:8000 -export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES -export HF_API_KEY=${HF_TOKEN} - - -curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml -npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - -# For running https locally -# mkdir -p ~/certificates -# if [ ! -f ~/certificates/local.key ]; then -# openssl req -nodes -new -x509 -keyout ~/certificates/local.key -out ~/certificates/local.cert -# fi - - - -# For running https locally -# gunicorn --keyfile ~/certificates/local.key --certfile ~/certificates/local.cert --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" -gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" diff --git a/name-guard.json b/name-guard.json deleted file mode 100644 index 2220823..0000000 --- a/name-guard.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "name-guard", - "railspec": { - "outputSchema": { - "schema": { - "names": { - "element": { - "type": "list", - "name": "names", - "description": "Generate a list of names, each entry should be an object with two properties: firstName and lastName.", - "onFail": "noop" - }, - "formatters": ["length: 1 10"], - "children": { - "item": { - "firstName": { - "element": { - "type": "string", - "name": "firstName", - "description": "The person's first name." - }, - "formatters": ["one-word", "length: 3 12"] - }, - "lastName": { - "element": { - "type": "string", - "name": "lastName", - "description": "The person's last name." - }, - "formatters": ["one-word", "length: 3 24"] - } - } - } - } - } - }, - "prompt": "Generate a dataset of fake people. Each row of the dataset should be valid.\n\n${gr.complete_json_suffix}", - "version": "0.1" - }, - "numReasks": 1 -} \ No newline at end of file diff --git a/object-string-guard.json b/object-string-guard.json deleted file mode 100644 index 2e56fa4..0000000 --- a/object-string-guard.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "object-string-guard", - "railspec": { - "outputSchema": { - "schema": { - "pet_name": { - "element": { - "type": "string", - "name": "pet_name", - "description": "Generate a pet name.", - "onFail": "fix" - }, - "formatters": ["length: 1 10"] - } - } - }, - "prompt": "Generate a random pet name between 1 and 10 characters. Each row of the dataset should be valid.\n\n${gr.complete_json_suffix}", - "version": "0.1" - }, - "numReasks": 1 -} \ No newline at end of file diff --git a/prod-build.sh b/prod-build.sh deleted file mode 100644 index 8ed2000..0000000 --- a/prod-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml - -# Dereference API Spec to JSON -npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - -# Copy Lambda Layer and convert to extension -# if [ ! -d "opentelemetry-lambda-layer" ]; then -# curl $(aws lambda get-layer-version-by-arn --arn arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-collector-arm64-0_2_0:1 --query 'Content.Location' --output text) --output otel-collector.zip -# unzip otel-collector.zip -d ./opentelemetry-lambda-layer -# rm otel-collector.zip -# fi - -docker build \ - -f Dockerfile.prod \ - --progress=plain \ - --build-arg CACHEBUST="$(date)" \ - --no-cache \ - -t "guardrails-api:prod" .; diff --git a/prod-run.sh b/prod-run.sh deleted file mode 100644 index 257f73e..0000000 --- a/prod-run.sh +++ /dev/null @@ -1,8 +0,0 @@ -docker stop guardrails-api-prod || true -docker rm guardrails-api-prod || true -docker run \ - -p 8000:8000 \ - -v ~/.aws:/root/.aws \ - --env-file local-prod.env \ - --name guardrails-api-prod \ - -it guardrails-api:prod; \ No newline at end of file diff --git a/refresh.sh b/refresh.sh deleted file mode 100644 index ed1ffb8..0000000 --- a/refresh.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -echo "Deactivating old virtual environment" -deactivate - -echo "Deleting old virtual environment" -rm -rf ./.venv - -echo "Creating new virtual environment" -python3 -m venv ./.venv - -echo "Sourcing new virtual environment" -source ./.venv/bin/activate - -echo "Installing dependencies" -pip install -r requirements.txt --no-cache-dir; -# make install -# make install-dev \ No newline at end of file diff --git a/requests.http b/requests.http deleted file mode 100644 index ed6beb1..0000000 --- a/requests.http +++ /dev/null @@ -1,16 +0,0 @@ -@hostname=localhost -@port=8000 -@host={{hostname}}:{{port}} - -GET http://{{host}}/guards -Content-Type: application/json - -### - -POST http://{{host}}/guards -Content-Type: application/json - -{ - "name": "guard-galaxy", - "railspec": {} -} \ No newline at end of file diff --git a/sdk-test.py b/sdk-test.py deleted file mode 100644 index f763ccb..0000000 --- a/sdk-test.py +++ /dev/null @@ -1,7 +0,0 @@ -from guard_rails_api_client import AuthenticatedClient -from guard_rails_api_client.api.guard import get_guards - -client = AuthenticatedClient(base_url="http://localhost:8000", follow_redirects=True, token="test-token") -guards = get_guards.sync(client=client) -guards_json = [g.to_dict() for g in guards] -print('guards: ', guards_json) diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..0b6fa25 --- /dev/null +++ b/start.sh @@ -0,0 +1,13 @@ +export APP_ENVIRONMENT=local +export PYTHONUNBUFFERED=1 +export LOGLEVEL="INFO" +export GUARDRAILS_LOG_LEVEL="INFO" +export GUARDRAILS_PROCESS_COUNT=1 +export SELF_ENDPOINT=http://localhost:8000 +export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + +# TODO: Include the bundled api spec in the published package +curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml +npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml + +gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" diff --git a/string-guard-rail-spec.xml b/string-guard-rail-spec.xml deleted file mode 100644 index bb8ec00..0000000 --- a/string-guard-rail-spec.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - Generate a random pet name between 1 and 10 characters. Return only the generated pet name and nothing else. Don't talk, just go. - - \ No newline at end of file diff --git a/string-guard.json b/string-guard.json deleted file mode 100644 index 821dbed..0000000 --- a/string-guard.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "string-guard", - "railspec": { - "outputSchema": { - "schema": { - "element": { - "type": "string", - "name": "pet_name", - "description": "Generate a pet name.", - "onFail": "fix" - }, - "formatters": ["hub://guardrails/valid_length: 1 10"] - } - }, - "prompt": "Generate a random pet name between 1 and 10 characters. Return only the generated pet name and nothing else. Don't talk, just go.", - "version": "0.1" - }, - "numReasks": 1 -} \ No newline at end of file diff --git a/test.ipynb b/test.ipynb deleted file mode 100644 index 8784b77..0000000 --- a/test.ipynb +++ /dev/null @@ -1,826 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "some-token\n", - "http://localhost:8000\n" - ] - } - ], - "source": [ - "import os\n", - "\n", - "os.environ[\"GUARDRAILS_API_KEY\"] = \"some-token\"\n", - "os.environ[\"GUARDRAILS_BASE_URL\"] = \"http://localhost:8000\"\n", - "\n", - "\n", - "print(os.environ.get(\"GUARDRAILS_API_KEY\"))\n", - "print(os.environ.get(\"GUARDRAILS_BASE_URL\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/calebcourier/Projects/gr-mono/guardrails-cdk/guardrails-api/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "prov-test\n" - ] - } - ], - "source": [ - "from guardrails import Guard\n", - "from guardrails.hub import ProvenanceLLM\n", - "\n", - "SOURCES = [\n", - " \"The sun is a star.\",\n", - " \"The sun rises in the east and sets in the west.\",\n", - " \"Sun is the largest object in the solar system, and all planets revolve around it.\",\n", - "]\n", - "\n", - "guard = Guard(name=\"prov-test\").use(\n", - " ProvenanceLLM(),\n", - " stream=True\n", - ")\n", - "\n", - "print(guard.name)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

ValidationOutcome(raw_llm_output='The', validated_output='The', reask=None, validation_passed=True, error=None)\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The'\u001b[0m, \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The'\u001b[0m, \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m, \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun',\n",
-       "    validated_output='The sun',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is',\n",
-       "    validated_output='The sun is',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a',\n",
-       "    validated_output='The sun is a',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star',\n",
-       "    validated_output='The sun is a star',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at',\n",
-       "    validated_output='The sun is a star at',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the',\n",
-       "    validated_output='The sun is a star at the',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center',\n",
-       "    validated_output='The sun is a star at the center',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of',\n",
-       "    validated_output='The sun is a star at the center of',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our',\n",
-       "    validated_output='The sun is a star at the center of our',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar',\n",
-       "    validated_output='The sun is a star at the center of our solar',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system',\n",
-       "    validated_output='The sun is a star at the center of our solar system',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that',\n",
-       "    validated_output='The sun is a star at the center of our solar system that',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to Earth',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to Earth',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
last message: \n",
-       "ValidationOutcome(\n",
-       "    raw_llm_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    validated_output='The sun is a star at the center of our solar system that provides light and heat to Earth.',\n",
-       "    reask=None,\n",
-       "    validation_passed=True,\n",
-       "    error=None\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "last message: \n", - "\u001b[1;35mValidationOutcome\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mraw_llm_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[32m'The sun is a star at the center of our solar system that provides light and heat to Earth.'\u001b[0m,\n", - " \u001b[33mreask\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mvalidation_passed\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import litellm\n", - "from rich import print\n", - "\n", - "response = guard(\n", - " llm_api=litellm.completion,\n", - " model=\"gpt-3.5-turbo\",\n", - " instructions=\"You are a helpful assistant.\",\n", - " prompt=\"Write a short and accurate statement about the sun.\",\n", - " metadata={\"sources\": SOURCES},\n", - " stream=True\n", - ")\n", - "\n", - "fragment_count = 0\n", - "last_message = None\n", - "for message in response:\n", - " fragment_count += 1\n", - " print(message)\n", - " last_message = message\n", - " \n", - "print(\"last message: \", last_message)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Call(\n",
-       "    iterations=[\n",
-       "        Iteration(\n",
-       "            inputs=Inputs(\n",
-       "                llm_api=None,\n",
-       "                llm_output=None,\n",
-       "                instructions=Instructions(You are a helpful assistant.),\n",
-       "                prompt=Prompt(Write a short and accurate statement about the sun...),\n",
-       "                msg_history=None,\n",
-       "                prompt_params={},\n",
-       "                num_reasks=1,\n",
-       "                metadata={\n",
-       "                    'sources': [\n",
-       "                        'The sun is a star.',\n",
-       "                        'The sun rises in the east and sets in the west.',\n",
-       "                        'Sun is the largest object in the solar system, and all planets revolve around it.'\n",
-       "                    ]\n",
-       "                },\n",
-       "                full_schema_reask=False\n",
-       "            ),\n",
-       "            outputs=Outputs(\n",
-       "                llm_response_info=LLMResponse(\n",
-       "                    prompt_token_count=None,\n",
-       "                    response_token_count=None,\n",
-       "                    output='The sun is a star located at the center of our solar system, providing light and heat \n",
-       "to Earth.',\n",
-       "                    stream_output=None\n",
-       "                ),\n",
-       "                raw_output='The sun is a star located at the center of our solar system, providing light and heat \n",
-       "to Earth.',\n",
-       "                parsed_output='The sun is a star located at the center of our solar system, providing light and \n",
-       "heat to Earth.',\n",
-       "                validation_response=None,\n",
-       "                guarded_output=None,\n",
-       "                reasks=[],\n",
-       "                validator_logs=[],\n",
-       "                error=None,\n",
-       "                exception=None\n",
-       "            )\n",
-       "        )\n",
-       "    ],\n",
-       "    inputs=CallInputs(\n",
-       "        llm_api=<function completion at 0x16800a8e0>,\n",
-       "        llm_output=None,\n",
-       "        instructions='You are a helpful assistant.',\n",
-       "        prompt='Write a short and accurate statement about the sun.',\n",
-       "        msg_history=None,\n",
-       "        prompt_params={},\n",
-       "        num_reasks=1,\n",
-       "        metadata={\n",
-       "            'sources': [\n",
-       "                'The sun is a star.',\n",
-       "                'The sun rises in the east and sets in the west.',\n",
-       "                'Sun is the largest object in the solar system, and all planets revolve around it.'\n",
-       "            ]\n",
-       "        },\n",
-       "        full_schema_reask=False,\n",
-       "        args=[],\n",
-       "        kwargs={'model': 'gpt-3.5-turbo', 'stream': True}\n",
-       "    )\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mCall\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33miterations\u001b[0m=\u001b[1m[\u001b[0m\n", - " \u001b[1;35mIteration\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33minputs\u001b[0m=\u001b[1;35mInputs\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mllm_api\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mllm_output\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33minstructions\u001b[0m=\u001b[1;35mInstructions\u001b[0m\u001b[1m(\u001b[0mYou are a helpful assistant.\u001b[1m)\u001b[0m,\n", - " \u001b[33mprompt\u001b[0m=\u001b[1;35mPrompt\u001b[0m\u001b[1m(\u001b[0mWrite a short and accurate statement about the sun\u001b[33m...\u001b[0m\u001b[1m)\u001b[0m,\n", - " \u001b[33mmsg_history\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mprompt_params\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[33mnum_reasks\u001b[0m=\u001b[1;36m1\u001b[0m,\n", - " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\n", - " \u001b[32m'sources'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[32m'The sun is a star.'\u001b[0m,\n", - " \u001b[32m'The sun rises in the east and sets in the west.'\u001b[0m,\n", - " \u001b[32m'Sun is the largest object in the solar system, and all planets revolve around it.'\u001b[0m\n", - " \u001b[1m]\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[33mfull_schema_reask\u001b[0m=\u001b[3;91mFalse\u001b[0m\n", - " \u001b[1m)\u001b[0m,\n", - " \u001b[33moutputs\u001b[0m=\u001b[1;35mOutputs\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mllm_response_info\u001b[0m=\u001b[1;35mLLMResponse\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mprompt_token_count\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mresponse_token_count\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33moutput\u001b[0m=\u001b[32m'The sun is a star located at the center of our solar system, providing light and heat \u001b[0m\n", - "\u001b[32mto Earth.'\u001b[0m,\n", - " \u001b[33mstream_output\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - " \u001b[1m)\u001b[0m,\n", - " \u001b[33mraw_output\u001b[0m=\u001b[32m'The sun is a star located at the center of our solar system, providing light and heat \u001b[0m\n", - "\u001b[32mto Earth.'\u001b[0m,\n", - " \u001b[33mparsed_output\u001b[0m=\u001b[32m'The sun is a star located at the center of our solar system, providing light and \u001b[0m\n", - "\u001b[32mheat to Earth.'\u001b[0m,\n", - " \u001b[33mvalidation_response\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mguarded_output\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mreasks\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mvalidator_logs\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33merror\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mexception\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - " \u001b[1m)\u001b[0m\n", - " \u001b[1m)\u001b[0m\n", - " \u001b[1m]\u001b[0m,\n", - " \u001b[33minputs\u001b[0m=\u001b[1;35mCallInputs\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mllm_api\u001b[0m=\u001b[1m<\u001b[0m\u001b[1;95mfunction\u001b[0m\u001b[39m completion at \u001b[0m\u001b[1;36m0x16800a8e0\u001b[0m\u001b[1m>\u001b[0m,\n", - " \u001b[33mllm_output\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33minstructions\u001b[0m=\u001b[32m'You are a helpful assistant.'\u001b[0m,\n", - " \u001b[33mprompt\u001b[0m=\u001b[32m'Write a short and accurate statement about the sun.'\u001b[0m,\n", - " \u001b[33mmsg_history\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - " \u001b[33mprompt_params\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[33mnum_reasks\u001b[0m=\u001b[1;36m1\u001b[0m,\n", - " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\n", - " \u001b[32m'sources'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[32m'The sun is a star.'\u001b[0m,\n", - " \u001b[32m'The sun rises in the east and sets in the west.'\u001b[0m,\n", - " \u001b[32m'Sun is the largest object in the solar system, and all planets revolve around it.'\u001b[0m\n", - " \u001b[1m]\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[33mfull_schema_reask\u001b[0m=\u001b[3;91mFalse\u001b[0m,\n", - " \u001b[33margs\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", - " \u001b[33mkwargs\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'model'\u001b[0m: \u001b[32m'gpt-3.5-turbo'\u001b[0m, \u001b[32m'stream'\u001b[0m: \u001b[3;92mTrue\u001b[0m\u001b[1m}\u001b[0m\n", - " \u001b[1m)\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(guard.history.last)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From dea441c933f901b7a7a3a133451e8a1b96dea88a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 5 Jun 2024 10:37:02 -0500 Subject: [PATCH 03/12] add todos --- TODO.md | 5 +++++ app.py | 1 + src/blueprints/guards.py | 1 + 3 files changed, 7 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..a01905b --- /dev/null +++ b/TODO.md @@ -0,0 +1,5 @@ + - [ ] Accept env file path as env var + - [ ] Accept config file path as env var + - [ ] Add pyproject toml and setup.py + - [ ] Add cli command to start server with arguments + - [ ] Update merge github action to publish module \ No newline at end of file diff --git a/app.py b/app.py index 97ffdba..2b70f35 100644 --- a/app.py +++ b/app.py @@ -24,6 +24,7 @@ def create_app(): if os.environ.get("APP_ENVIRONMENT") != "production": from dotenv import load_dotenv + # TODO: Accept this as an env var load_dotenv() app = Flask(__name__) diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 2daa7c3..f7e5cbd 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -27,6 +27,7 @@ guard_client = PGGuardClient() else: guard_client = MemoryGuardClient() + # TODO: Accept file path as env var and dynamically import # read in guards from file import config From e03a208ee059f04c1c3ca76572853d763389270d Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 5 Jun 2024 13:30:35 -0500 Subject: [PATCH 04/12] move to guardrails_api directory --- Makefile | 8 +-- {src => guardrails_api}/__init__.py | 0 app.py => guardrails_api/app.py | 11 ++-- .../blueprints/__init__.py | 0 {src => guardrails_api}/blueprints/guards.py | 22 +++---- {src => guardrails_api}/blueprints/root.py | 9 ++- {src => guardrails_api}/classes/__init__.py | 0 .../classes/data_type_struct.py | 6 +- .../classes/element_stub.py | 0 .../classes/guard_struct.py | 8 +-- .../classes/health_check.py | 0 {src => guardrails_api}/classes/http_error.py | 0 .../classes/rail_spec_struct.py | 6 +- .../classes/schema_element_struct.py | 4 +- .../classes/schema_struct.py | 2 +- .../classes/validation_output.py | 2 +- {src => guardrails_api}/clients/__init__.py | 0 .../clients/guard_client.py | 2 +- .../clients/memory_guard_client.py | 4 +- .../clients/pg_guard_client.py | 12 ++-- .../clients/postgres_client.py | 6 +- {src => guardrails_api}/otel/__init__.py | 6 +- {src => guardrails_api}/otel/constants.py | 0 {src => guardrails_api}/otel/logs.py | 2 +- {src => guardrails_api}/otel/metrics.py | 2 +- {src => guardrails_api}/otel/traces.py | 2 +- start.sh => guardrails_api/start.sh | 0 {src => guardrails_api}/utils/__init__.py | 0 .../utils/escape_curlys.py | 0 {src => guardrails_api}/utils/file.py | 0 .../utils/gather_request_metrics.py | 0 .../utils/get_llm_callable.py | 0 {src => guardrails_api}/utils/handle_error.py | 4 +- {src => guardrails_api}/utils/logger.py | 3 - .../utils/payload_validator.py | 4 +- {src => guardrails_api}/utils/pip.py | 4 +- {src => guardrails_api}/utils/pluck.py | 0 .../utils/prep_environment.py | 6 +- {src => guardrails_api}/utils/remove_nones.py | 0 .../utils/try_json_loads.py | 0 pyproject.toml | 47 ++++++++++++++ src/models/base.py | 8 --- src/models/guard_item.py | 27 -------- src/models/guard_item_audit.py | 63 ------------------- tests/blueprints/test_guards.py | 28 ++++----- tests/blueprints/test_root.py | 6 +- tests/clients/test_mem_guard_client.py | 11 ++-- tests/clients/test_pg_guard_client.py | 34 +++++----- tests/mocks/mock_guard_client.py | 2 +- tests/utils/test_pluck.py | 2 +- tests/utils/test_remove_nones.py | 2 +- wsgi.py | 8 --- 52 files changed, 151 insertions(+), 222 deletions(-) rename {src => guardrails_api}/__init__.py (100%) rename app.py => guardrails_api/app.py (82%) rename {src => guardrails_api}/blueprints/__init__.py (100%) rename {src => guardrails_api}/blueprints/guards.py (94%) rename {src => guardrails_api}/blueprints/root.py (89%) rename {src => guardrails_api}/classes/__init__.py (100%) rename {src => guardrails_api}/classes/data_type_struct.py (98%) rename {src => guardrails_api}/classes/element_stub.py (100%) rename {src => guardrails_api}/classes/guard_struct.py (92%) rename {src => guardrails_api}/classes/health_check.py (100%) rename {src => guardrails_api}/classes/http_error.py (100%) rename {src => guardrails_api}/classes/rail_spec_struct.py (97%) rename {src => guardrails_api}/classes/schema_element_struct.py (98%) rename {src => guardrails_api}/classes/schema_struct.py (98%) rename {src => guardrails_api}/classes/validation_output.py (98%) rename {src => guardrails_api}/clients/__init__.py (100%) rename {src => guardrails_api}/clients/guard_client.py (94%) rename {src => guardrails_api}/clients/memory_guard_client.py (93%) rename {src => guardrails_api}/clients/pg_guard_client.py (90%) rename {src => guardrails_api}/clients/postgres_client.py (92%) rename {src => guardrails_api}/otel/__init__.py (81%) rename {src => guardrails_api}/otel/constants.py (100%) rename {src => guardrails_api}/otel/logs.py (76%) rename {src => guardrails_api}/otel/metrics.py (97%) rename {src => guardrails_api}/otel/traces.py (97%) rename start.sh => guardrails_api/start.sh (100%) rename {src => guardrails_api}/utils/__init__.py (100%) rename {src => guardrails_api}/utils/escape_curlys.py (100%) rename {src => guardrails_api}/utils/file.py (100%) rename {src => guardrails_api}/utils/gather_request_metrics.py (100%) rename {src => guardrails_api}/utils/get_llm_callable.py (100%) rename {src => guardrails_api}/utils/handle_error.py (89%) rename {src => guardrails_api}/utils/logger.py (71%) rename {src => guardrails_api}/utils/payload_validator.py (90%) rename {src => guardrails_api}/utils/pip.py (95%) rename {src => guardrails_api}/utils/pluck.py (100%) rename {src => guardrails_api}/utils/prep_environment.py (85%) rename {src => guardrails_api}/utils/remove_nones.py (100%) rename {src => guardrails_api}/utils/try_json_loads.py (100%) create mode 100644 pyproject.toml delete mode 100644 src/models/base.py delete mode 100644 src/models/guard_item.py delete mode 100644 src/models/guard_item_audit.py delete mode 100644 wsgi.py diff --git a/Makefile b/Makefile index fc5dda8..60c6bae 100644 --- a/Makefile +++ b/Makefile @@ -37,13 +37,13 @@ refresh: format: - ruff check app.py wsgi.py src/ tests/ --fix - ruff format app.py wsgi.py src/ tests/ + ruff check app.py src/ tests/ --fix + ruff format app.py src/ tests/ lint: - ruff check app.py wsgi.py src/ tests/ - ruff format app.py wsgi.py src/ tests/ + ruff check app.py src/ tests/ + ruff format app.py src/ tests/ qa: make lint diff --git a/src/__init__.py b/guardrails_api/__init__.py similarity index 100% rename from src/__init__.py rename to guardrails_api/__init__.py diff --git a/app.py b/guardrails_api/app.py similarity index 82% rename from app.py rename to guardrails_api/app.py index 2b70f35..4e16246 100644 --- a/app.py +++ b/guardrails_api/app.py @@ -5,8 +5,8 @@ from urllib.parse import urlparse from guardrails import configure_logging from opentelemetry.instrumentation.flask import FlaskInstrumentor -from src.clients.postgres_client import postgres_is_enabled -from src.otel import otel_is_disabled, initialize +from guardrails_api.clients.postgres_client import postgres_is_enabled +from guardrails_api.otel import otel_is_disabled, initialize class ReverseProxied(object): @@ -39,20 +39,19 @@ def create_app(): guardrails_log_level = os.environ.get("GUARDRAILS_LOG_LEVEL", "INFO") configure_logging(log_level=guardrails_log_level) - print("otel_is_disabled: ", otel_is_disabled()) if not otel_is_disabled(): FlaskInstrumentor().instrument_app(app) initialize() # if no pg_host is set, don't set up postgres if postgres_is_enabled(): - from src.clients.postgres_client import PostgresClient + from guardrails_api.clients.postgres_client import PostgresClient pg_client = PostgresClient() pg_client.initialize(app) - from src.blueprints.root import root_bp - from src.blueprints.guards import guards_bp + from guardrails_api.blueprints.root import root_bp + from guardrails_api.blueprints.guards import guards_bp app.register_blueprint(root_bp) app.register_blueprint(guards_bp) diff --git a/src/blueprints/__init__.py b/guardrails_api/blueprints/__init__.py similarity index 100% rename from src/blueprints/__init__.py rename to guardrails_api/blueprints/__init__.py diff --git a/src/blueprints/guards.py b/guardrails_api/blueprints/guards.py similarity index 94% rename from src/blueprints/guards.py rename to guardrails_api/blueprints/guards.py index f7e5cbd..bc1e195 100644 --- a/src/blueprints/guards.py +++ b/guardrails_api/blueprints/guards.py @@ -8,15 +8,15 @@ from guardrails import Guard from guardrails.classes import ValidationOutcome from opentelemetry.trace import Span -from src.classes.guard_struct import GuardStruct -from src.classes.http_error import HttpError -from src.classes.validation_output import ValidationOutput -from src.clients.memory_guard_client import MemoryGuardClient -from src.clients.pg_guard_client import PGGuardClient -from src.clients.postgres_client import postgres_is_enabled -from src.utils.handle_error import handle_error -from src.utils.get_llm_callable import get_llm_callable -from src.utils.prep_environment import cleanup_environment, prep_environment +from guardrails_api.classes.guard_struct import GuardStruct +from guardrails_api.classes.http_error import HttpError +from guardrails_api.classes.validation_output import ValidationOutput +from guardrails_api.clients.memory_guard_client import MemoryGuardClient +from guardrails_api.clients.pg_guard_client import PGGuardClient +from guardrails_api.clients.postgres_client import postgres_is_enabled +from guardrails_api.utils.handle_error import handle_error +from guardrails_api.utils.get_llm_callable import get_llm_callable +from guardrails_api.utils.prep_environment import cleanup_environment, prep_environment guards_bp = Blueprint("guards", __name__, url_prefix="/guards") @@ -164,8 +164,6 @@ def collect_telemetry( @guards_bp.route("//validate", methods=["POST"]) @handle_error def validate(guard_name: str): - from rich import print - # Do we actually need a child span here? # We could probably use the existing span from the request unless we forsee # capturing the same attributes on non-GaaS Guard runs. @@ -279,7 +277,6 @@ def validate_streamer(guard_iter): next_result = result # next_validation_output = validation_output fragment = json.dumps(validation_output.to_response()) - print("yielding fragment") yield f"{fragment}\n" final_validation_output: ValidationOutput = ValidationOutput( @@ -298,7 +295,6 @@ def validate_streamer(guard_iter): # result=next_result # ) final_output_json = json.dumps(final_validation_output.to_response()) - print("Yielding final output.") yield f"{final_output_json}\n" return Response( diff --git a/src/blueprints/root.py b/guardrails_api/blueprints/root.py similarity index 89% rename from src/blueprints/root.py rename to guardrails_api/blueprints/root.py index 18a6cd9..78ff7f7 100644 --- a/src/blueprints/root.py +++ b/guardrails_api/blueprints/root.py @@ -4,12 +4,11 @@ from string import Template from flask import Blueprint from sqlalchemy import text -from src.classes.health_check import HealthCheck -from src.clients.postgres_client import PostgresClient, postgres_is_enabled -from src.utils.handle_error import handle_error -from src.utils.logger import logger +from guardrails_api.classes.health_check import HealthCheck +from guardrails_api.clients.postgres_client import PostgresClient, postgres_is_enabled +from guardrails_api.utils.handle_error import handle_error +from guardrails_api.utils.logger import logger -# from src.modules.otel_logger import logger root_bp = Blueprint("root", __name__, url_prefix="/") cached_api_spec = None diff --git a/src/classes/__init__.py b/guardrails_api/classes/__init__.py similarity index 100% rename from src/classes/__init__.py rename to guardrails_api/classes/__init__.py diff --git a/src/classes/data_type_struct.py b/guardrails_api/classes/data_type_struct.py similarity index 98% rename from src/classes/data_type_struct.py rename to guardrails_api/classes/data_type_struct.py index f687e02..5fbe7c5 100644 --- a/src/classes/data_type_struct.py +++ b/guardrails_api/classes/data_type_struct.py @@ -4,9 +4,9 @@ from lxml.etree import _Element, SubElement from guardrails.datatypes import DataType, registry from guardrails.validatorsattr import ValidatorsAttr -from src.classes.schema_element_struct import SchemaElementStruct -from src.classes.element_stub import ElementStub -from src.utils.pluck import pluck +from guardrails_api.classes.schema_element_struct import SchemaElementStruct +from guardrails_api.classes.element_stub import ElementStub +from guardrails_api.utils.pluck import pluck class DataTypeStruct: diff --git a/src/classes/element_stub.py b/guardrails_api/classes/element_stub.py similarity index 100% rename from src/classes/element_stub.py rename to guardrails_api/classes/element_stub.py diff --git a/src/classes/guard_struct.py b/guardrails_api/classes/guard_struct.py similarity index 92% rename from src/classes/guard_struct.py rename to guardrails_api/classes/guard_struct.py index ea0dc4e..e59311d 100644 --- a/src/classes/guard_struct.py +++ b/guardrails_api/classes/guard_struct.py @@ -2,10 +2,10 @@ from guardrails import Guard from opentelemetry.trace import Tracer from lxml.etree import tostring -from src.classes.rail_spec_struct import RailSpecStruct -from src.models.guard_item import GuardItem -from src.utils.pluck import pluck -from src.utils.payload_validator import validate_payload +from guardrails_api.classes.rail_spec_struct import RailSpecStruct +from guardrails_api.models.guard_item import GuardItem +from guardrails_api.utils.pluck import pluck +from guardrails_api.utils.payload_validator import validate_payload class GuardStruct: diff --git a/src/classes/health_check.py b/guardrails_api/classes/health_check.py similarity index 100% rename from src/classes/health_check.py rename to guardrails_api/classes/health_check.py diff --git a/src/classes/http_error.py b/guardrails_api/classes/http_error.py similarity index 100% rename from src/classes/http_error.py rename to guardrails_api/classes/http_error.py diff --git a/src/classes/rail_spec_struct.py b/guardrails_api/classes/rail_spec_struct.py similarity index 97% rename from src/classes/rail_spec_struct.py rename to guardrails_api/classes/rail_spec_struct.py index 2658bda..52fb4e8 100644 --- a/src/classes/rail_spec_struct.py +++ b/guardrails_api/classes/rail_spec_struct.py @@ -2,9 +2,9 @@ from lxml.etree import _Element, Element, SubElement from guardrails import Instructions, Prompt, Rail from lxml import etree -from src.classes.schema_struct import SchemaStruct -from src.utils.pluck import pluck -from src.utils.escape_curlys import escape_curlys, descape_curlys +from guardrails_api.classes.schema_struct import SchemaStruct +from guardrails_api.utils.pluck import pluck +from guardrails_api.utils.escape_curlys import escape_curlys, descape_curlys class RailSpecStruct: diff --git a/src/classes/schema_element_struct.py b/guardrails_api/classes/schema_element_struct.py similarity index 98% rename from src/classes/schema_element_struct.py rename to guardrails_api/classes/schema_element_struct.py index 07e9e6f..2c3d91f 100644 --- a/src/classes/schema_element_struct.py +++ b/guardrails_api/classes/schema_element_struct.py @@ -1,7 +1,7 @@ from typing import List, Optional from lxml.etree import _Element -from src.utils.pluck import pluck -from src.classes.element_stub import ElementStub +from guardrails_api.utils.pluck import pluck +from guardrails_api.classes.element_stub import ElementStub class SchemaElementStruct: diff --git a/src/classes/schema_struct.py b/guardrails_api/classes/schema_struct.py similarity index 98% rename from src/classes/schema_struct.py rename to guardrails_api/classes/schema_struct.py index cc2f339..d90296d 100644 --- a/src/classes/schema_struct.py +++ b/guardrails_api/classes/schema_struct.py @@ -1,7 +1,7 @@ from typing import Dict, List, Union from lxml.etree import _Element, _Comment, SubElement from guardrails.schema import Schema, StringSchema, JsonSchema -from src.classes.data_type_struct import DataTypeStruct +from guardrails_api.classes.data_type_struct import DataTypeStruct # TODO: Rather than a custom schema construct like what this is now diff --git a/src/classes/validation_output.py b/guardrails_api/classes/validation_output.py similarity index 98% rename from src/classes/validation_output.py rename to guardrails_api/classes/validation_output.py index 69eda78..22a279d 100644 --- a/src/classes/validation_output.py +++ b/guardrails_api/classes/validation_output.py @@ -3,7 +3,7 @@ from guardrails.classes.history import Call from guardrails.utils.reask_utils import ReAsk -from src.utils.try_json_loads import try_json_loads +from guardrails_api.utils.try_json_loads import try_json_loads class ValidationOutput: diff --git a/src/clients/__init__.py b/guardrails_api/clients/__init__.py similarity index 100% rename from src/clients/__init__.py rename to guardrails_api/clients/__init__.py diff --git a/src/clients/guard_client.py b/guardrails_api/clients/guard_client.py similarity index 94% rename from src/clients/guard_client.py rename to guardrails_api/clients/guard_client.py index 806f2ee..81c1f79 100644 --- a/src/clients/guard_client.py +++ b/guardrails_api/clients/guard_client.py @@ -1,7 +1,7 @@ from typing import List, Union from guardrails import Guard -from src.classes.guard_struct import GuardStruct +from guardrails_api.classes.guard_struct import GuardStruct class GuardClient: diff --git a/src/clients/memory_guard_client.py b/guardrails_api/clients/memory_guard_client.py similarity index 93% rename from src/clients/memory_guard_client.py rename to guardrails_api/clients/memory_guard_client.py index a536b4d..9ecfbf1 100644 --- a/src/clients/memory_guard_client.py +++ b/guardrails_api/clients/memory_guard_client.py @@ -1,8 +1,8 @@ from typing import List from guardrails import Guard -from src.classes.http_error import HttpError -from src.clients.guard_client import GuardClient +from guardrails_api.classes.http_error import HttpError +from guardrails_api.clients.guard_client import GuardClient class MemoryGuardClient(GuardClient): diff --git a/src/clients/pg_guard_client.py b/guardrails_api/clients/pg_guard_client.py similarity index 90% rename from src/clients/pg_guard_client.py rename to guardrails_api/clients/pg_guard_client.py index 9684be1..005c045 100644 --- a/src/clients/pg_guard_client.py +++ b/guardrails_api/clients/pg_guard_client.py @@ -1,10 +1,10 @@ from typing import List -from src.classes.guard_struct import GuardStruct -from src.classes.http_error import HttpError -from src.clients.guard_client import GuardClient -from src.models.guard_item import GuardItem -from src.clients.postgres_client import PostgresClient -from src.models.guard_item_audit import GuardItemAudit +from guardrails_api.classes.guard_struct import GuardStruct +from guardrails_api.classes.http_error import HttpError +from guardrails_api.clients.guard_client import GuardClient +from guardrails_api.models.guard_item import GuardItem +from guardrails_api.clients.postgres_client import PostgresClient +from guardrails_api.models.guard_item_audit import GuardItemAudit class PGGuardClient(GuardClient): diff --git a/src/clients/postgres_client.py b/guardrails_api/clients/postgres_client.py similarity index 92% rename from src/clients/postgres_client.py rename to guardrails_api/clients/postgres_client.py index ed814d7..d7b2ac5 100644 --- a/src/clients/postgres_client.py +++ b/guardrails_api/clients/postgres_client.py @@ -4,7 +4,7 @@ from flask import Flask from sqlalchemy import text from typing import Tuple -from src.models.base import db, INIT_EXTENSIONS +from guardrails_api.models.base import db, INIT_EXTENSIONS def postgres_is_enabled() -> bool: @@ -68,8 +68,8 @@ def initialize(self, app: Flask): self.app = app self.db = db db.init_app(app) - from src.models.guard_item import GuardItem # NOQA - from src.models.guard_item_audit import ( # NOQA + from guardrails_api.models.guard_item import GuardItem # NOQA + from guardrails_api.models.guard_item_audit import ( # NOQA GuardItemAudit, AUDIT_FUNCTION, AUDIT_TRIGGER, diff --git a/src/otel/__init__.py b/guardrails_api/otel/__init__.py similarity index 81% rename from src/otel/__init__.py rename to guardrails_api/otel/__init__.py index e44072b..db47b77 100644 --- a/src/otel/__init__.py +++ b/guardrails_api/otel/__init__.py @@ -1,11 +1,11 @@ import os -from src.otel.logs import logs_are_disabled -from src.otel.metrics import ( +from guardrails_api.otel.logs import logs_are_disabled +from guardrails_api.otel.metrics import ( initialize_metrics_collector, metrics_are_disabled, get_meter, # noqa ) -from src.otel.traces import ( +from guardrails_api.otel.traces import ( traces_are_disabled, initialize_tracer, get_tracer, # noqa diff --git a/src/otel/constants.py b/guardrails_api/otel/constants.py similarity index 100% rename from src/otel/constants.py rename to guardrails_api/otel/constants.py diff --git a/src/otel/logs.py b/guardrails_api/otel/logs.py similarity index 76% rename from src/otel/logs.py rename to guardrails_api/otel/logs.py index 4418b2f..790cc42 100644 --- a/src/otel/logs.py +++ b/guardrails_api/otel/logs.py @@ -1,5 +1,5 @@ import os -from src.otel.constants import none +from guardrails_api.otel.constants import none def logs_are_disabled() -> bool: diff --git a/src/otel/metrics.py b/guardrails_api/otel/metrics.py similarity index 97% rename from src/otel/metrics.py rename to guardrails_api/otel/metrics.py index a009566..8f81e35 100644 --- a/src/otel/metrics.py +++ b/guardrails_api/otel/metrics.py @@ -14,7 +14,7 @@ from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import ( OTLPMetricExporter as GrpcMetricExporter, ) -from src.otel.constants import none +from guardrails_api.otel.constants import none def metrics_are_disabled() -> bool: diff --git a/src/otel/traces.py b/guardrails_api/otel/traces.py similarity index 97% rename from src/otel/traces.py rename to guardrails_api/otel/traces.py index 6257d23..fdbcb8b 100644 --- a/src/otel/traces.py +++ b/guardrails_api/otel/traces.py @@ -16,7 +16,7 @@ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( OTLPSpanExporter as GrpcSpanExporter, ) -from src.otel.constants import none +from guardrails_api.otel.constants import none def traces_are_disabled() -> bool: diff --git a/start.sh b/guardrails_api/start.sh similarity index 100% rename from start.sh rename to guardrails_api/start.sh diff --git a/src/utils/__init__.py b/guardrails_api/utils/__init__.py similarity index 100% rename from src/utils/__init__.py rename to guardrails_api/utils/__init__.py diff --git a/src/utils/escape_curlys.py b/guardrails_api/utils/escape_curlys.py similarity index 100% rename from src/utils/escape_curlys.py rename to guardrails_api/utils/escape_curlys.py diff --git a/src/utils/file.py b/guardrails_api/utils/file.py similarity index 100% rename from src/utils/file.py rename to guardrails_api/utils/file.py diff --git a/src/utils/gather_request_metrics.py b/guardrails_api/utils/gather_request_metrics.py similarity index 100% rename from src/utils/gather_request_metrics.py rename to guardrails_api/utils/gather_request_metrics.py diff --git a/src/utils/get_llm_callable.py b/guardrails_api/utils/get_llm_callable.py similarity index 100% rename from src/utils/get_llm_callable.py rename to guardrails_api/utils/get_llm_callable.py diff --git a/src/utils/handle_error.py b/guardrails_api/utils/handle_error.py similarity index 89% rename from src/utils/handle_error.py rename to guardrails_api/utils/handle_error.py index a47bfb1..403d48f 100644 --- a/src/utils/handle_error.py +++ b/guardrails_api/utils/handle_error.py @@ -1,8 +1,8 @@ from functools import wraps import traceback from werkzeug.exceptions import HTTPException -from src.classes.http_error import HttpError -from src.utils.logger import logger +from guardrails_api.classes.http_error import HttpError +from guardrails_api.utils.logger import logger def handle_error(fn): diff --git a/src/utils/logger.py b/guardrails_api/utils/logger.py similarity index 71% rename from src/utils/logger.py rename to guardrails_api/utils/logger.py index a0cd8a4..b37babd 100644 --- a/src/utils/logger.py +++ b/guardrails_api/utils/logger.py @@ -1,9 +1,6 @@ import os import logging -# Shouldn't need this with auto-instrumentation -# from src.modules.otel_logger import handler as otel_handler - def get_logger(): log_level = os.environ.get("LOGLEVEL", logging.INFO) diff --git a/src/utils/payload_validator.py b/guardrails_api/utils/payload_validator.py similarity index 90% rename from src/utils/payload_validator.py rename to guardrails_api/utils/payload_validator.py index 514af3d..9cff0c3 100644 --- a/src/utils/payload_validator.py +++ b/guardrails_api/utils/payload_validator.py @@ -1,8 +1,8 @@ import json from jsonschema import Draft202012Validator, ValidationError from referencing import Registry, jsonschema as jsonschema_ref -from src.classes.http_error import HttpError -from src.utils.remove_nones import remove_nones +from guardrails_api.classes.http_error import HttpError +from guardrails_api.utils.remove_nones import remove_nones with open("./open-api-spec.json") as api_spec_file: api_spec = json.loads(api_spec_file.read()) diff --git a/src/utils/pip.py b/guardrails_api/utils/pip.py similarity index 95% rename from src/utils/pip.py rename to guardrails_api/utils/pip.py index 08fe0e5..954214d 100644 --- a/src/utils/pip.py +++ b/guardrails_api/utils/pip.py @@ -3,8 +3,8 @@ import pkg_resources as pkg from os import getcwd from typing import Optional, List -from src.utils.logger import logger -from src.utils.file import get_file_contents +from guardrails_api.utils.logger import logger +from guardrails_api.utils.file import get_file_contents def pip_process(action: str, package: str, flags: Optional[List[str]] = []): diff --git a/src/utils/pluck.py b/guardrails_api/utils/pluck.py similarity index 100% rename from src/utils/pluck.py rename to guardrails_api/utils/pluck.py diff --git a/src/utils/prep_environment.py b/guardrails_api/utils/prep_environment.py similarity index 85% rename from src/utils/prep_environment.py rename to guardrails_api/utils/prep_environment.py index 1058e60..899b7e9 100644 --- a/src/utils/prep_environment.py +++ b/guardrails_api/utils/prep_environment.py @@ -1,9 +1,9 @@ import importlib from os import getcwd from typing import List -from src.classes.guard_struct import GuardStruct -from src.utils.pip import install, is_frozen, uninstall, get_module_name -from src.utils.logger import logger +from guardrails_api.classes.guard_struct import GuardStruct +from guardrails_api.utils.pip import install, is_frozen, uninstall, get_module_name +from guardrails_api.utils.logger import logger def dynamic_import(package: str): diff --git a/src/utils/remove_nones.py b/guardrails_api/utils/remove_nones.py similarity index 100% rename from src/utils/remove_nones.py rename to guardrails_api/utils/remove_nones.py diff --git a/src/utils/try_json_loads.py b/guardrails_api/utils/try_json_loads.py similarity index 100% rename from src/utils/try_json_loads.py rename to guardrails_api/utils/try_json_loads.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..51db2b2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +[project] +name = "guardrails-api" +version = "0.0.0" +description = "Guardrails API" +authors = [ + {name = "Guardrails AI", email = "contact@guardrailsai.com"} +] +license = {file = "LICENSE"} +readme = "README.md" +keywords = ["Guardrails", "Guardrails AI", "Guardrails API", "Guardrails API"] +requires-python = ">= 3.8.1" +dependencies = [ + "flask", + "sqlalchemy", + "lxml", + "flask_sqlalchemy", + "werkzeug", + "jsonschema", + "referencing", + "flask_cors", + "numpy", + "faiss-cpu", + "nltk", + "boto3", + "gunicorn", + "psycopg2-binary", + "litellm", + "guardrails-ai@git+https://github.com/guardrails-ai/guardrails.git@async_streaming" +] + +[project.optional-dependencies] +dev = [ + "ruff", + "pytest", + "coverage", + "pytest-mock" +] + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "-rP" +testpaths = [ + "tests" +] + +[tool.pyright] +include = ["guardrails_api"] \ No newline at end of file diff --git a/src/models/base.py b/src/models/base.py deleted file mode 100644 index 29f0169..0000000 --- a/src/models/base.py +++ /dev/null @@ -1,8 +0,0 @@ -from flask_sqlalchemy import SQLAlchemy - -db = SQLAlchemy() - -INIT_EXTENSIONS = """ -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "vector"; -""" diff --git a/src/models/guard_item.py b/src/models/guard_item.py deleted file mode 100644 index 663603f..0000000 --- a/src/models/guard_item.py +++ /dev/null @@ -1,27 +0,0 @@ -from sqlalchemy import Column, String, Integer -from sqlalchemy.dialects.postgresql import JSONB -from src.models.base import db - - -class GuardItem(db.Model): - __tablename__ = "guards" - # TODO: Make primary key a composite between guard.name and the guard owner's userId - name = Column(String, primary_key=True) - railspec = Column(JSONB, nullable=False) - num_reasks = Column(Integer, nullable=True) - description = Column(String, nullable=True) - # owner = Column(String, nullable=False) - - def __init__( - self, - name, - railspec, - num_reasks, - description, - # owner = None - ): - self.name = name - self.railspec = railspec - self.num_reasks = num_reasks - self.description = description - # self.owner = owner diff --git a/src/models/guard_item_audit.py b/src/models/guard_item_audit.py deleted file mode 100644 index 437acef..0000000 --- a/src/models/guard_item_audit.py +++ /dev/null @@ -1,63 +0,0 @@ -from sqlalchemy import Column, String, Integer -from sqlalchemy.dialects.postgresql import JSONB, TIMESTAMP, CHAR -from src.models.base import db - - -class GuardItemAudit(db.Model): - __tablename__ = "guards_audit" - id = Column(String, primary_key=True) - name = Column(String, nullable=False, index=True) - railspec = Column(JSONB, nullable=False) - num_reasks = Column(Integer, nullable=True) - description = Column(String, nullable=True) - # owner = Column(String, nullable=False) - replaced_on = Column(TIMESTAMP, nullable=False) - # replaced_by = Column(String, nullable=False) - operation = Column(CHAR, nullable=False) - - def __init__( - self, - id, - name, - railspec, - num_reasks, - description, - # owner = None - replaced_on, - # replaced_by - operation, - ): - self.id = id - self.name = name - self.railspec = railspec - self.num_reasks = num_reasks - self.description = description - self.replaced_on = replaced_on - self.operation = operation - # self.owner = owner - - -AUDIT_FUNCTION = """ -CREATE OR REPLACE FUNCTION guard_audit_function() RETURNS TRIGGER AS $guard_audit$ -BEGIN - IF (TG_OP = 'DELETE') THEN - INSERT INTO guards_audit SELECT uuid_generate_v4(), OLD.*, now(), 'D'; - ELSIF (TG_OP = 'UPDATE') THEN - INSERT INTO guards_audit SELECT uuid_generate_v4(), OLD.*, now(), 'U'; - ELSIF (TG_OP = 'INSERT') THEN - INSERT INTO guards_audit SELECT uuid_generate_v4(), NEW.*, now(), 'I'; - END IF; - RETURN null; -END; -$guard_audit$ -LANGUAGE plpgsql; -""" - -AUDIT_TRIGGER = """ -DROP TRIGGER IF EXISTS guard_audit_trigger - ON guards; -CREATE TRIGGER guard_audit_trigger - AFTER INSERT OR UPDATE OR DELETE ON guards - FOR EACH ROW - EXECUTE PROCEDURE guard_audit_function(); -""" diff --git a/tests/blueprints/test_guards.py b/tests/blueprints/test_guards.py index 666fd72..f383b0a 100644 --- a/tests/blueprints/test_guards.py +++ b/tests/blueprints/test_guards.py @@ -28,7 +28,7 @@ def around_each(): def test_route_setup(mocker): mocker.patch("flask.Blueprint", new=MockBlueprint) - from src.blueprints.guards import guards_bp + from guardrails_api.blueprints.guards import guards_bp assert guards_bp.route_call_count == 3 assert guards_bp.routes == ["/", "/", "//validate"] @@ -51,7 +51,7 @@ def test_guards__get(mocker): # ) # mocker.patch("src.blueprints.guards.get_tracer") - from src.blueprints.guards import guards + from guardrails_api.blueprints.guards import guards response = guards() @@ -74,7 +74,7 @@ def test_guards__post_pg(mocker): "src.blueprints.guards.guard_client.create_guard", return_value=mock_guard ) - from src.blueprints.guards import guards + from guardrails_api.blueprints.guards import guards response = guards() @@ -93,7 +93,7 @@ def test_guards__post_mem(mocker): mocker.patch("flask.Blueprint", new=MockBlueprint) mocker.patch("src.blueprints.guards.request", mock_request) - from src.blueprints.guards import guards + from guardrails_api.blueprints.guards import guards response = guards() @@ -110,7 +110,7 @@ def test_guards__raises(mocker): # mocker.patch("src.blueprints.guards.get_tracer") mocker.patch("src.utils.handle_error.logger.error") mocker.patch("src.utils.handle_error.traceback.print_exception") - from src.blueprints.guards import guards + from guardrails_api.blueprints.guards import guards response = guards() @@ -145,7 +145,7 @@ def test_guard__get_mem(mocker): # ) # mocker.patch("src.blueprints.guards.get_tracer") - from src.blueprints.guards import guard + from guardrails_api.blueprints.guards import guard response = guard("My%20Guard's%20Name") @@ -178,7 +178,7 @@ def test_guard__put_pg(mocker): # ) # mocker.patch("src.blueprints.guards.get_tracer") - from src.blueprints.guards import guard + from guardrails_api.blueprints.guards import guard response = guard("My%20Guard's%20Name") @@ -207,7 +207,7 @@ def test_guard__delete_pg(mocker): # ) # mocker.patch("src.blueprints.guards.get_tracer") - from src.blueprints.guards import guard + from guardrails_api.blueprints.guards import guard response = guard("my-guard-name") @@ -224,7 +224,7 @@ def test_guard__raises(mocker): # mocker.patch("src.blueprints.guards.get_tracer") mocker.patch("src.utils.handle_error.logger.error") mocker.patch("src.utils.handle_error.traceback.print_exception") - from src.blueprints.guards import guard + from guardrails_api.blueprints.guards import guard response = guard("guard") @@ -248,7 +248,7 @@ def test_validate__raises_method_not_allowed(mocker): # mocker.patch("src.blueprints.guards.get_tracer") mocker.patch("src.utils.handle_error.logger.error") mocker.patch("src.utils.handle_error.traceback.print_exception") - from src.blueprints.guards import validate + from guardrails_api.blueprints.guards import validate response = validate("guard") @@ -279,7 +279,7 @@ def test_validate__raises_bad_request__openai_api_key(mocker): # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) mocker.patch("src.utils.handle_error.logger.error") mocker.patch("src.utils.handle_error.traceback.print_exception") - from src.blueprints.guards import validate + from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") @@ -315,7 +315,7 @@ def test_validate__raises_bad_request__num_reasks(mocker): # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) mocker.patch("src.utils.handle_error.logger.error") mocker.patch("src.utils.handle_error.traceback.print_exception") - from src.blueprints.guards import validate + from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") @@ -370,7 +370,7 @@ def test_validate__parse(mocker): ) mock_status.return_value = "pass" mock_guard.history = Stack(Call()) - from src.blueprints.guards import validate + from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") @@ -460,7 +460,7 @@ def test_validate__call(mocker): ) mock_status.return_value = "fail" mock_guard.history = Stack(Call()) - from src.blueprints.guards import validate + from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") diff --git a/tests/blueprints/test_root.py b/tests/blueprints/test_root.py index 3754748..1a964eb 100644 --- a/tests/blueprints/test_root.py +++ b/tests/blueprints/test_root.py @@ -1,12 +1,12 @@ import os -from src.utils.logger import logger +from guardrails_api.utils.logger import logger from tests.mocks.mock_blueprint import MockBlueprint from tests.mocks.mock_postgres_client import MockPostgresClient def test_home(mocker): mocker.patch("flask.Blueprint", new=MockBlueprint) - from src.blueprints.root import home, root_bp + from guardrails_api.blueprints.root import home, root_bp response = home() @@ -30,7 +30,7 @@ def text_side_effect(query: str): mock_text = mocker.patch("src.blueprints.root.text", side_effect=text_side_effect) - from src.blueprints.root import health_check + from guardrails_api.blueprints.root import health_check info_spy = mocker.spy(logger, "info") diff --git a/tests/clients/test_mem_guard_client.py b/tests/clients/test_mem_guard_client.py index ed2bd77..1dc9c79 100644 --- a/tests/clients/test_mem_guard_client.py +++ b/tests/clients/test_mem_guard_client.py @@ -1,9 +1,8 @@ -# from src.clients.memory_guard_client import MemoryGuardClient from tests.mocks.mock_guard_client import MockGuardStruct def test_init(mocker): - from src.clients.memory_guard_client import MemoryGuardClient + from guardrails_api.clients.memory_guard_client import MemoryGuardClient mem_guard_client = MemoryGuardClient() @@ -12,7 +11,7 @@ def test_init(mocker): class TestGetGuard: def test_get_all(self, mocker): - from src.clients.memory_guard_client import MemoryGuardClient + from guardrails_api.clients.memory_guard_client import MemoryGuardClient guard_client = MemoryGuardClient() @@ -21,7 +20,7 @@ def test_get_all(self, mocker): assert result == [] def test_get_all_after_insert(self, mocker): - from src.clients.memory_guard_client import MemoryGuardClient + from guardrails_api.clients.memory_guard_client import MemoryGuardClient guard_client = MemoryGuardClient() new_guard = MockGuardStruct() @@ -31,7 +30,7 @@ def test_get_all_after_insert(self, mocker): assert result == [new_guard] def test_get_guard_after_insert(self, mocker): - from src.clients.memory_guard_client import MemoryGuardClient + from guardrails_api.clients.memory_guard_client import MemoryGuardClient guard_client = MemoryGuardClient() new_guard = MockGuardStruct("test_guard") @@ -41,7 +40,7 @@ def test_get_guard_after_insert(self, mocker): assert result == new_guard def test_not_found(self, mocker): - from src.clients.memory_guard_client import MemoryGuardClient + from guardrails_api.clients.memory_guard_client import MemoryGuardClient guard_client = MemoryGuardClient() new_guard = MockGuardStruct("test_guard") diff --git a/tests/clients/test_pg_guard_client.py b/tests/clients/test_pg_guard_client.py index d220dd0..56a1072 100644 --- a/tests/clients/test_pg_guard_client.py +++ b/tests/clients/test_pg_guard_client.py @@ -1,10 +1,9 @@ import pytest from unittest.mock import ANY as AnyMatcher -from src.classes.http_error import HttpError +from guardrails_api.classes.http_error import HttpError -# from src.clients.memory_guard_client import MemoryGuardClient -from src.models.guard_item import GuardItem -from src.models.guard_item_audit import GuardItemAudit +from guardrails_api.models.guard_item import GuardItem +from guardrails_api.models.guard_item_audit import GuardItemAudit from tests.mocks.mock_postgres_client import MockPostgresClient from tests.mocks.mock_guard_client import MockGuardStruct, MockRailspec from unittest.mock import call @@ -16,10 +15,9 @@ def test_init(mocker): "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client ) - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient pg_guard_client = PGGuardClient() - # mem_guard_client = MemoryGuardClient() assert pg_guard_client.initialized is True assert isinstance(pg_guard_client.pgClient, MockPostgresClient) @@ -44,7 +42,7 @@ def test_get_latest(self, mocker): ) mock_from_guard_item.return_value = latest_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -77,7 +75,7 @@ def test_with_as_of_date(self, mocker): ) mock_from_guard_item.return_value = previous_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -116,7 +114,7 @@ def test_raises_not_found(self, mocker): "src.clients.pg_guard_client.GuardStruct.from_guard_item" ) - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -144,7 +142,7 @@ def test_get_guard_item(mocker): latest_guard = MockGuardStruct("latest") mock_first.return_value = latest_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -175,7 +173,7 @@ def test_get_guards(mocker): ) mock_from_guard_item.side_effect = [guard_one, guard_two] - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -208,7 +206,7 @@ def test_create_guard(mocker): ) mock_from_guard_item.return_value = mock_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -251,7 +249,7 @@ def test_raises_not_found(self, mocker): "src.clients.pg_guard_client.GuardStruct.from_guard_item" ) - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -287,7 +285,7 @@ def test_updates_guard_item(self, mocker): ) mock_from_guard_item.return_value = updated_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -327,7 +325,7 @@ def test_guard_doesnt_exist_yet(self, mocker): ) mock_create_guard.return_value = new_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -359,7 +357,7 @@ def test_guard_already_exists(self, mocker): ) mock_from_guard_item.return_value = updated_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -393,7 +391,7 @@ def test_raises_not_found(self, mocker): "src.clients.pg_guard_client.GuardStruct.from_guard_item" ) - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() @@ -428,7 +426,7 @@ def test_deletes_guard_item(self, mocker): ) mock_from_guard_item.return_value = old_guard - from src.clients.pg_guard_client import PGGuardClient + from guardrails_api.clients.pg_guard_client import PGGuardClient guard_client = PGGuardClient() diff --git a/tests/mocks/mock_guard_client.py b/tests/mocks/mock_guard_client.py index 214146e..61e385a 100644 --- a/tests/mocks/mock_guard_client.py +++ b/tests/mocks/mock_guard_client.py @@ -1,4 +1,4 @@ -from src.classes.guard_struct import GuardStruct +from guardrails_api.classes.guard_struct import GuardStruct class MockRailspec: diff --git a/tests/utils/test_pluck.py b/tests/utils/test_pluck.py index c757bf2..448ff2a 100644 --- a/tests/utils/test_pluck.py +++ b/tests/utils/test_pluck.py @@ -1,4 +1,4 @@ -from src.utils.pluck import pluck +from guardrails_api.utils.pluck import pluck def test_pluck(): diff --git a/tests/utils/test_remove_nones.py b/tests/utils/test_remove_nones.py index 9d5fb5b..1345230 100644 --- a/tests/utils/test_remove_nones.py +++ b/tests/utils/test_remove_nones.py @@ -1,4 +1,4 @@ -from src.utils.remove_nones import remove_nones +from guardrails_api.utils.remove_nones import remove_nones def test_remove_nones(): diff --git a/wsgi.py b/wsgi.py deleted file mode 100644 index 6eef787..0000000 --- a/wsgi.py +++ /dev/null @@ -1,8 +0,0 @@ -from app import create_app -import os - -if __name__ == "__main__": - stage = os.environ.get("STAGE", "local") - the_port = 8000 - app = create_app() - app.run(host="0.0.0.0", port=the_port) From 66eb2b6158762da19e1dfcfc69f91286481c1c3a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 5 Jun 2024 16:28:16 -0500 Subject: [PATCH 05/12] unignore models, install specific otel libraries we need --- .gitignore | 5 +- guardrails_api/__init__.py | 1 + guardrails_api/default.env | 7 +++ guardrails_api/models/base.py | 8 +++ guardrails_api/models/guard_item.py | 27 ++++++++++ guardrails_api/models/guard_item_audit.py | 63 +++++++++++++++++++++++ guardrails_api/py.typed | 0 requirements.txt | 13 +++-- 8 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 guardrails_api/default.env create mode 100644 guardrails_api/models/base.py create mode 100644 guardrails_api/models/guard_item.py create mode 100644 guardrails_api/models/guard_item_audit.py create mode 100644 guardrails_api/py.typed diff --git a/.gitignore b/.gitignore index 0853ece..8377660 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,6 @@ open-api-spec.json open-api-spec.yml .python-version requirements-lock-old.txt -models -opensearch \ No newline at end of file +opensearch +build +*.egg-info \ No newline at end of file diff --git a/guardrails_api/__init__.py b/guardrails_api/__init__.py index e69de29..3aa0d7b 100644 --- a/guardrails_api/__init__.py +++ b/guardrails_api/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.0" \ No newline at end of file diff --git a/guardrails_api/default.env b/guardrails_api/default.env new file mode 100644 index 0000000..674906a --- /dev/null +++ b/guardrails_api/default.env @@ -0,0 +1,7 @@ +APP_ENVIRONMENT=local +PYTHONUNBUFFERED=1 +LOGLEVEL="INFO" +GUARDRAILS_LOG_LEVEL="INFO" +GUARDRAILS_PROCESS_COUNT=1 +SELF_ENDPOINT=http://localhost:8000 +OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES \ No newline at end of file diff --git a/guardrails_api/models/base.py b/guardrails_api/models/base.py new file mode 100644 index 0000000..29f0169 --- /dev/null +++ b/guardrails_api/models/base.py @@ -0,0 +1,8 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + +INIT_EXTENSIONS = """ +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "vector"; +""" diff --git a/guardrails_api/models/guard_item.py b/guardrails_api/models/guard_item.py new file mode 100644 index 0000000..a2dbf32 --- /dev/null +++ b/guardrails_api/models/guard_item.py @@ -0,0 +1,27 @@ +from sqlalchemy import Column, String, Integer +from sqlalchemy.dialects.postgresql import JSONB +from guardrails_api.models.base import db + + +class GuardItem(db.Model): + __tablename__ = "guards" + # TODO: Make primary key a composite between guard.name and the guard owner's userId + name = Column(String, primary_key=True) + railspec = Column(JSONB, nullable=False) + num_reasks = Column(Integer, nullable=True) + description = Column(String, nullable=True) + # owner = Column(String, nullable=False) + + def __init__( + self, + name, + railspec, + num_reasks, + description, + # owner = None + ): + self.name = name + self.railspec = railspec + self.num_reasks = num_reasks + self.description = description + # self.owner = owner diff --git a/guardrails_api/models/guard_item_audit.py b/guardrails_api/models/guard_item_audit.py new file mode 100644 index 0000000..183626e --- /dev/null +++ b/guardrails_api/models/guard_item_audit.py @@ -0,0 +1,63 @@ +from sqlalchemy import Column, String, Integer +from sqlalchemy.dialects.postgresql import JSONB, TIMESTAMP, CHAR +from guardrails_api.models.base import db + + +class GuardItemAudit(db.Model): + __tablename__ = "guards_audit" + id = Column(String, primary_key=True) + name = Column(String, nullable=False, index=True) + railspec = Column(JSONB, nullable=False) + num_reasks = Column(Integer, nullable=True) + description = Column(String, nullable=True) + # owner = Column(String, nullable=False) + replaced_on = Column(TIMESTAMP, nullable=False) + # replaced_by = Column(String, nullable=False) + operation = Column(CHAR, nullable=False) + + def __init__( + self, + id, + name, + railspec, + num_reasks, + description, + # owner = None + replaced_on, + # replaced_by + operation, + ): + self.id = id + self.name = name + self.railspec = railspec + self.num_reasks = num_reasks + self.description = description + self.replaced_on = replaced_on + self.operation = operation + # self.owner = owner + + +AUDIT_FUNCTION = """ +CREATE OR REPLACE FUNCTION guard_audit_function() RETURNS TRIGGER AS $guard_audit$ +BEGIN + IF (TG_OP = 'DELETE') THEN + INSERT INTO guards_audit SELECT uuid_generate_v4(), OLD.*, now(), 'D'; + ELSIF (TG_OP = 'UPDATE') THEN + INSERT INTO guards_audit SELECT uuid_generate_v4(), OLD.*, now(), 'U'; + ELSIF (TG_OP = 'INSERT') THEN + INSERT INTO guards_audit SELECT uuid_generate_v4(), NEW.*, now(), 'I'; + END IF; + RETURN null; +END; +$guard_audit$ +LANGUAGE plpgsql; +""" + +AUDIT_TRIGGER = """ +DROP TRIGGER IF EXISTS guard_audit_trigger + ON guards; +CREATE TRIGGER guard_audit_trigger + AFTER INSERT OR UPDATE OR DELETE ON guards + FOR EACH ROW + EXECUTE PROCEDURE guard_audit_function(); +""" diff --git a/guardrails_api/py.typed b/guardrails_api/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 3eb72dd..5d20715 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,10 @@ -flask -sqlalchemy -lxml guardrails-ai @ git+https://github.com/guardrails-ai/guardrails.git@async_streaming +flask # Let this come from guardrails-ai as a transient dependency. # Pip confuses tag versions with commit ids, # and claims a conflict even though it's the same thing. # guard-rails-api-client @ git+https://github.com/guardrails-ai/guardrails-api-client.git@v0.0.2#egg=guard-rails-api-client&subdirectory=guard-rails-api-client -flask_sqlalchemy +Flask-SQLAlchemy werkzeug jsonschema referencing @@ -17,4 +15,9 @@ nltk boto3 gunicorn psycopg2-binary -litellm \ No newline at end of file +litellm +opentelemetry-api>=1.0.0,<2 +opentelemetry-sdk>=1.0.0,<2 +opentelemetry-exporter-otlp-proto-grpc>=1.0.0,<2 +opentelemetry-exporter-otlp-proto-http>=1.0.0,<2 +opentelemetry-instrumentation-flask>=0.12b0,<1 \ No newline at end of file From fe4e4c6b85aebb82e73562776fd6eeaec2708e4b Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 6 Jun 2024 17:16:45 -0500 Subject: [PATCH 06/12] initial cli plus some cleanup --- MANIFEST.in | 2 + Makefile | 27 ++-- default.env | 7 - guardrails_api/__init__.py | 2 +- guardrails_api/app.py | 25 ++- guardrails_api/blueprints/guards.py | 11 +- guardrails_api/blueprints/root.py | 9 +- guardrails_api/cli/__init__.py | 24 +++ guardrails_api/cli/cli.py | 4 + guardrails_api/cli/start.py | 50 ++++++ config.py => guardrails_api/default_config.py | 8 +- guardrails_api/models/__init__.py | 0 guardrails_api/open_api_spec.py | 17 ++ guardrails_api/start.sh | 2 +- guardrails_api/utils/payload_validator.py | 6 +- guardrails_api/utils/pip.py | 79 ---------- guardrails_api/utils/prep_environment.py | 40 ----- guardrails_api/utils/try_json_loads.py | 4 +- pyproject.toml | 44 ++++-- requirements.txt | 2 +- setup.cfg | 2 + setup.py | 38 +++++ tests/blueprints/test_guards.py | 145 +++++++++--------- tests/blueprints/test_root.py | 6 +- tests/cli/test_init.py | 27 ++++ tests/cli/test_start.py | 23 +++ tests/clients/test_pg_guard_client.py | 79 ++++++---- tests/utils/test_try_json_loads.py | 17 ++ 28 files changed, 407 insertions(+), 293 deletions(-) create mode 100644 MANIFEST.in delete mode 100644 default.env create mode 100644 guardrails_api/cli/__init__.py create mode 100644 guardrails_api/cli/cli.py create mode 100644 guardrails_api/cli/start.py rename config.py => guardrails_api/default_config.py (71%) create mode 100644 guardrails_api/models/__init__.py create mode 100644 guardrails_api/open_api_spec.py delete mode 100644 guardrails_api/utils/pip.py delete mode 100644 guardrails_api/utils/prep_environment.py create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tests/cli/test_init.py create mode 100644 tests/cli/test_start.py create mode 100644 tests/utils/test_try_json_loads.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e1ada05 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include guardrails_api/* +exclude */__pycache__/* \ No newline at end of file diff --git a/Makefile b/Makefile index 60c6bae..ee9cf60 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,6 @@ # Installs production dependencies install: pip install -r requirements.txt; - # This is a workaround because of this issue: https://github.com/open-telemetry/opentelemetry-python-contrib/issues/2053 - pip uninstall aiohttp -y - pip install opentelemetry-distro - opentelemetry-bootstrap -a install - pip install aiohttp # Installs development dependencies install-dev: @@ -18,8 +13,13 @@ lock: install-lock: pip install -r requirements-lock.txt +.PHONY: build +build: + curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml + npx @redocly/cli bundle --dereferenced --output ./guardrails_api/open-api-spec.json --ext json ./open-api-spec.yml + start: - bash start.sh + bash ./guardrails_api/start.sh infra: docker compose --profile infra up --build @@ -37,15 +37,16 @@ refresh: format: - ruff check app.py src/ tests/ --fix - ruff format app.py src/ tests/ + ruff check guardrails_api/ tests/ --fix + ruff format guardrails_api/ tests/ lint: - ruff check app.py src/ tests/ - ruff format app.py src/ tests/ + ruff check guardrails_api/ tests/ + ruff format guardrails_api/ tests/ qa: + make build make lint make test-cov @@ -57,10 +58,10 @@ test: pytest ./tests test-cov: - coverage run --source=./src -m pytest ./tests - coverage report --fail-under=50 + coverage run --source=./guardrails_api -m pytest ./tests + coverage report --fail-under=45 view-test-cov: - coverage run --source=./src -m pytest ./tests + coverage run --source=./guardrails_api -m pytest ./tests coverage html open htmlcov/index.html \ No newline at end of file diff --git a/default.env b/default.env deleted file mode 100644 index 674906a..0000000 --- a/default.env +++ /dev/null @@ -1,7 +0,0 @@ -APP_ENVIRONMENT=local -PYTHONUNBUFFERED=1 -LOGLEVEL="INFO" -GUARDRAILS_LOG_LEVEL="INFO" -GUARDRAILS_PROCESS_COUNT=1 -SELF_ENDPOINT=http://localhost:8000 -OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES \ No newline at end of file diff --git a/guardrails_api/__init__.py b/guardrails_api/__init__.py index 3aa0d7b..6c8e6b9 100644 --- a/guardrails_api/__init__.py +++ b/guardrails_api/__init__.py @@ -1 +1 @@ -__version__ = "0.0.0" \ No newline at end of file +__version__ = "0.0.0" diff --git a/guardrails_api/app.py b/guardrails_api/app.py index 4e16246..dbd505f 100644 --- a/guardrails_api/app.py +++ b/guardrails_api/app.py @@ -1,4 +1,5 @@ import os +from typing import Optional from flask import Flask from flask_cors import CORS from werkzeug.middleware.proxy_fix import ProxyFix @@ -20,12 +21,30 @@ def __call__(self, environ, start_response): return self.app(environ, start_response) -def create_app(): +def register_config(config: Optional[str] = None): + default_config_file = os.path.join(os.path.dirname(__file__), "default_config.py") + + config_file = config or default_config_file + config_file_path = os.path.abspath(config_file) + if os.path.isfile(config_file_path): + from importlib.machinery import SourceFileLoader + + # This creates a module named "validators" with the contents of the init file + # This allow statements like `from validators import StartsWith` + # But more importantly, it registers all of the validators imported in the init + SourceFileLoader("config", config_file_path).load_module() + + +def create_app(env: Optional[str] = None, config: Optional[str] = None): if os.environ.get("APP_ENVIRONMENT") != "production": from dotenv import load_dotenv - # TODO: Accept this as an env var - load_dotenv() + default_env_file = os.path.join(os.path.dirname(__file__), "default.env") + env_file = env or default_env_file + env_file_path = os.path.abspath(env_file) + load_dotenv(env_file_path) + + register_config(config) app = Flask(__name__) diff --git a/guardrails_api/blueprints/guards.py b/guardrails_api/blueprints/guards.py index bc1e195..3bf738c 100644 --- a/guardrails_api/blueprints/guards.py +++ b/guardrails_api/blueprints/guards.py @@ -16,7 +16,6 @@ from guardrails_api.clients.postgres_client import postgres_is_enabled from guardrails_api.utils.handle_error import handle_error from guardrails_api.utils.get_llm_callable import get_llm_callable -from guardrails_api.utils.prep_environment import cleanup_environment, prep_environment guards_bp = Blueprint("guards", __name__, url_prefix="/guards") @@ -27,9 +26,8 @@ guard_client = PGGuardClient() else: guard_client = MemoryGuardClient() - # TODO: Accept file path as env var and dynamically import - # read in guards from file - import config + # Will be defined at runtime + import config # noqa exports = config.__dir__() for export_name in exports: @@ -180,9 +178,6 @@ def validate(guard_name: str): ) decoded_guard_name = unquote_plus(guard_name) guard_struct = guard_client.get_guard(decoded_guard_name) - if isinstance(guard_struct, GuardStruct): - # TODO: is there a way to do this with Guard? - prep_environment(guard_struct) llm_output = payload.pop("llmOutput", None) num_reasks = payload.pop("numReasks", guard_struct.num_reasks) @@ -327,6 +322,4 @@ def validate_streamer(guard_iter): # prompt_params=prompt_params, # result=result # ) - if isinstance(guard_struct, GuardStruct): - cleanup_environment(guard_struct) return validation_output.to_response() diff --git a/guardrails_api/blueprints/root.py b/guardrails_api/blueprints/root.py index 78ff7f7..7315901 100644 --- a/guardrails_api/blueprints/root.py +++ b/guardrails_api/blueprints/root.py @@ -3,6 +3,7 @@ import flask from string import Template from flask import Blueprint +from guardrails_api.open_api_spec import get_open_api_spec from sqlalchemy import text from guardrails_api.classes.health_check import HealthCheck from guardrails_api.clients.postgres_client import PostgresClient, postgres_is_enabled @@ -11,7 +12,6 @@ root_bp = Blueprint("root", __name__, url_prefix="/") -cached_api_spec = None @root_bp.route("/") @@ -42,11 +42,8 @@ def health_check(): @root_bp.route("/api-docs") @handle_error def api_docs(): - global cached_api_spec - if not cached_api_spec: - with open("./open-api-spec.json") as api_spec_file: - cached_api_spec = json.loads(api_spec_file.read()) - return json.dumps(cached_api_spec) + api_spec = get_open_api_spec() + return json.dumps(api_spec) @root_bp.route("/docs") diff --git a/guardrails_api/cli/__init__.py b/guardrails_api/cli/__init__.py new file mode 100644 index 0000000..1980bf6 --- /dev/null +++ b/guardrails_api/cli/__init__.py @@ -0,0 +1,24 @@ +import typer +import guardrails_api.cli.start # noqa +from typing import Optional +from guardrails_api import __version__ +from guardrails_api.cli.cli import cli + + +def version_callback(value: bool): + if value: + print(f"guardrails-api CLI Version: {__version__}") + raise typer.Exit() + + +@cli.callback() +def main( + version: Optional[bool] = typer.Option( + None, "--version", callback=version_callback, is_eager=True + ), +): + pass + + +if __name__ == "__main__": + cli() diff --git a/guardrails_api/cli/cli.py b/guardrails_api/cli/cli.py new file mode 100644 index 0000000..25fb0ad --- /dev/null +++ b/guardrails_api/cli/cli.py @@ -0,0 +1,4 @@ +import typer + + +cli = typer.Typer() diff --git a/guardrails_api/cli/start.py b/guardrails_api/cli/start.py new file mode 100644 index 0000000..d686621 --- /dev/null +++ b/guardrails_api/cli/start.py @@ -0,0 +1,50 @@ +import typer +from typing import Optional +from gunicorn.app.base import BaseApplication +from guardrails_api.cli.cli import cli +from guardrails_api.app import create_app + + +class StandaloneApplication(BaseApplication): + def __init__(self, app, options=None): + self.options = options or {} + self.application = app + super().__init__() + + def load_config(self): + config = { + key: value + for key, value in self.options.items() + if key in self.cfg.settings and value is not None + } + for key, value in config.items(): + self.cfg.set(key.lower(), value) + + def load(self): + return self.application + + +@cli.command("start") +def start( + env: Optional[str] = typer.Option( + default="", + help="An env file to load environment variables from.", + prompt=".env file (optional)", + ), + config: Optional[str] = typer.Option( + default="", + help="A config file to load Guards from.", + prompt="config file (optional)", + ), +): + # TODO: If these are empty, + # look for them in a .guardrailsrc in the current directory. + env = env or None + config = config or None + + options = { + "bind": "0.0.0.0:8000", + "timeout": 5, + "threads": 10, + } + StandaloneApplication(create_app(env, config), options).run() diff --git a/config.py b/guardrails_api/default_config.py similarity index 71% rename from config.py rename to guardrails_api/default_config.py index 5f67b8e..eab62ad 100644 --- a/config.py +++ b/guardrails_api/default_config.py @@ -1,11 +1,11 @@ -''' +""" All guards defined here will be initialized, if and only if the application is using in memory guards. The application will use in memory guards if pg_host is left -undefined. Otherwise, a postgres instance will be started +undefined. Otherwise, a postgres instance will be started and guards will be persisted into postgres. In that case, these guards will not be initialized. -''' +""" -from guardrails import Guard \ No newline at end of file +from guardrails import Guard # noqa diff --git a/guardrails_api/models/__init__.py b/guardrails_api/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/guardrails_api/open_api_spec.py b/guardrails_api/open_api_spec.py new file mode 100644 index 0000000..45d33bd --- /dev/null +++ b/guardrails_api/open_api_spec.py @@ -0,0 +1,17 @@ +import json +import os + +open_api_spec = None + + +def get_open_api_spec(): + global open_api_spec + if not open_api_spec: + api_spec_file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), "open-api-spec.json") + ) + + with open(api_spec_file_path) as api_spec_file: + open_api_spec = json.loads(api_spec_file.read()) + + return open_api_spec diff --git a/guardrails_api/start.sh b/guardrails_api/start.sh index 0b6fa25..2178033 100755 --- a/guardrails_api/start.sh +++ b/guardrails_api/start.sh @@ -10,4 +10,4 @@ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml -gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" +gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "guardrails_api.app:create_app()" diff --git a/guardrails_api/utils/payload_validator.py b/guardrails_api/utils/payload_validator.py index 9cff0c3..32556c1 100644 --- a/guardrails_api/utils/payload_validator.py +++ b/guardrails_api/utils/payload_validator.py @@ -1,12 +1,10 @@ -import json from jsonschema import Draft202012Validator, ValidationError from referencing import Registry, jsonschema as jsonschema_ref +from guardrails_api.open_api_spec import get_open_api_spec from guardrails_api.classes.http_error import HttpError from guardrails_api.utils.remove_nones import remove_nones -with open("./open-api-spec.json") as api_spec_file: - api_spec = json.loads(api_spec_file.read()) - +api_spec = get_open_api_spec() registry = Registry().with_resources( [ ( diff --git a/guardrails_api/utils/pip.py b/guardrails_api/utils/pip.py deleted file mode 100644 index 954214d..0000000 --- a/guardrails_api/utils/pip.py +++ /dev/null @@ -1,79 +0,0 @@ -import sys -import subprocess -import pkg_resources as pkg -from os import getcwd -from typing import Optional, List -from guardrails_api.utils.logger import logger -from guardrails_api.utils.file import get_file_contents - - -def pip_process(action: str, package: str, flags: Optional[List[str]] = []): - try: - logger.debug(f"running pip {action} {' '.join(flags)} {package}") - command = [sys.executable, "-m", "pip", action] - command.extend(flags) - command.append(package) - output = subprocess.check_output(command) - logger.debug(f"decoding output from pip {action} {package}") - return str(output.decode()) - except subprocess.CalledProcessError as exc: - logger.error( - f"Failed to {action} {package}", - f"Exit code: {exc.returncode}", - f"stdout: {exc.output}", - ) - except Exception as e: - logger.error( - f"An unexpected exception occurred while try to {action} {package}!", - e, - ) - - -def install(package): - install_output = pip_process("install", package) - logger.debug(install_output) - - -def uninstall(package): - uninstall_output = pip_process("uninstall", package, ["-y"]) - logger.debug(uninstall_output) - - -def show(package): - show_output = pip_process("show", package) - logger.debug(show_output) - - -def is_installed(package): - show_output = pip_process("show", package) - if show_output is None: - return False - else: - return True - - -# Checks if a package is containd in the requirements.txt -def is_frozen(package): - requirements_txt_path = f"{getcwd()}/requirements.txt" - requirements = open(requirements_txt_path, "r") - package_is_frozen = False - for line in requirements: - module_name = line.split("==").pop(0) - module_name = module_name.split(" @ ").pop(0) - if module_name == package: - package_is_frozen = True - break - logger.debug(f"{package} is frozen? {package_is_frozen}") - return package_is_frozen - - -def get_module_name(package_name: str): - module_name = package_name.replace("-", "_") # a really bad fallback - dist = pkg.get_distribution(package_name) - if dist is not None: - metadata_dir = dist.egg_info - file_path = f"{metadata_dir}/top_level.txt" - file = get_file_contents(file_path) - if file is not None: - module_name = file.read().rstrip().split("\n").pop(0) - return module_name diff --git a/guardrails_api/utils/prep_environment.py b/guardrails_api/utils/prep_environment.py deleted file mode 100644 index 899b7e9..0000000 --- a/guardrails_api/utils/prep_environment.py +++ /dev/null @@ -1,40 +0,0 @@ -import importlib -from os import getcwd -from typing import List -from guardrails_api.classes.guard_struct import GuardStruct -from guardrails_api.utils.pip import install, is_frozen, uninstall, get_module_name -from guardrails_api.utils.logger import logger - - -def dynamic_import(package: str): - # We can't try to retrive the module name until the package is installed. - # If it is already installed this will go quickly. - package_path = ( - f"{getcwd()}/{package}" - if package == "guardrails-custom-validators" - else package - ) - inst_out = install(package_path) - logger.debug(inst_out) - module_name = get_module_name(package) - try: - mod = importlib.import_module(module_name, package) - return mod - except Exception as e: - logger.error(f"Failed to import {module_name}!", e) - - -def prep_environment(guard: GuardStruct): - plugins: List[str] = guard.railspec.get_all_plugins() - for p in plugins: - dynamic_import(p) - - -def cleanup_environment(guard: GuardStruct): - plugins: List[str] = guard.railspec.get_all_plugins() - for p in plugins: - # Only uninstall packages that we did not already have installed. - logger.debug(f"checking if {p} is in requirements.txt") - if is_frozen(p) is not True: - logger.debug(f"uninstalling {p}") - uninstall(p) diff --git a/guardrails_api/utils/try_json_loads.py b/guardrails_api/utils/try_json_loads.py index ad50ffb..ba5d720 100644 --- a/guardrails_api/utils/try_json_loads.py +++ b/guardrails_api/utils/try_json_loads.py @@ -3,7 +3,7 @@ def try_json_loads(val): try: - string_val = json.loads(val, default=str) - return string_val + json_val = json.loads(val) + return json_val except Exception: return val diff --git a/pyproject.toml b/pyproject.toml index 51db2b2..5ed5c20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "guardrails-api" -version = "0.0.0" +dynamic = ["version"] description = "Guardrails API" authors = [ {name = "Guardrails AI", email = "contact@guardrailsai.com"} @@ -10,24 +10,31 @@ readme = "README.md" keywords = ["Guardrails", "Guardrails AI", "Guardrails API", "Guardrails API"] requires-python = ">= 3.8.1" dependencies = [ - "flask", - "sqlalchemy", - "lxml", - "flask_sqlalchemy", - "werkzeug", - "jsonschema", - "referencing", - "flask_cors", - "numpy", - "faiss-cpu", - "nltk", - "boto3", - "gunicorn", - "psycopg2-binary", - "litellm", - "guardrails-ai@git+https://github.com/guardrails-ai/guardrails.git@async_streaming" + "guardrails-ai@git+https://github.com/guardrails-ai/guardrails.git@0.4.5-dev", + "flask>=3.0.3,<4", + "Flask-SQLAlchemy>=3.1.1,<4", + "Werkzeug>=3.0.3,<4", + "jsonschema>=4.22.0,<5", + "referencing>=0.35.1,<1", + "Flask-Cors>=4.0.1,<5", + "boto3>=1.34.115,<2", + "gunicorn>=22.0.0,<23", + "psycopg2-binary>=2.9.9,<3", + "litellm>=1.39.3,<2", + "typer>=0.9.4,<1", + "opentelemetry-api>=1.0.0,<2", + "opentelemetry-sdk>=1.0.0,<2", + "opentelemetry-exporter-otlp-proto-grpc>=1.0.0,<2", + "opentelemetry-exporter-otlp-proto-http>=1.0.0,<2", + "opentelemetry-instrumentation-flask>=0.12b0,<1", ] +[tool.setuptools.dynamic] +version = {attr = "guardrails_api.__version__"} + +[project.scripts] +guardrails-api = "guardrails_api.cli:cli" + [project.optional-dependencies] dev = [ "ruff", @@ -42,6 +49,9 @@ addopts = "-rP" testpaths = [ "tests" ] +pythonpath = [ + ".", "guardrails_api" +] [tool.pyright] include = ["guardrails_api"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5d20715..c4ccfea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -guardrails-ai @ git+https://github.com/guardrails-ai/guardrails.git@async_streaming +guardrails-ai @ git+https://github.com/guardrails-ai/guardrails.git@0.4.5-dev flask # Let this come from guardrails-ai as a transient dependency. # Pip confuses tag versions with commit ids, diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..77f1ae2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +version = attr: guardrails_api.__version__ \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0c11870 --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +import codecs +import os +import pathlib + +from setuptools import find_packages, setup + +here = pathlib.Path(__file__).parent.resolve() +long_description = (here / "README.md").read_text(encoding="utf-8") + + +setup( + name="guardrails-api", + description="Guardrails API", + long_description=long_description, + long_description_content_type="text/markdown", + packages=find_packages(), + python_requires=">=3.8, <4", + install_requires=[ + "guardrails-ai@git+https://github.com/guardrails-ai/guardrails.git@0.4.5-dev", + "flask>=3.0.3,<4", + "Flask-SQLAlchemy>=3.1.1,<4", + "Werkzeug>=3.0.3,<4", + "jsonschema>=4.22.0,<5", + "referencing>=0.35.1,<1", + "Flask-Cors>=4.0.1,<5", + "boto3>=1.34.115,<2", + "gunicorn>=22.0.0,<23", + "psycopg2-binary>=2.9.9,<3", + "litellm>=1.39.3,<2", + "typer>=0.9.4,<1", + "opentelemetry-api>1,<2", + "opentelemetry-exporter-otlp-proto-grpc>1,<2", + "opentelemetry-exporter-otlp-proto-http>1,<2", + "opentelemetry-instrumentation-flask>=0.12b0,<1" + ], + package_data={"guardrails_api": ["py.typed"]}, +) + \ No newline at end of file diff --git a/tests/blueprints/test_guards.py b/tests/blueprints/test_guards.py index f383b0a..6957bef 100644 --- a/tests/blueprints/test_guards.py +++ b/tests/blueprints/test_guards.py @@ -10,7 +10,11 @@ from guardrails.classes import ValidationOutcome from guardrails.classes.generic import Stack from guardrails.classes.history import Call -# from tests.mocks.mock_trace import MockTracer +from guardrails_api.app import register_config + +# TODO: Should we mock this somehow? +# Right now it's just empty, but it technically does a file read +register_config() @pytest.fixture(autouse=True) @@ -39,17 +43,18 @@ def test_guards__get(mocker): mock_request = MockRequest("GET") mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guards = mocker.patch( - "src.blueprints.guards.guard_client.get_guards", return_value=[mock_guard] + "guardrails_api.blueprints.guards.guard_client.get_guards", + return_value=[mock_guard], ) - mocker.patch("src.blueprints.guards.collect_telemetry") + mocker.patch("guardrails_api.blueprints.guards.collect_telemetry") # >>> Conflict # mock_get_guards = mocker.patch( - # "src.blueprints.guards.guard_client.get_guards", return_value=[mock_guard] + # "guardrails_api.blueprints.guards.guard_client.get_guards", return_value=[mock_guard] # ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") from guardrails_api.blueprints.guards import guards @@ -66,12 +71,14 @@ def test_guards__post_pg(mocker): mock_request = MockRequest("POST", mock_guard.to_response()) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_from_request = mocker.patch( - "src.blueprints.guards.GuardStruct.from_request", return_value=mock_guard + "guardrails_api.blueprints.guards.GuardStruct.from_request", + return_value=mock_guard, ) mock_create_guard = mocker.patch( - "src.blueprints.guards.guard_client.create_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.create_guard", + return_value=mock_guard, ) from guardrails_api.blueprints.guards import guards @@ -91,7 +98,7 @@ def test_guards__post_mem(mocker): mock_request = MockRequest("POST", mock_guard.to_response()) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) from guardrails_api.blueprints.guards import guards @@ -106,10 +113,10 @@ def test_guards__raises(mocker): mock_request = MockRequest("PUT") mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) - # mocker.patch("src.blueprints.guards.get_tracer") - mocker.patch("src.utils.handle_error.logger.error") - mocker.patch("src.utils.handle_error.traceback.print_exception") + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") + mocker.patch("guardrails_api.utils.handle_error.logger.error") + mocker.patch("guardrails_api.utils.handle_error.traceback.print_exception") from guardrails_api.blueprints.guards import guards response = guards() @@ -132,18 +139,19 @@ def test_guard__get_mem(mocker): mock_request = MockRequest("GET", args={"asOf": timestamp}) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guard = mocker.patch( - "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.get_guard", + return_value=mock_guard, ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") # >>> Conflict # mock_get_guard = mocker.patch( - # "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + # "guardrails_api.blueprints.guards.guard_client.get_guard", return_value=mock_guard # ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") from guardrails_api.blueprints.guards import guard @@ -159,24 +167,26 @@ def test_guard__put_pg(mocker): mock_request = MockRequest("PUT", json={"name": "mock-guard"}) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_from_request = mocker.patch( - "src.blueprints.guards.GuardStruct.from_request", return_value=mock_guard + "guardrails_api.blueprints.guards.GuardStruct.from_request", + return_value=mock_guard, ) mock_upsert_guard = mocker.patch( - "src.blueprints.guards.guard_client.upsert_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.upsert_guard", + return_value=mock_guard, ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") # >>> Conflict # mock_from_request = mocker.patch( - # "src.blueprints.guards.GuardStruct.from_request", return_value=mock_guard + # "guardrails_api.blueprints.guards.GuardStruct.from_request", return_value=mock_guard # ) # mock_upsert_guard = mocker.patch( - # "src.blueprints.guards.guard_client.upsert_guard", return_value=mock_guard + # "guardrails_api.blueprints.guards.guard_client.upsert_guard", return_value=mock_guard # ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") from guardrails_api.blueprints.guards import guard @@ -194,18 +204,19 @@ def test_guard__delete_pg(mocker): mock_request = MockRequest("DELETE") mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_delete_guard = mocker.patch( - "src.blueprints.guards.guard_client.delete_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.delete_guard", + return_value=mock_guard, ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") # >>> Conflict # mock_delete_guard = mocker.patch( - # "src.blueprints.guards.guard_client.delete_guard", return_value=mock_guard + # "guardrails_api.blueprints.guards.guard_client.delete_guard", return_value=mock_guard # ) - # mocker.patch("src.blueprints.guards.get_tracer") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") from guardrails_api.blueprints.guards import guard @@ -220,10 +231,10 @@ def test_guard__raises(mocker): mock_request = MockRequest("POST") mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) - # mocker.patch("src.blueprints.guards.get_tracer") - mocker.patch("src.utils.handle_error.logger.error") - mocker.patch("src.utils.handle_error.traceback.print_exception") + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") + mocker.patch("guardrails_api.utils.handle_error.logger.error") + mocker.patch("guardrails_api.utils.handle_error.traceback.print_exception") from guardrails_api.blueprints.guards import guard response = guard("guard") @@ -244,10 +255,10 @@ def test_validate__raises_method_not_allowed(mocker): mock_request = MockRequest("PUT") mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) - # mocker.patch("src.blueprints.guards.get_tracer") - mocker.patch("src.utils.handle_error.logger.error") - mocker.patch("src.utils.handle_error.traceback.print_exception") + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer") + mocker.patch("guardrails_api.utils.handle_error.logger.error") + mocker.patch("guardrails_api.utils.handle_error.traceback.print_exception") from guardrails_api.blueprints.guards import validate response = validate("guard") @@ -271,19 +282,18 @@ def test_validate__raises_bad_request__openai_api_key(mocker): mock_request = MockRequest("POST", json={"llmApi": "bar"}) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guard = mocker.patch( - "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.get_guard", + return_value=mock_guard, ) - mock_prep_environment = mocker.patch("src.blueprints.guards.prep_environment") - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) - mocker.patch("src.utils.handle_error.logger.error") - mocker.patch("src.utils.handle_error.traceback.print_exception") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) + mocker.patch("guardrails_api.utils.handle_error.logger.error") + mocker.patch("guardrails_api.utils.handle_error.traceback.print_exception") from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") - assert mock_prep_environment.call_count == 1 mock_get_guard.assert_called_once_with("My Guard's Name") assert isinstance(response, Tuple) @@ -307,19 +317,18 @@ def test_validate__raises_bad_request__num_reasks(mocker): mock_request = MockRequest("POST", json={"numReasks": 3}) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guard = mocker.patch( - "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.get_guard", + return_value=mock_guard, ) - mock_prep_environment = mocker.patch("src.blueprints.guards.prep_environment") - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) - mocker.patch("src.utils.handle_error.logger.error") - mocker.patch("src.utils.handle_error.traceback.print_exception") + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) + mocker.patch("guardrails_api.utils.handle_error.logger.error") + mocker.patch("guardrails_api.utils.handle_error.traceback.print_exception") from guardrails_api.blueprints.guards import validate response = validate("My%20Guard's%20Name") - assert mock_prep_environment.call_count == 1 mock_get_guard.assert_called_once_with("My Guard's Name") assert isinstance(response, Tuple) @@ -351,17 +360,16 @@ def test_validate__parse(mocker): ) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guard = mocker.patch( - "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.get_guard", + return_value=mock_guard, ) - mock_prep_environment = mocker.patch("src.blueprints.guards.prep_environment") - mock_cleanup_environment = mocker.patch("src.blueprints.guards.cleanup_environment") - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) # >>> Conflict - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) # set_attribute_spy = mocker.spy(mock_tracer.span, "set_attribute") @@ -374,7 +382,6 @@ def test_validate__parse(mocker): response = validate("My%20Guard's%20Name") - assert mock_prep_environment.call_count == 1 mock_get_guard.assert_called_once_with("My Guard's Name") assert mock_parse.call_count == 1 @@ -404,8 +411,6 @@ def test_validate__parse(mocker): # ] # set_attribute_spy.assert_has_calls(expected_calls) - assert mock_cleanup_environment.call_count == 1 - assert response == { "result": True, "validatedOutput": "Hello world!", @@ -437,21 +442,20 @@ def test_validate__call(mocker): ) mocker.patch("flask.Blueprint", new=MockBlueprint) - mocker.patch("src.blueprints.guards.request", mock_request) + mocker.patch("guardrails_api.blueprints.guards.request", mock_request) mock_get_guard = mocker.patch( - "src.blueprints.guards.guard_client.get_guard", return_value=mock_guard + "guardrails_api.blueprints.guards.guard_client.get_guard", + return_value=mock_guard, ) - mock_prep_environment = mocker.patch("src.blueprints.guards.prep_environment") - mock_cleanup_environment = mocker.patch("src.blueprints.guards.cleanup_environment") mocker.patch( - "src.blueprints.guards.get_llm_callable", + "guardrails_api.blueprints.guards.get_llm_callable", return_value="openai.Completion.create", ) - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) # >>> Conflict - # mocker.patch("src.blueprints.guards.get_tracer", return_value=mock_tracer) + # mocker.patch("guardrails_api.blueprints.guards.get_tracer", return_value=mock_tracer) # set_attribute_spy = mocker.spy(mock_tracer.span, "set_attribute") @@ -464,7 +468,6 @@ def test_validate__call(mocker): response = validate("My%20Guard's%20Name") - assert mock_prep_environment.call_count == 1 mock_get_guard.assert_called_once_with("My Guard's Name") assert mock___call__.call_count == 1 @@ -494,8 +497,6 @@ def test_validate__call(mocker): # ] # set_attribute_spy.assert_has_calls(expected_calls) - assert mock_cleanup_environment.call_count == 1 - assert response == { "result": False, "validatedOutput": None, diff --git a/tests/blueprints/test_root.py b/tests/blueprints/test_root.py index 1a964eb..7ef611f 100644 --- a/tests/blueprints/test_root.py +++ b/tests/blueprints/test_root.py @@ -23,12 +23,14 @@ def test_health_check(mocker): mock_pg = MockPostgresClient() mock_pg.db.session._set_rows([(1,)]) - mocker.patch("src.blueprints.root.PostgresClient", return_value=mock_pg) + mocker.patch("guardrails_api.blueprints.root.PostgresClient", return_value=mock_pg) def text_side_effect(query: str): return query - mock_text = mocker.patch("src.blueprints.root.text", side_effect=text_side_effect) + mock_text = mocker.patch( + "guardrails_api.blueprints.root.text", side_effect=text_side_effect + ) from guardrails_api.blueprints.root import health_check diff --git a/tests/cli/test_init.py b/tests/cli/test_init.py new file mode 100644 index 0000000..53cff2c --- /dev/null +++ b/tests/cli/test_init.py @@ -0,0 +1,27 @@ +import pytest +import typer + + +def test_version_callback(mocker): + mocker.patch("guardrails_api.cli.__init__.cli") + mock_print = mocker.patch("builtins.print") + + from guardrails_api.cli import version_callback + from guardrails_api import __version__ + + with pytest.raises(typer.Exit): + version_callback(True) + + mock_print.assert_called_once_with(f"guardrails-api CLI Version: {__version__}") + + +class TestMain: + def test_no_args(self, mocker): + # mocker.patch("guardrails_api.cli.__init__.cli") + mock_version_callback = mocker.patch("guardrails_api.cli.version_callback") + + from guardrails_api.cli import main + + main() + + assert mock_version_callback.call_count == 0 diff --git a/tests/cli/test_start.py b/tests/cli/test_start.py new file mode 100644 index 0000000..9d91ad9 --- /dev/null +++ b/tests/cli/test_start.py @@ -0,0 +1,23 @@ +def test_start(mocker): + mocker.patch("guardrails_api.cli.start.cli") + mock_gunicorn = mocker.patch("guardrails_api.cli.start.StandaloneApplication") + + mock_flask_app = mocker.stub() + mock_create_app = mocker.patch( + "guardrails_api.cli.start.create_app", return_value=mock_flask_app + ) + + from guardrails_api.cli.start import start + + start("env", "config") + + mock_gunicorn.assert_called_once_with( + mock_flask_app, + { + "bind": "0.0.0.0:8000", + "timeout": 5, + "threads": 10, + }, + ) + + mock_create_app.assert_called_once_with("env", "config") diff --git a/tests/clients/test_pg_guard_client.py b/tests/clients/test_pg_guard_client.py index 56a1072..fed1879 100644 --- a/tests/clients/test_pg_guard_client.py +++ b/tests/clients/test_pg_guard_client.py @@ -12,7 +12,8 @@ def test_init(mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) from guardrails_api.clients.pg_guard_client import PGGuardClient @@ -28,7 +29,8 @@ class TestGetGuard: def test_get_latest(self, mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) query_spy = mocker.spy(mock_pg_client.db.session, "query") @@ -38,7 +40,7 @@ def test_get_latest(self, mocker): mock_first.return_value = latest_guard mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = latest_guard @@ -58,7 +60,8 @@ def test_get_latest(self, mocker): def test_with_as_of_date(self, mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) query_spy = mocker.spy(mock_pg_client.db.session, "query") @@ -71,7 +74,7 @@ def test_with_as_of_date(self, mocker): mock_first.side_effect = [latest_guard, previous_guard] mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = previous_guard @@ -105,13 +108,14 @@ def test_with_as_of_date(self, mocker): def test_raises_not_found(self, mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_first = mocker.patch.object(mock_pg_client.db.session, "first") mock_first.return_value = None mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) from guardrails_api.clients.pg_guard_client import PGGuardClient @@ -133,7 +137,8 @@ def test_raises_not_found(self, mocker): def test_get_guard_item(mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) query_spy = mocker.spy(mock_pg_client.db.session, "query") @@ -158,7 +163,8 @@ def test_get_guard_item(mocker): def test_get_guards(mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) query_spy = mocker.spy(mock_pg_client.db.session, "query") @@ -169,7 +175,7 @@ def test_get_guards(mocker): mock_all.return_value = guards mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.side_effect = [guard_one, guard_two] @@ -194,15 +200,18 @@ def test_create_guard(mocker): mock_pg_client = MockPostgresClient() mock_guard_struct_init_spy = mocker.spy(MockGuardStruct, "__init__") mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, + ) + mocker.patch( + "guardrails_api.clients.pg_guard_client.GuardItem", new=MockGuardStruct ) - mocker.patch("src.clients.pg_guard_client.GuardItem", new=MockGuardStruct) add_spy = mocker.spy(mock_pg_client.db.session, "add") commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = mock_guard @@ -237,16 +246,17 @@ def test_raises_not_found(self, mocker): mock_guard = MockGuardStruct() mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = None commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) from guardrails_api.clients.pg_guard_client import PGGuardClient @@ -271,17 +281,18 @@ def test_updates_guard_item(self, mocker): updated_guard = MockGuardStruct(num_reasks=2) mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = old_guard to_dict_spy = mocker.spy(updated_guard.railspec, "to_dict") commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = updated_guard @@ -309,19 +320,20 @@ def test_guard_doesnt_exist_yet(self, mocker): new_guard = MockGuardStruct() mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = None commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_create_guard = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.create_guard" + "guardrails_api.clients.pg_guard_client.PGGuardClient.create_guard" ) mock_create_guard.return_value = new_guard @@ -343,17 +355,18 @@ def test_guard_already_exists(self, mocker): updated_guard = MockGuardStruct(num_reasks=2, description="updated description") mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = old_guard to_dict_spy = mocker.spy(updated_guard.railspec, "to_dict") commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = updated_guard @@ -379,16 +392,17 @@ class TestDeleteGuard: def test_raises_not_found(self, mocker): mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = None commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) from guardrails_api.clients.pg_guard_client import PGGuardClient @@ -412,17 +426,18 @@ def test_deletes_guard_item(self, mocker): old_guard = MockGuardStruct() mock_pg_client = MockPostgresClient() mocker.patch( - "src.clients.pg_guard_client.PostgresClient", return_value=mock_pg_client + "guardrails_api.clients.pg_guard_client.PostgresClient", + return_value=mock_pg_client, ) mock_get_guard_item = mocker.patch( - "src.clients.pg_guard_client.PGGuardClient.get_guard_item" + "guardrails_api.clients.pg_guard_client.PGGuardClient.get_guard_item" ) mock_get_guard_item.return_value = old_guard delete_spy = mocker.spy(mock_pg_client.db.session, "delete") commit_spy = mocker.spy(mock_pg_client.db.session, "commit") mock_from_guard_item = mocker.patch( - "src.clients.pg_guard_client.GuardStruct.from_guard_item" + "guardrails_api.clients.pg_guard_client.GuardStruct.from_guard_item" ) mock_from_guard_item.return_value = old_guard diff --git a/tests/utils/test_try_json_loads.py b/tests/utils/test_try_json_loads.py new file mode 100644 index 0000000..b0f2a12 --- /dev/null +++ b/tests/utils/test_try_json_loads.py @@ -0,0 +1,17 @@ +from guardrails_api.utils.try_json_loads import try_json_loads + + +class TestTryJsonLoads: + def test_pass(self): + val = '{"a": 1}' + + actual = try_json_loads(val) + + assert actual == {"a": 1} + + def test_fail(self): + val = "not a json object" + + actual = try_json_loads(val) + + assert actual == "not a json object" From 34e326ff87537b2110bf7610991feec1bf01210f Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 6 Jun 2024 17:23:34 -0500 Subject: [PATCH 07/12] update TODOs --- TODO.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index a01905b..7d6ed05 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,5 @@ - - [ ] Accept env file path as env var - - [ ] Accept config file path as env var - - [ ] Add pyproject toml and setup.py - - [ ] Add cli command to start server with arguments + - [x] Accept env file path as env var + - [x] Accept config file path as env var + - [x] Add pyproject toml and setup.py + - [x] Add cli command to start server with arguments - [ ] Update merge github action to publish module \ No newline at end of file From da35ae3984a2f76de782d8b0dddf9824cde11162 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 7 Jun 2024 09:24:41 -0500 Subject: [PATCH 08/12] add publish action, update pr checks --- .github/workflows/pr_qa.yml | 24 +++++++++----- .github/workflows/publish.yml | 45 ++++++++++++++++++++++++++ .github/workflows/publish_image.yml | 49 ----------------------------- 3 files changed, 61 insertions(+), 57 deletions(-) create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/publish_image.yml diff --git a/.github/workflows/pr_qa.yml b/.github/workflows/pr_qa.yml index 5624fc4..ec497d4 100644 --- a/.github/workflows/pr_qa.yml +++ b/.github/workflows/pr_qa.yml @@ -5,7 +5,9 @@ name: Pull Request Quality Checks # Controls when the workflow will run on: pull_request: - branches: [ main ] + branches: + - main + - feat/* workflow_dispatch: jobs: @@ -13,20 +15,26 @@ jobs: name: PR checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v4 + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.12.x + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Dependencies run: | python -m venv ./.venv source ./.venv/bin/activate - make install-lock; make install-dev; - curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml - npx @redocly/cli bundle --dereferenced --output ./open-api-spec.json --ext json ./open-api-spec.yml - - name: Run Quality Checks run: | source ./.venv/bin/activate - make qa; \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..b0daf62 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,45 @@ +name: Publish To PyPI +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build Image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.12.x + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip; + pip install twine; + pip install build; + continue-on-error: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Build the module + env: + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + make build + python -m build + continue-on-error: false + + - name: Upload to PyPI + env: + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + twine upload dist/* -u __token__ -p $PYPI_PASSWORD \ No newline at end of file diff --git a/.github/workflows/publish_image.yml b/.github/workflows/publish_image.yml deleted file mode 100644 index 37d543e..0000000 --- a/.github/workflows/publish_image.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Publish Image To ECR -on: - push: - branches: - - 'main' - workflow_dispatch: - -permissions: - contents: read - -jobs: - build: - name: Build Image - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - # - name: Login to Amazon ECR - # id: login-ecr - # uses: aws-actions/amazon-ecr-login@v1 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build, tag, and push base image to Amazon ECR - env: - IMAGE_NAME: api - ECR_REPO_NAME: guardrails-validation-service-test - run: | - bash buildspecs/build.sh - - - name: Build, tag, and push heavy image to Amazon ECR - env: - IMAGE_NAME: graas-heavy - ECR_REPO_NAME: guardrails-as-a-service-heavy-test - DOCKERFILE: Dockerfile.heavy - run: | - bash buildspecs/build.sh \ No newline at end of file From 83b8459c7d00e9783ce364896543e05ebac8a91a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 7 Jun 2024 09:31:42 -0500 Subject: [PATCH 09/12] pip install from pyproject toml, remove requirements files except lock for reference --- Makefile | 5 ++--- requirements-dev.txt | 4 ---- requirements.txt | 23 ----------------------- 3 files changed, 2 insertions(+), 30 deletions(-) delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt diff --git a/Makefile b/Makefile index ee9cf60..c881832 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,10 @@ # Installs production dependencies install: - pip install -r requirements.txt; + pip install .; # Installs development dependencies install-dev: - make install - pip install -r requirements-dev.txt; + pip install .[dev]; lock: pip freeze --exclude guardrails-api-client > requirements-lock.txt diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index bb740cd..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -ruff -pytest -coverage -pytest-mock \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b5dba40..0000000 --- a/requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -guardrails-ai -flask -# Let this come from guardrails-ai as a transient dependency. -# Pip confuses tag versions with commit ids, -# and claims a conflict even though it's the same thing. -# guard-rails-api-client @ git+https://github.com/guardrails-ai/guardrails-api-client.git@v0.0.2#egg=guard-rails-api-client&subdirectory=guard-rails-api-client -Flask-SQLAlchemy -werkzeug -jsonschema -referencing -flask_cors -numpy -faiss-cpu -nltk -boto3 -gunicorn -psycopg2-binary -litellm -opentelemetry-api>=1.0.0,<2 -opentelemetry-sdk>=1.0.0,<2 -opentelemetry-exporter-otlp-proto-grpc>=1.0.0,<2 -opentelemetry-exporter-otlp-proto-http>=1.0.0,<2 -opentelemetry-instrumentation-flask>=0.12b0,<1 \ No newline at end of file From 49c7d5f8e1aa45b101cf63230c221009201a6a38 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 14 Jun 2024 10:05:58 -0500 Subject: [PATCH 10/12] update build --- .github/workflows/pr_qa.yml | 3 +-- Makefile | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr_qa.yml b/.github/workflows/pr_qa.yml index b9310aa..8943511 100644 --- a/.github/workflows/pr_qa.yml +++ b/.github/workflows/pr_qa.yml @@ -32,10 +32,9 @@ jobs: run: | python -m venv ./.venv source ./.venv/bin/activate + make build make install-dev; - cp ./.venv/lib/python3.12/site-packages/guardrails_api_client/openapi-spec.json ./open-api-spec.json - - name: Run Quality Checks run: | source ./.venv/bin/activate diff --git a/Makefile b/Makefile index c881832..fc234be 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,9 @@ install-lock: .PHONY: build build: - curl https://raw.githubusercontent.com/guardrails-ai/guardrails-api-client/main/service-specs/guardrails-service-spec.yml -o ./open-api-spec.yml - npx @redocly/cli bundle --dereferenced --output ./guardrails_api/open-api-spec.json --ext json ./open-api-spec.yml + make install + + cp "$$(python -c "import guardrails_api_client as _; print(_.__path__[0])")/openapi-spec.json" ./guardrails_api/open-api-spec.json start: bash ./guardrails_api/start.sh From 3dd779197f4f96fc0d31d8d11ff4c9eb089651c9 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 14 Jun 2024 10:28:02 -0500 Subject: [PATCH 11/12] fix build; hack function ser; update swagger --- Makefile | 2 ++ guardrails_api/app.py | 4 +++- guardrails_api/blueprints/guards.py | 1 - guardrails_api/blueprints/root.py | 4 ++-- guardrails_api/start.sh | 4 ---- setup.py | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index fc234be..c1629a8 100644 --- a/Makefile +++ b/Makefile @@ -17,8 +17,10 @@ build: make install cp "$$(python -c "import guardrails_api_client as _; print(_.__path__[0])")/openapi-spec.json" ./guardrails_api/open-api-spec.json + start: + make build bash ./guardrails_api/start.sh infra: diff --git a/guardrails_api/app.py b/guardrails_api/app.py index 518be3e..b672b0c 100644 --- a/guardrails_api/app.py +++ b/guardrails_api/app.py @@ -16,7 +16,9 @@ class OverrideJsonProvider(DefaultJSONProvider): def default(self, o): if isinstance(o, set): return list(o) - return super().default(self, o) + if callable(o): + return str(o) + return super().default(o) class ReverseProxied(object): diff --git a/guardrails_api/blueprints/guards.py b/guardrails_api/blueprints/guards.py index a341dfb..777fff3 100644 --- a/guardrails_api/blueprints/guards.py +++ b/guardrails_api/blueprints/guards.py @@ -10,7 +10,6 @@ from opentelemetry.trace import Span from guardrails_api_client import Guard as GuardStruct from guardrails_api.classes.http_error import HttpError -from guardrails_api.classes.validation_output import ValidationOutput from guardrails_api.clients.memory_guard_client import MemoryGuardClient from guardrails_api.clients.pg_guard_client import PGGuardClient from guardrails_api.clients.postgres_client import postgres_is_enabled diff --git a/guardrails_api/blueprints/root.py b/guardrails_api/blueprints/root.py index 7315901..ed388be 100644 --- a/guardrails_api/blueprints/root.py +++ b/guardrails_api/blueprints/root.py @@ -60,11 +60,11 @@ def docs(): content="SwaggerUI" /> SwaggerUI - +
- +