From 6f02922d315022648afb2be6a0cb1b4d05395186 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Mon, 9 Oct 2023 16:05:55 -0500 Subject: [PATCH 01/31] additional instr for guard steps, cleanup --- src/blueprints/guards.py | 25 +++++++++++++++++++++++-- src/blueprints/root.py | 3 ++- string-guard.json | 21 +++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 string-guard.json diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 9056e5f..0051037 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -148,11 +148,25 @@ def validate(guard_name: str): result, validated_output, guard.state.all_histories, raw_output ) + prompt = ( + guard.rail.prompt.format(**(prompt_params or {})).source + if guard.rail.prompt + else None + ) + if prompt: + validate_span.set_attribute("prompt", prompt) + + instructions = ( + guard.rail.instructions.format(**(prompt_params or {})).source + if guard.rail.instructions + else None + ) + if instructions: + validate_span.set_attribute("instructions", instructions) + validation_status = "pass" if result is True else "fail" validate_span.set_attribute("validation_status", validation_status) validate_span.set_attribute("raw_llm_ouput", raw_output) - validate_span.set_attribute("prompt", guard.rail.prompt.format(**(prompt_params or {}))) - validate_span.set_attribute("instructions", guard.rail.instructions.format(**(prompt_params or {}))) # Use the serialization from the class instead of re-writing it valid_output: str = ( @@ -169,5 +183,12 @@ def validate(guard_name: str): total_token_count = prompt_token_count + response_token_count validate_span.set_attribute("tokens_consumed", total_token_count) + num_of_reasks = ( + len(guard_history.history) - 1 + if len(guard_history.history) > 0 + else 0 + ) + validate_span.set_attribute("num_of_reasks", num_of_reasks) + cleanup_environment(guard_struct) return validation_output.to_response() diff --git a/src/blueprints/root.py b/src/blueprints/root.py index 9548c82..7863a57 100644 --- a/src/blueprints/root.py +++ b/src/blueprints/root.py @@ -4,7 +4,8 @@ from src.clients.postgres_client import PostgresClient from src.utils.handle_error import handle_error from src.utils.gather_request_metrics import gather_request_metrics -from src.modules.otel_logger import logger +from src.utils import logger +# from src.modules.otel_logger import logger root_bp = Blueprint("root", __name__, url_prefix="/") diff --git a/string-guard.json b/string-guard.json new file mode 100644 index 0000000..b3c5018 --- /dev/null +++ b/string-guard.json @@ -0,0 +1,21 @@ +{ + "name": "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 pet name.", + "version": "0.1" + }, + "numReasks": 1 +} \ No newline at end of file From a0f11c1b479937056d7073822504a6fadc8c8570 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 12 Oct 2023 09:23:02 -0500 Subject: [PATCH 02/31] cleaup dockerfile --- Dockerfile.dev | 5 ----- open-api-spec.yml | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Dockerfile.dev b/Dockerfile.dev index b0593e3..29c9cde 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,3 @@ -# FROM python:3.6 FROM public.ecr.aws/docker/library/python:3.11-slim # Create app directory @@ -21,10 +20,6 @@ COPY ./guardrails-custom-validators ./guardrails-custom-validators # Copy the requirements file COPY requirements.txt . -# RUN apt-get update -# RUN apt-get install build-essential -y -# RUN apt-get install python3-dev -y -# RUN apt-get install libevent-dev -y RUN pip install ./guardrails-sdk RUN pip install ./guard-rails-api-client diff --git a/open-api-spec.yml b/open-api-spec.yml index 9a796ba..5b4d5ff 100644 --- a/open-api-spec.yml +++ b/open-api-spec.yml @@ -218,6 +218,7 @@ components: required: false schema: type: string + format: password schemas: HttpError: type: object From 6a0f5025c8c4a15984363b6a2d587952de9ddd7c Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 12 Oct 2023 14:04:18 -0500 Subject: [PATCH 03/31] fix string schema support --- open-api-spec.yml | 10 ++-- src/classes/data_type_struct.py | 29 +++++++++-- src/classes/schema_struct.py | 87 +++++++++++++++++++++------------ string-guard.json | 18 +++---- 4 files changed, 95 insertions(+), 49 deletions(-) diff --git a/open-api-spec.yml b/open-api-spec.yml index 5b4d5ff..63a4b3f 100644 --- a/open-api-spec.yml +++ b/open-api-spec.yml @@ -307,11 +307,15 @@ components: type: object properties: schema: - type: object - additionalProperties: - $ref: "#/components/schemas/DataType" + oneOf: + - $ref: "#/components/schemas/JsonSchema" + - $ref: "#/components/schemas/DataType" required: - schema + JsonSchema: + type: object + additionalProperties: + $ref: "#/components/schemas/DataType" DataType: type: object properties: diff --git a/src/classes/data_type_struct.py b/src/classes/data_type_struct.py index 4f0dcbf..ef5702e 100644 --- a/src/classes/data_type_struct.py +++ b/src/classes/data_type_struct.py @@ -1,5 +1,6 @@ +import inspect import re -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional from operator import attrgetter from lxml.etree import _Element, SubElement from guardrails.datatypes import DataType, registry @@ -10,6 +11,11 @@ class DataTypeStruct: + children: Dict[str, Any] = None + formatters: List[str] = None + element: SchemaElementStruct = None + plugins: List[str] = None + def __init__( self, children: Dict[str, Any] = None, @@ -250,8 +256,15 @@ def from_xml(cls, elem: _Element): return cls(children, formatters, element, plugins) - def to_xml(self, parent: _Element) -> _Element: - element = self.element.to_element() + def to_xml(self, parent: _Element, as_parent: Optional[bool] = False) -> _Element: + element = None + if as_parent: + element = parent + elem_attribs: ElementStub = self.element.to_element() + for k, v in elem_attribs.attrib.items(): + element.set(k, v) + else: + element = self.element.to_element() format = ( "; ".join(self.formatters) if len(self.formatters) > 0 else None @@ -264,7 +277,7 @@ def to_xml(self, parent: _Element) -> _Element: if plugins is not None: element.attrib["plugins"] = plugins - xml_data_type = SubElement(parent, element.tag, element.attrib) + xml_data_type = element if as_parent else SubElement(parent, element.tag, element.attrib) self_is_list = self.element.type == "list" if self.children is not None: @@ -293,3 +306,11 @@ def get_all_plugins(self) -> List[str]: for child_key in children: plugins.extend(children[child_key].get_all_plugins()) return plugins + + @staticmethod + def is_data_type_struct(other: Any) -> bool: + if isinstance(other, dict): + data_type_struct_attrs = DataTypeStruct.__dict__.keys() + other_keys = other.keys() + return set(other_keys).issubset(data_type_struct_attrs) + return False diff --git a/src/classes/schema_struct.py b/src/classes/schema_struct.py index 30742ad..ed04fca 100644 --- a/src/classes/schema_struct.py +++ b/src/classes/schema_struct.py @@ -1,4 +1,4 @@ -from typing import Dict, List +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 @@ -8,7 +8,7 @@ # consider making this JSONSchema # https://json-schema.org/ class SchemaStruct: - schema: Dict[str, DataTypeStruct] = None + schema: Union[Dict[str, DataTypeStruct], DataTypeStruct] = None def __init__(self, schema: Dict[str, DataTypeStruct] = None): self.schema = schema @@ -16,21 +16,24 @@ def __init__(self, schema: Dict[str, DataTypeStruct] = None): @classmethod def from_schema(cls, schema: Schema): serialized_schema = {} - for key in schema._schema: - schema_element = schema._schema[key] - serialized_schema[key] = DataTypeStruct.from_data_type( - schema_element + + if isinstance(schema, StringSchema): + serialized_schema = DataTypeStruct.from_data_type( + schema ) + else: + for key in schema._schema: + schema_element = schema._schema[key] + serialized_schema[key] = DataTypeStruct.from_data_type( + schema_element + ) return cls({"schema": serialized_schema}) def to_schema(self) -> Schema: schema = {} inner_schema = self.schema["schema"] - if ( - hasattr(inner_schema, "element") - and inner_schema.element.type == "string" - ): + if isinstance(inner_schema, DataTypeStruct): string_schema = StringSchema() string_schema.string_key = inner_schema.element.name string_schema[ @@ -48,20 +51,28 @@ def to_schema(self) -> Schema: def from_dict(cls, schema: dict): if schema is not None: serialized_schema = {} - orig_schema = schema["schema"] - for key in orig_schema: - schema_element = orig_schema[key] - serialized_schema[key] = DataTypeStruct.from_dict( - schema_element + inner_schema = schema["schema"] + if DataTypeStruct.is_data_type_struct(inner_schema): + serialized_schema = DataTypeStruct.from_dict( + inner_schema ) + else: + for key in inner_schema: + schema_element = inner_schema[key] + serialized_schema[key] = DataTypeStruct.from_dict( + schema_element + ) return cls({"schema": serialized_schema}) def to_dict(self): dict_schema = {} inner_schema = self.schema["schema"] - for key in inner_schema: - schema_element = inner_schema[key] - dict_schema[key] = schema_element.to_dict() + if isinstance(inner_schema, DataTypeStruct): + dict_schema = inner_schema.to_dict() + else: + for key in inner_schema: + schema_element = inner_schema[key] + dict_schema[key] = schema_element.to_dict() return {"schema": dict_schema} @classmethod @@ -69,21 +80,33 @@ def from_request(cls, schema: dict): if schema is not None: serialized_schema = {} inner_schema = schema["schema"] - for key in inner_schema: - schema_element = inner_schema[key] - serialized_schema[key] = DataTypeStruct.from_request( - schema_element + + # StringSchema (or really just any PrimitiveSchema) + if DataTypeStruct.is_data_type_struct(inner_schema): + serialized_schema = DataTypeStruct.from_request( + inner_schema ) + else: + # JsonSchema + for key in inner_schema: + schema_element = inner_schema[key] + serialized_schema[key] = DataTypeStruct.from_request( + schema_element + ) return cls({"schema": serialized_schema}) def to_response(self): dict_schema = {} inner_schema = self.schema["schema"] - for key in inner_schema: - schema_element = inner_schema[key] - dict_schema[key] = schema_element.to_response() + if isinstance(inner_schema, DataTypeStruct): + dict_schema = inner_schema.to_response() + else: + for key in inner_schema: + schema_element = inner_schema[key] + dict_schema[key] = schema_element.to_response() return {"schema": dict_schema} + # FIXME: if this is ever used it needs to be updated to handle StringSchemas @classmethod def from_xml(cls, xml: _Element): schema = {} @@ -99,9 +122,12 @@ def from_xml(cls, xml: _Element): def to_xml(self, parent: _Element, tag: str) -> _Element: xml_schema = SubElement(parent, tag) inner_schema = self.schema["schema"] - for key in inner_schema: - child: DataTypeStruct = inner_schema[key] - child.to_xml(xml_schema) + if isinstance(inner_schema, DataTypeStruct): + inner_schema.to_xml(xml_schema, True) + else: + for key in inner_schema: + child: DataTypeStruct = inner_schema[key] + child.to_xml(xml_schema) return xml_schema @@ -109,10 +135,7 @@ def get_all_plugins(self) -> List[str]: plugins = [] inner_schema = self.schema["schema"] - if ( - hasattr(inner_schema, "element") - and inner_schema.element.type == "string" - ): + if isinstance(inner_schema, DataTypeStruct): plugins.extend(inner_schema.get_all_plugins()) else: for key in inner_schema: diff --git a/string-guard.json b/string-guard.json index b3c5018..a8b2cc0 100644 --- a/string-guard.json +++ b/string-guard.json @@ -3,18 +3,16 @@ "railspec": { "outputSchema": { "schema": { - "pet_name": { - "element": { - "type": "string", - "name": "pet_name", - "description": "Generate a pet name.", - "onFail": "fix" - }, - "formatters": ["length: 1 10"] - } + "element": { + "type": "string", + "name": "pet_name", + "description": "Generate a pet name.", + "onFail": "fix" + }, + "formatters": ["length: 1 10"] } }, - "prompt": "Generate a pet name.", + "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 From 7f7fc37dc7856a146060939fb4d0104ba839b2be Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 17 Oct 2023 15:52:07 -0500 Subject: [PATCH 04/31] another sample guard --- name-guard.json | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 name-guard.json diff --git a/name-guard.json b/name-guard.json new file mode 100644 index 0000000..2220823 --- /dev/null +++ b/name-guard.json @@ -0,0 +1,41 @@ +{ + "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 From b416b4cea36d50ba5249577446650221dda601c4 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 20 Oct 2023 08:42:09 -0500 Subject: [PATCH 05/31] create guard within context of tracer, new object string guard --- object-string-guard.json | 21 +++++++++++++++++++++ src/blueprints/guards.py | 7 +++---- 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 object-string-guard.json diff --git a/object-string-guard.json b/object-string-guard.json new file mode 100644 index 0000000..2e56fa4 --- /dev/null +++ b/object-string-guard.json @@ -0,0 +1,21 @@ +{ + "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/src/blueprints/guards.py b/src/blueprints/guards.py index 0051037..58ede94 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -84,7 +84,6 @@ def validate(guard_name: str): openai_api_key = request.headers.get("x-openai-api-key", None) guard_struct = guard_client.get_guard(guard_name) prep_environment(guard_struct) - guard: Guard = guard_struct.to_guard(openai_api_key, otel_tracer) llm_output = payload.pop("llmOutput", None) num_reasks = payload.pop("numReasks", guard_struct.num_reasks) @@ -93,8 +92,8 @@ def validate(guard_name: str): args = payload.pop("args", []) with otel_tracer.start_as_current_span(f"validate-{guard_name}") as validate_span: - # Don't use this; just use the trace id - # validate_span.set_attribute("guard_run_id", str(guard_run_id)) + guard: Guard = guard_struct.to_guard(openai_api_key, otel_tracer) + validate_span.set_attribute("guardName", guard_name) if llm_api is not None: llm_api = get_llm_callable(llm_api) @@ -189,6 +188,6 @@ def validate(guard_name: str): else 0 ) validate_span.set_attribute("num_of_reasks", num_of_reasks) - + cleanup_environment(guard_struct) return validation_output.to_response() From 1b907c95fd22777be736fc13452ad931926ef51c Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Fri, 20 Oct 2023 16:13:35 -0500 Subject: [PATCH 06/31] railspecs --- rail-spec.xml => guard-rail-spec.xml | 0 string-guard-rail-spec.xml | 11 +++++++++++ 2 files changed, 11 insertions(+) rename rail-spec.xml => guard-rail-spec.xml (100%) create mode 100644 string-guard-rail-spec.xml diff --git a/rail-spec.xml b/guard-rail-spec.xml similarity index 100% rename from rail-spec.xml rename to guard-rail-spec.xml diff --git a/string-guard-rail-spec.xml b/string-guard-rail-spec.xml new file mode 100644 index 0000000..bb8ec00 --- /dev/null +++ b/string-guard-rail-spec.xml @@ -0,0 +1,11 @@ + + + + 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 From 955eb65898fdfe531beab38c462a19ced863c56e Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Mon, 6 Nov 2023 10:40:36 -0600 Subject: [PATCH 07/31] decode guard names --- src/blueprints/guards.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 58ede94..2606642 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -1,5 +1,6 @@ import json from flask import Blueprint, request +from urllib.parse import unquote_plus from guardrails import Guard from guardrails.utils.logs_utils import GuardLogs from src.classes.guard_struct import GuardStruct @@ -43,17 +44,19 @@ def guards(): @handle_error @gather_request_metrics def guard(guard_name: str): + decoded_guard_name = unquote_plus(guard_name) if request.method == "GET": as_of_query = request.args.get("asOf") - guard = guard_client.get_guard(guard_name, as_of_query) + print(f"Guard with name {decoded_guard_name} was requested.") + guard = guard_client.get_guard(decoded_guard_name, as_of_query) return guard.to_response() elif request.method == "PUT": payload = request.json guard = GuardStruct.from_request(payload) - updated_guard = guard_client.upsert_guard(guard_name, guard) + updated_guard = guard_client.upsert_guard(decoded_guard_name, guard) return updated_guard.to_response() elif request.method == "DELETE": - guard = guard_client.delete_guard(guard_name) + guard = guard_client.delete_guard(decoded_guard_name) return guard.to_response() else: raise HttpError( @@ -82,7 +85,8 @@ def validate(guard_name: str): ) payload = request.json openai_api_key = request.headers.get("x-openai-api-key", None) - guard_struct = guard_client.get_guard(guard_name) + decoded_guard_name = unquote_plus(guard_name) + guard_struct = guard_client.get_guard(decoded_guard_name) prep_environment(guard_struct) llm_output = payload.pop("llmOutput", None) @@ -91,10 +95,10 @@ def validate(guard_name: str): llm_api = payload.pop("llmApi", None) args = payload.pop("args", []) - with otel_tracer.start_as_current_span(f"validate-{guard_name}") as validate_span: + with otel_tracer.start_as_current_span(f"validate-{decoded_guard_name}") as validate_span: guard: Guard = guard_struct.to_guard(openai_api_key, otel_tracer) - validate_span.set_attribute("guardName", guard_name) + validate_span.set_attribute("guardName", decoded_guard_name) if llm_api is not None: llm_api = get_llm_callable(llm_api) if openai_api_key is None: From 7246e7fc29ea586859562353dbb54b3d29d3bd5e Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 8 Nov 2023 09:38:25 -0600 Subject: [PATCH 08/31] prepare for cdk --- .dockerignore | 7 ++ .github/workflows/publish_image.yml | 38 +++++++ .gitignore | 3 + Dockerfile.prod | 36 +++++++ buildspecs/build.sh | 35 +++++++ buildspecs/deploy.yml | 8 ++ dbuild.sh => dev-build.sh | 2 +- dev-run.sh | 3 + dev.sh | 13 +-- docker-compose-dev.yml | 148 ---------------------------- prod-build.sh | 6 ++ prod-run.sh | 3 + 12 files changed, 147 insertions(+), 155 deletions(-) create mode 100644 .github/workflows/publish_image.yml create mode 100644 Dockerfile.prod create mode 100644 buildspecs/build.sh create mode 100644 buildspecs/deploy.yml rename dbuild.sh => dev-build.sh (78%) create mode 100644 dev-run.sh delete mode 100644 docker-compose-dev.yml create mode 100644 prod-build.sh create mode 100644 prod-run.sh diff --git a/.dockerignore b/.dockerignore index 8b5b20d..7981043 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,9 @@ .venv diagrams +.github +buildspecs +tests +docker* +Docker* +*.sh +ingestion-service-spec.yml \ No newline at end of file diff --git a/.github/workflows/publish_image.yml b/.github/workflows/publish_image.yml new file mode 100644 index 0000000..d3f0e9e --- /dev/null +++ b/.github/workflows/publish_image.yml @@ -0,0 +1,38 @@ +name: Publish Image To ER +on: + push: + branches: + - 'main' + workflow_dispatch: + +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 image to Amazon ECR + env: + IMAGE_NAME: api + ECR_REPO_NAME: guardrails-validation-service-test + run: | + bash buildspecs/build.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index aa54a5b..7612491 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ coverage.json .ropeproject install*.sh guardrails-custom-validators +guardrails-api-client +guardrails +*.env \ No newline at end of file diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..6beca82 --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,36 @@ +FROM public.ecr.aws/docker/library/python:3.11-slim + +# 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 +RUN apt-get update && apt-get install -y git + +COPY ./guard-rails-api-client ./guard-rails-api-client + +# Copy the requirements file +COPY requirements.txt . + +RUN pip install ./guard-rails-api-client +RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry + +# Install app dependencies +RUN pip install -r requirements.txt + +# Freeze dependencies +RUN pip freeze > requirements.txt + +# Copy the whole folder inside the Image filesystem +COPY . . + +EXPOSE 8000 + +CMD opentelemetry-instrument gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" diff --git a/buildspecs/build.sh b/buildspecs/build.sh new file mode 100644 index 0000000..ea56d1d --- /dev/null +++ b/buildspecs/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +callerIdentity=$(aws sts get-caller-identity); +accountId=$(jq -r .Account <<< $callerIdentity); +version=$(cat ./package.json | jq -r .version); +imageName="${IMAGE_NAME:-api}"; +repoName="${ECR_REPO_NAME:-guardrails-validation-service-test}"; +commitSha=$(git rev-parse HEAD); +region="${AWS_DEFAULT_REGION:-us-east-1}"; +defaultEcrEndpoint="${accountId}.dkr.ecr.${region}.amazonaws.com"; +ecrEndpoint="${ECR_ENDPOINT:-$defaultEcrEndpoint}" +ecrImageUrl="${ecrEndpoint}/${repoName}"; + +# Setup unpublished api client +echo "Building api client..." +bash build-sdk.sh + +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 \ + -f Dockerfile.prod \ + -t "$imageName:$commitSha" \ + -t "$imageName:$version" \ + -t "$imageName:latest" . \ + || exit 1; + +docker image tag "$imageName:$commitSha" "$ecrImageUrl:$commitSha"; +docker image tag "$imageName:$version" "$ecrImageUrl:$version"; +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 new file mode 100644 index 0000000..373f15a --- /dev/null +++ b/buildspecs/deploy.yml @@ -0,0 +1,8 @@ +version: 0.2 + +phases: + build: + commands: + - echo "Deploying $ECR_ENDPOINT:$IMAGE_VERSION_TAG to lambda function $LAMBDA_FUNCTION_NAME" + - aws lambda update-function-code --function-name $LAMBDA_FUNCTION_NAME --image-uri "$ECR_ENDPOINT:$IMAGE_VERSION_TAG" + - echo "Deployment complete" \ No newline at end of file diff --git a/dbuild.sh b/dev-build.sh similarity index 78% rename from dbuild.sh rename to dev-build.sh index 6584cfc..dac1948 100644 --- a/dbuild.sh +++ b/dev-build.sh @@ -3,4 +3,4 @@ docker build \ --progress=plain \ --no-cache \ --build-arg CACHEBUST="$(date)" \ - -t "guardrails-api:latest" .; \ No newline at end of file + -t "guardrails-api:dev" .; \ No newline at end of file diff --git a/dev-run.sh b/dev-run.sh new file mode 100644 index 0000000..9f81758 --- /dev/null +++ b/dev-run.sh @@ -0,0 +1,3 @@ +docker stop guardrails-api-dev || true +docker rm guardrails-api-dev || true +docker run -p 8000:8000 --name guardrails-api-dev -it guardrails-api:dev \ No newline at end of file diff --git a/dev.sh b/dev.sh index 88e8809..122230a 100644 --- a/dev.sh +++ b/dev.sh @@ -1,10 +1,10 @@ #!/bin/bash -pip uninstall -y guardrails-ai -rm -rf ./guardrails-sdk -cp -r ../guardrails ./guardrails-sdk -pip install ../guardrails +# 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 +# cp -r ../guardrails-custom-validators ./guardrails-custom-validators bash build-sdk.sh @@ -14,4 +14,5 @@ cp ./pgadmin-dev-server.json ./pgadmin-data/servers.json PG_PASSWORD="${PGPASSWORD:-changeme}" echo "$PG_PASSWORD" > ./pgadmin-data/passfile -docker-compose -f docker-compose-dev.yml up --build \ No newline at end of file +bash dev-build.sh +bash dev-run.sh \ No newline at end of file diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml deleted file mode 100644 index c4ef71a..0000000 --- a/docker-compose-dev.yml +++ /dev/null @@ -1,148 +0,0 @@ -version: '3' -services: - postgres: - image: postgres - environment: - POSTGRES_USER: ${PGUSER:-postgres} - POSTGRES_PASSWORD: ${PGPASSWORD:-changeme} - POSTGRES_DATA: /data/postgres - volumes: - - ./postgres:/data/postgres - ports: - - "5432:5432" - restart: always - pgadmin: - image: dpage/pgadmin4 - restart: always - ports: - - "8888:80" - environment: - PGADMIN_DEFAULT_EMAIL: "${PGUSER:-postgres}@guardrails.com" - PGADMIN_DEFAULT_PASSWORD: ${PGPASSWORD:-changeme} - PGADMIN_SERVER_JSON_FILE: /var/lib/pgadmin/servers.json - volumes: - - ./pgadmin-data:/var/lib/pgadmin - depends_on: - - postgres - guardrails-api: - image: guardrails-api:latest - build: - context: . - dockerfile: Dockerfile.dev - args: - PORT: "8000" - ports: - - "8000:8000" - environment: - PGPORT: 5432 - REDIS_ENDPOINT: http://postgres:5432 - PGDATABASE: postgres - PGHOST: postgres - PGUSER: ${PGUSER:-postgres} - PGPASSWORD: ${PGPASSWORD:-changeme} - PYTHONUNBUFFERED: 1 - LOGLEVEL: DEBUG - OTLP_ENDPOINT: 'http://otel-collector:4317' - depends_on: - - postgres - - opensearch-node1 - - opensearch-node2 - - data-prepper - - otel-collector - opensearch-node1: - image: opensearchproject/opensearch:latest - container_name: opensearch-node1 - environment: - - cluster.name=guardrails-cluster - - node.name=opensearch-node1 - - discovery.seed_hosts=opensearch-node1,opensearch-node2 - - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 - - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - volumes: - - opensearch-data1:/usr/share/opensearch/data - ports: - - 9200:9200 - - 9600:9600 - # networks: - # - opensearch-net - opensearch-node2: - image: opensearchproject/opensearch:latest - container_name: opensearch-node2 - environment: - - cluster.name=guardrails-cluster - - node.name=opensearch-node2 - - discovery.seed_hosts=opensearch-node1,opensearch-node2 - - cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2 - - bootstrap.memory_lock=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - volumes: - - opensearch-data2:/usr/share/opensearch/data - # networks: - # - opensearch-net - # For demo purposes - opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:latest - container_name: opensearch-dashboards - ports: - - 5601:5601 - expose: - - "5601" - environment: - OPENSEARCH_HOSTS: '["https://opensearch-node1:9200","https://opensearch-node2:9200"]' - # networks: - # - opensearch-net - data-prepper: - restart: unless-stopped - container_name: data-prepper - image: opensearchproject/data-prepper:latest - volumes: - - ./configs/pipelines.yml:/usr/share/data-prepper/pipelines/pipelines.yaml - - ./configs/data-prepper-config.yml:/usr/share/data-prepper/config/data-prepper-config.yaml - ports: - - 21890:21890 - - 21891:21891 - - 21892:21892 - expose: - - "21890" - - "21891" - - "21892" - # networks: - # - opensearch-net - depends_on: - - opensearch-node1 - - opensearch-node2 - otel-collector: - restart: unless-stopped - container_name: otel-collector - image: otel/opentelemetry-collector:latest - command: ["--config=/etc/otel-collector-config.yml"] - volumes: - - ./configs/otel-collector-config.yml:/etc/otel-collector-config.yml - ports: - - "4317:4317" - depends_on: - - data-prepper - # networks: - # - opensearch-net - -volumes: - opensearch-data1: - opensearch-data2: - -# networks: -# opensearch-net: \ No newline at end of file diff --git a/prod-build.sh b/prod-build.sh new file mode 100644 index 0000000..cef7794 --- /dev/null +++ b/prod-build.sh @@ -0,0 +1,6 @@ +docker build \ + -f Dockerfile.prod \ + --progress=plain \ + --no-cache \ + --build-arg CACHEBUST="$(date)" \ + -t "guardrails-api:prod" .; \ No newline at end of file diff --git a/prod-run.sh b/prod-run.sh new file mode 100644 index 0000000..90ae185 --- /dev/null +++ b/prod-run.sh @@ -0,0 +1,3 @@ +docker stop guardrails-api-prod || true +docker rm guardrails-api-prod || true +docker run -p 8000:8000 --env-file local-prod.env --name guardrails-api-prod -it guardrails-api:prod \ No newline at end of file From 07ef48e775ff39d41b3e1ce0b6f715b6dcb041d1 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 8 Nov 2023 09:55:06 -0600 Subject: [PATCH 09/31] cleanup --- buildspecs/build.sh | 4 +--- src/blueprints/guards.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/buildspecs/build.sh b/buildspecs/build.sh index ea56d1d..61ca6db 100644 --- a/buildspecs/build.sh +++ b/buildspecs/build.sh @@ -2,8 +2,7 @@ callerIdentity=$(aws sts get-caller-identity); accountId=$(jq -r .Account <<< $callerIdentity); -version=$(cat ./package.json | jq -r .version); -imageName="${IMAGE_NAME:-api}"; +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}"; @@ -23,7 +22,6 @@ docker buildx build \ --progress plain \ -f Dockerfile.prod \ -t "$imageName:$commitSha" \ - -t "$imageName:$version" \ -t "$imageName:latest" . \ || exit 1; diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 2606642..070eb1d 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -47,7 +47,6 @@ def guard(guard_name: str): decoded_guard_name = unquote_plus(guard_name) if request.method == "GET": as_of_query = request.args.get("asOf") - print(f"Guard with name {decoded_guard_name} was requested.") guard = guard_client.get_guard(decoded_guard_name, as_of_query) return guard.to_response() elif request.method == "PUT": From bef8e1ce69c06b753b0d8f6de4a641390937e49e Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 8 Nov 2023 15:49:13 -0600 Subject: [PATCH 10/31] cdk first pass --- Dockerfile.prod | 4 +- 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 | 10 + cdktf/src/configs/index.ts | 3 + cdktf/src/configs/open-search-config.ts | 12 + cdktf/src/constructs/application.ts | 241 + cdktf/src/constructs/deployment-pipeline.ts | 324 ++ cdktf/src/constructs/index.ts | 3 + cdktf/src/constructs/rds-postgres.ts | 198 + 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 | 117 + cdktf/src/utils/index.ts | 1 + cdktf/src/utils/truncate.ts | 24 + cdktf/tsconfig.json | 35 + prod-run.sh | 7 +- requirements.txt | 5 +- src/clients/postgres_client.py | 14 +- src/models/base.py | 1 + 27 files changed, 5660 insertions(+), 5 deletions(-) create mode 100644 cdktf/.gitignore create mode 100644 cdktf/__tests__/main-test.ts create mode 100644 cdktf/help create mode 100644 cdktf/index.ts create mode 100644 cdktf/package-lock.json create mode 100644 cdktf/package.json create mode 100644 cdktf/src/configs/allow-all-egress.ts create mode 100644 cdktf/src/configs/base-construct-config.ts create mode 100644 cdktf/src/configs/index.ts create mode 100644 cdktf/src/configs/open-search-config.ts create mode 100644 cdktf/src/constructs/application.ts create mode 100644 cdktf/src/constructs/deployment-pipeline.ts create mode 100644 cdktf/src/constructs/index.ts create mode 100644 cdktf/src/constructs/rds-postgres.ts create mode 100644 cdktf/src/index.ts create mode 100644 cdktf/src/stacks/index.ts create mode 100644 cdktf/src/stacks/main.ts create mode 100644 cdktf/src/substacks/index.ts create mode 100644 cdktf/src/substacks/main.ts create mode 100644 cdktf/src/utils/index.ts create mode 100644 cdktf/src/utils/truncate.ts create mode 100644 cdktf/tsconfig.json diff --git a/Dockerfile.prod b/Dockerfile.prod index 6beca82..f989edc 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -12,7 +12,8 @@ RUN python3 -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" # Install git -RUN apt-get update && apt-get install -y git +RUN apt-get update +RUN apt-get install -y git curl COPY ./guard-rails-api-client ./guard-rails-api-client @@ -21,6 +22,7 @@ COPY requirements.txt . RUN pip install ./guard-rails-api-client RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry +RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem # Install app dependencies RUN pip install -r requirements.txt diff --git a/cdktf/.gitignore b/cdktf/.gitignore new file mode 100644 index 0000000..1dfae30 --- /dev/null +++ b/cdktf/.gitignore @@ -0,0 +1,11 @@ +*.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 new file mode 100644 index 0000000..5498de8 --- /dev/null +++ b/cdktf/__tests__/main-test.ts @@ -0,0 +1,89 @@ +// 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 new file mode 100644 index 0000000..716be8d --- /dev/null +++ b/cdktf/help @@ -0,0 +1,51 @@ +======================================================================================================== + + 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 new file mode 100644 index 0000000..8879d7a --- /dev/null +++ b/cdktf/index.ts @@ -0,0 +1 @@ +export * from './src'; \ No newline at end of file diff --git a/cdktf/package-lock.json b/cdktf/package-lock.json new file mode 100644 index 0000000..00d7626 --- /dev/null +++ b/cdktf/package-lock.json @@ -0,0 +1,4449 @@ +{ + "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 new file mode 100644 index 0000000..5ca83e2 --- /dev/null +++ b/cdktf/package.json @@ -0,0 +1,34 @@ +{ + "name": "guardrails-validation-cdktf", + "version": "1.0.0", + "main": "main.js", + "types": "main.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 new file mode 100644 index 0000000..d2450a8 --- /dev/null +++ b/cdktf/src/configs/allow-all-egress.ts @@ -0,0 +1,13 @@ +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 new file mode 100644 index 0000000..89f66de --- /dev/null +++ b/cdktf/src/configs/base-construct-config.ts @@ -0,0 +1,10 @@ +export type BaseConstructConfig = { + environment: 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 new file mode 100644 index 0000000..a4b0057 --- /dev/null +++ b/cdktf/src/configs/index.ts @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000..aa2185a --- /dev/null +++ b/cdktf/src/configs/open-search-config.ts @@ -0,0 +1,12 @@ +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; +} \ No newline at end of file diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts new file mode 100644 index 0000000..a910d6b --- /dev/null +++ b/cdktf/src/constructs/application.ts @@ -0,0 +1,241 @@ +import { Construct } from 'constructs'; +import { + cloudwatchLogGroup as cloudwatchLogGroupLib, + ecrRepository, + dataAwsIamPolicyDocument, + iamRole, + lambdaFunction as lambdaFunctionLib, + lambdaFunctionUrl as lambdaFunctionUrlLib, + opensearchDomainPolicy as opensearchDomainPolicyLib, + subnet, + vpc as vpcLib +} from '@cdktf/provider-aws'; +import { DefaultedBaseConstructConfig, OpenSearchConfig } from '../configs'; + +import CloudwatchLogGroup = cloudwatchLogGroupLib.CloudwatchLogGroup; +import EcrRepository = ecrRepository.EcrRepository; +import DataAwsIamPolicyDocument = dataAwsIamPolicyDocument.DataAwsIamPolicyDocument; +import IamRole = iamRole.IamRole; +import LambdaFunction = lambdaFunctionLib.LambdaFunction; +import LambdaFunctionUrl = lambdaFunctionUrlLib.LambdaFunctionUrl; +import OpensearchDomainPolicy = opensearchDomainPolicyLib.OpensearchDomainPolicy; +import Subnet = subnet.Subnet; +import Vpc = vpcLib.Vpc; +import { RdsPostgres } from './rds-postgres'; + +export type ApplicationConfig = { + ecrRepo: EcrRepository; + lambdaFunctionName: string; + openSearchConfig: OpenSearchConfig; + rdsPostgres: RdsPostgres; + subnets: Subnet[]; + vpc: Vpc; +}; + +export class Application extends Construct { + private _endpoint: string; + private _lambdaFunction: LambdaFunction; + private _lambdaRole: IamRole; + private _lambdaUrl: LambdaFunctionUrl; + private _lambdaLogs: CloudwatchLogGroup; + + constructor (scope: Construct, id: string, baseConfig: DefaultedBaseConstructConfig, applicationConfig: ApplicationConfig) { + super(scope, id); + + const { + environment + } = baseConfig; + + const { + ecrRepo, + lambdaFunctionName, + openSearchConfig, + rdsPostgres, + subnets, + vpc + } = applicationConfig; + + const { + credentials: openSearchClusterCredentials, + opensearchDomain + } = openSearchConfig; + + this._lambdaLogs = new CloudwatchLogGroup(this, `${id}-lambda-logs`, { + name: `/aws/lambda/${lambdaFunctionName}`, + retentionInDays: 30 + }); + + this._lambdaRole = new IamRole(this, `${id}-lambda-role`, { + name: `${lambdaFunctionName}-role`, + assumeRolePolicy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com' + }, + Action: 'sts:AssumeRole' + }] + }), + inlinePolicy: [ + { + name: 'opensearch-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'es:ESHttp*' + ], + Resource: [ + opensearchDomain.arn, + `${opensearchDomain.arn}/*` + ] + }] + }) + }, + { + name: 'secrets-manager-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'secretsmanager:GetSecretValue' + ], + Resource: [ + openSearchClusterCredentials.arn, + rdsPostgres.secretArn + ] + }] + }) + }, + { + name: 'eni-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'ec2:CreateNetworkInterface', + 'ec2:DescribeNetworkInterfaces', + 'ec2:DeleteNetworkInterface' + ], + // Does not work without broad access + Resource: '*' + }] + }) + }, + { + name: 'cloudwatch-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ], + Resource: [ + this.lambdaLogs.arn, + `${this.lambdaLogs.arn}/*`, + `${this.lambdaLogs.arn}:*` + ] + }] + }) + } + ] + }); + + this._lambdaFunction = new LambdaFunction(this, `${id}-lambda-function`, { + functionName: lambdaFunctionName, + description: 'Guardrails Telemetry Service API', + role: this.lambdaRole.arn, + architectures: ['arm64'], + environment: { + variables: { + APP_ENVIRONMENT: environment, + AWS_LWA_READINESS_CHECK_PORT: '8000', + LOGLEVEL: 'INFO', + NODE_ENV: 'production', + // TODO: enable otlp via lambda extension: + // https://opentelemetry.io/docs/faas/lambda-collector/ + // https://aws.amazon.com/blogs/compute/working-with-lambda-layers-and-extensions-in-container-images/ + OPENSEARCH_SECRET: openSearchClusterCredentials.arn, + OPENSEARCH_URL: opensearchDomain.endpoint, + OTEL_SERVICE_NAME: 'guardrails-api', + // OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4317', + OTEL_TRACES_EXPORTER: 'console', //'otlp', + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', + OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', + OTEL_METRICS_EXPORTER: 'console', //'otlp', + PGPORT: rdsPostgres.instance.port.toString(), + PGDATABASE: rdsPostgres.instance.dbName, + PGHOST: rdsPostgres.instance.endpoint, + PGUSER: 'postgres', + PGPASSWORD_SECRET_ARN: rdsPostgres.secretArn, + PORT: '8000', + PYTHONUNBUFFERED: '1' + } + }, + imageUri: `${ecrRepo.repositoryUrl}:latest`, + memorySize: 512, + packageType: 'Image', + timeout: 60 * 15, + vpcConfig: { + securityGroupIds: [vpc.defaultSecurityGroupId], + subnetIds: subnets.map(s => s.id) + } + }); + + this._lambdaUrl = new LambdaFunctionUrl(this, `${id}-lambda-url`, { + functionName: this.lambdaFunction.functionName, + authorizationType: 'NONE' + }); + this._endpoint = this.lambdaUrl.functionUrl; + + // This needs to happen here because since the lambda role will be a principal, it must exist before creating the below policy. + // We make this depend on the lambda url to force it to launch last. + const policyDocument = new DataAwsIamPolicyDocument(this, `${id}-domain-policy-document`, { + statement: [ + { + effect: 'Allow', + principals: [ + { + type: 'AWS', + identifiers: [this.lambdaRole.arn] + } + ], + actions: ['es:ESHttp*'], + resources: [ + opensearchDomain.arn, + `${opensearchDomain.arn}/*` + ] + } + ], + dependsOn: [this.lambdaRole, opensearchDomain, this.lambdaUrl] + }); + new OpensearchDomainPolicy(this, `${id}-domain-policy`, { + domainName: opensearchDomain.domainName, + accessPolicies: policyDocument.json, + dependsOn: [this.lambdaRole, opensearchDomain, this.lambdaUrl] + }); + } + + public get endpoint (): string { + return this._endpoint; + } + public get lambdaLogs (): CloudwatchLogGroup { + return this._lambdaLogs; + } + public get lambdaRole (): IamRole { + return this._lambdaRole; + } + public get lambdaFunction (): LambdaFunction { + return this._lambdaFunction; + } + public get lambdaUrl (): LambdaFunctionUrl { + return this._lambdaUrl; + } +} \ No newline at end of file diff --git a/cdktf/src/constructs/deployment-pipeline.ts b/cdktf/src/constructs/deployment-pipeline.ts new file mode 100644 index 0000000..7b4b349 --- /dev/null +++ b/cdktf/src/constructs/deployment-pipeline.ts @@ -0,0 +1,324 @@ +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; + /** + * Name of the Lambda Function to deploy changes to. + */ + lambdaFunctionName: 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; +}; + +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, + lambdaFunctionName, + subnets, + vpc + } = deploymentPipelineConfig; + + this._deployLogs = new CloudwatchLogGroup(this, `${id}-deploy-logs`, { + name: `/${lambdaFunctionName}/${environment}/deploy`, + retentionInDays: 180 + }); + + const roleNamePostfix = `-${environment}-deploy-role`; + const namePrefix = truncate(`${lambdaFunctionName}`, (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: 'lambda-deploy-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'lambda:UpdateFunctionCode' + ], + Resource: [ + `arn:aws:lambda:${region}:${accountId}:function:${lambdaFunctionName}` + ] + }] + }) + }, + { + 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 lambda function ${lambdaFunctionName}` + }); + + /** + * 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: `${lambdaFunctionName}-${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: 'LAMBDA_FUNCTION_NAME', + value: lambdaFunctionName + } + ] + }, + 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(`${lambdaFunctionName}-${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 ${lambdaFunctionName}-${environment}-deploy-rule` + }); + const onPublishRule = new CloudwatchEventRule(this, `${id}-deploy-pub-rule`, { + name: truncate(`${lambdaFunctionName}-${environment}-deploy-rule`, 64), + description: `Starts ${lambdaFunctionName}-${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/index.ts b/cdktf/src/constructs/index.ts new file mode 100644 index 0000000..2133730 --- /dev/null +++ b/cdktf/src/constructs/index.ts @@ -0,0 +1,3 @@ +export * from './application'; +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 new file mode 100644 index 0000000..9a32831 --- /dev/null +++ b/cdktf/src/constructs/rds-postgres.ts @@ -0,0 +1,198 @@ +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' + */ + 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', + 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, + 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/index.ts b/cdktf/src/index.ts new file mode 100644 index 0000000..d56efdf --- /dev/null +++ b/cdktf/src/index.ts @@ -0,0 +1,2 @@ +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 new file mode 100644 index 0000000..3d80728 --- /dev/null +++ b/cdktf/src/stacks/index.ts @@ -0,0 +1 @@ +export * from './main'; \ No newline at end of file diff --git a/cdktf/src/stacks/main.ts b/cdktf/src/stacks/main.ts new file mode 100644 index 0000000..da816f9 --- /dev/null +++ b/cdktf/src/stacks/main.ts @@ -0,0 +1,14 @@ +import { Construct } from "constructs"; +import { App, TerraformStack } from "cdktf"; + +class MyStack extends TerraformStack { + constructor(scope: Construct, id: string) { + super(scope, id); + + // define resources here + } +} + +const app = new App(); +new MyStack(app, "cdktf"); +app.synth(); diff --git a/cdktf/src/substacks/index.ts b/cdktf/src/substacks/index.ts new file mode 100644 index 0000000..3d80728 --- /dev/null +++ b/cdktf/src/substacks/index.ts @@ -0,0 +1 @@ +export * from './main'; \ No newline at end of file diff --git a/cdktf/src/substacks/main.ts b/cdktf/src/substacks/main.ts new file mode 100644 index 0000000..b40827f --- /dev/null +++ b/cdktf/src/substacks/main.ts @@ -0,0 +1,117 @@ +import { Construct } from 'constructs'; +import { + dataAwsCallerIdentity, + dataAwsRegion, + ecrRepository, + subnet, + vpc as vpcLib +} from '@cdktf/provider-aws'; +import { BaseConstructConfig, DefaultedBaseConstructConfig, OpenSearchConfig } from '../configs'; +import { + Application, + ApplicationConfig, + DeploymentPipeline, + DeploymentPipelineConfig as DeploymentPipelineConfigRequired, + RdsPostgres, + RdsPostgresConfig as RdsPostgresConfigRequired +} from '../constructs'; +import { truncate } from '../utils'; + +import AwsCallerIdentity = dataAwsCallerIdentity.DataAwsCallerIdentity; +import AwsRegion = dataAwsRegion.DataAwsRegion +import EcrRepository = ecrRepository.EcrRepository; +import Subnet = subnet.Subnet; +import Vpc = vpcLib.Vpc; + +export type RdsPostgresConfig = Omit +export type DeploymentPipelineConfig = Omit< + DeploymentPipelineConfigRequired, + 'ecrRepo' | + 'lambdaFunctionName' | + 'subnets' | + 'vpc' +> + +export type GuadrailsValidationServiceSubStackConfig = BaseConstructConfig & { + deploymentPipelineConfig: DeploymentPipelineConfig; + ecrRepo: EcrRepository; + openSearchConfig: OpenSearchConfig; + rdsPostgresConfig?: RdsPostgresConfig; + subnets: Subnet[]; + vpc: Vpc; +} + +export class GuadrailsValidationServiceSubStack extends Construct { + private _pgDatabase: RdsPostgres; + private _deploymentPipeline: DeploymentPipeline; + private _application: Application; + + constructor (scope: Construct, id: string, config: GuadrailsValidationServiceSubStackConfig) { + 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, + deploymentPipelineConfig, + environment, + ecrRepo, + openSearchConfig, + rdsPostgresConfig = {}, + region = defaultRegion, + subnets, + vpc + } = config; + + const baseConfig: DefaultedBaseConstructConfig = { + accountId, + environment, + region + }; + + const baseName = `${id}-guardrails-validation-service`; + const lambdaFunctionName = truncate(`${baseName}-api-${environment}`, 59); // Allow 5 characters for appending `-role` + const dbName = `${baseName}-postgres-db`; + + const deploymentPipelineConfigRequired: DeploymentPipelineConfigRequired = { + ...deploymentPipelineConfig, + ecrRepo, + lambdaFunctionName, + 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-db`, rdsPostgresConfigRequired) + + const applicationConfig: ApplicationConfig = { + ecrRepo, + lambdaFunctionName, + openSearchConfig, + rdsPostgres: this._pgDatabase, + vpc, + subnets + }; + this._application = new Application(this, `${id}-app`, baseConfig, applicationConfig); + } + + public get pgDatabase(): RdsPostgres { + return this._pgDatabase; + } + public get deploymentPipeline (): DeploymentPipeline { + return this._deploymentPipeline; + } + public get application (): Application { + return this._application; + } +} \ No newline at end of file diff --git a/cdktf/src/utils/index.ts b/cdktf/src/utils/index.ts new file mode 100644 index 0000000..32942a1 --- /dev/null +++ b/cdktf/src/utils/index.ts @@ -0,0 +1 @@ +export * from './truncate'; \ No newline at end of file diff --git a/cdktf/src/utils/truncate.ts b/cdktf/src/utils/truncate.ts new file mode 100644 index 0000000..b7ca935 --- /dev/null +++ b/cdktf/src/utils/truncate.ts @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..27659f5 --- /dev/null +++ b/cdktf/tsconfig.json @@ -0,0 +1,35 @@ +{ + "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/prod-run.sh b/prod-run.sh index 90ae185..257f73e 100644 --- a/prod-run.sh +++ b/prod-run.sh @@ -1,3 +1,8 @@ docker stop guardrails-api-prod || true docker rm guardrails-api-prod || true -docker run -p 8000:8000 --env-file local-prod.env --name guardrails-api-prod -it guardrails-api:prod \ No newline at end of file +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/requirements.txt b/requirements.txt index a099c04..dc33752 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,8 @@ backoff==2.2.1 black==23.3.0 blinker==1.6.2 boltons==23.0.0 +boto3==1.28.81 +botocore==1.31.81 build==0.10.0 CacheControl==0.12.14 certifi==2023.7.22 @@ -38,8 +40,6 @@ googleapis-common-protos==1.59.1 greenlet==2.0.2 griffe==0.29.1 grpcio==1.58.0 -# guard-rails-api-client @ file:///Users/calebcourier/Projects/guardrails-dev/guardrails-poc/guard-rails-api-client -# guardrails-ai @ file:///Users/calebcourier/Projects/guardrails-dev/guardrails gunicorn==20.1.0 h11==0.14.0 html5lib==1.1 @@ -135,6 +135,7 @@ requests-toolbelt==1.0.0 rich==13.4.2 rope==1.9.0 rpds-py==0.8.8 +s3transfer==0.7.0 shellingham==1.5.0.post1 six==1.16.0 sniffio==1.3.0 diff --git a/src/clients/postgres_client.py b/src/clients/postgres_client.py index a04b091..851f1c6 100644 --- a/src/clients/postgres_client.py +++ b/src/clients/postgres_client.py @@ -1,3 +1,4 @@ +import boto3 import os from flask import Flask from sqlalchemy import text @@ -11,10 +12,21 @@ def __new__(cls): if cls._instance is None: cls._instance = super(PostgresClient, cls).__new__(cls) return cls._instance + + def fetch_pg_secret(self, secret_arn: str) -> str: + client = boto3.client('secretsmanager') + response: dict = client.get_secret_value( + SecretId=secret_arn + ) + return response.get('SecretString') + def initialize(self, app: Flask): pg_user = os.environ.get("PGUSER", "postgres") - pg_password = os.environ.get("PGPASSWORD", "undefined") + pg_password = os.environ.get("PGPASSWORD") + pg_password_secret = os.environ.get("PGPASSWORD_SECRET_ARN") + if pg_password is None and pg_password_secret is not None: + pg_password = self.fetch_pg_secret(pg_password_secret) pg_host = os.environ.get("PGHOST", "localhost") pg_port = os.environ.get("PGPORT", "5432") pg_database = os.environ.get("PGDATABASE", "postgres") diff --git a/src/models/base.py b/src/models/base.py index 1076659..29f0169 100644 --- a/src/models/base.py +++ b/src/models/base.py @@ -4,4 +4,5 @@ INIT_EXTENSIONS = """ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "vector"; """ From 5667fa369fbcb76783618797759607c8a3283463 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 8 Nov 2023 15:56:52 -0600 Subject: [PATCH 11/31] output pem --- Dockerfile.prod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.prod b/Dockerfile.prod index f989edc..72bd325 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -22,7 +22,7 @@ COPY requirements.txt . RUN pip install ./guard-rails-api-client RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry -RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem +RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem # Install app dependencies RUN pip install -r requirements.txt From 6c30e93f4b516ad28c0c68a118003d559757652a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 9 Nov 2023 09:21:49 -0600 Subject: [PATCH 12/31] typo and stack --- cdktf/src/stacks/main.ts | 20 ++++++++++---------- cdktf/src/substacks/main.ts | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cdktf/src/stacks/main.ts b/cdktf/src/stacks/main.ts index da816f9..9aa0987 100644 --- a/cdktf/src/stacks/main.ts +++ b/cdktf/src/stacks/main.ts @@ -1,14 +1,14 @@ -import { Construct } from "constructs"; -import { App, TerraformStack } from "cdktf"; +import { Construct } from 'constructs'; +import { TerraformStack } from 'cdktf'; +import { GuardrailsValidationServiceSubStack, GuardrailsValidationServiceSubStackConfig } from '..'; -class MyStack extends TerraformStack { - constructor(scope: Construct, id: string) { +export type GuardrailsTelemetryServiceStackConfig = GuardrailsValidationServiceSubStackConfig; + +// This is for if we want to do a multi-stack deployment +export class GuardrailsValidationServiceStack extends TerraformStack { + constructor (scope: Construct, id: string, config: GuardrailsTelemetryServiceStackConfig) { super(scope, id); - // define resources here + new GuardrailsValidationServiceSubStack(this, `${id}-substack`, config); } -} - -const app = new App(); -new MyStack(app, "cdktf"); -app.synth(); +} \ No newline at end of file diff --git a/cdktf/src/substacks/main.ts b/cdktf/src/substacks/main.ts index b40827f..806c665 100644 --- a/cdktf/src/substacks/main.ts +++ b/cdktf/src/substacks/main.ts @@ -32,7 +32,7 @@ export type DeploymentPipelineConfig = Omit< 'vpc' > -export type GuadrailsValidationServiceSubStackConfig = BaseConstructConfig & { +export type GuardrailsValidationServiceSubStackConfig = BaseConstructConfig & { deploymentPipelineConfig: DeploymentPipelineConfig; ecrRepo: EcrRepository; openSearchConfig: OpenSearchConfig; @@ -41,12 +41,12 @@ export type GuadrailsValidationServiceSubStackConfig = BaseConstructConfig & { vpc: Vpc; } -export class GuadrailsValidationServiceSubStack extends Construct { +export class GuardrailsValidationServiceSubStack extends Construct { private _pgDatabase: RdsPostgres; private _deploymentPipeline: DeploymentPipeline; private _application: Application; - constructor (scope: Construct, id: string, config: GuadrailsValidationServiceSubStackConfig) { + constructor (scope: Construct, id: string, config: GuardrailsValidationServiceSubStackConfig) { super(scope, id); const currentIdentity = new AwsCallerIdentity(this, `${id}-aws-identity`); From ae368fdf9657234b1c9ec921dba8978ae605e05a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 9 Nov 2023 09:26:28 -0600 Subject: [PATCH 13/31] fix packagejson and description --- cdktf/package.json | 4 ++-- cdktf/src/constructs/application.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cdktf/package.json b/cdktf/package.json index 5ca83e2..a9a5a0c 100644 --- a/cdktf/package.json +++ b/cdktf/package.json @@ -1,8 +1,8 @@ { "name": "guardrails-validation-cdktf", "version": "1.0.0", - "main": "main.js", - "types": "main.ts", + "main": "index.ts", + "types": "index.ts", "license": "MPL-2.0", "private": true, "scripts": { diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index a910d6b..26dbbe7 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -150,7 +150,7 @@ export class Application extends Construct { this._lambdaFunction = new LambdaFunction(this, `${id}-lambda-function`, { functionName: lambdaFunctionName, - description: 'Guardrails Telemetry Service API', + description: 'Guardrails Validation Service API', role: this.lambdaRole.arn, architectures: ['arm64'], environment: { From 8da2e255873e7ff7ea69b3c3516b25ccd0338224 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 9 Nov 2023 09:57:59 -0600 Subject: [PATCH 14/31] fix name and instance --- cdktf/src/constructs/rds-postgres.ts | 5 +++-- cdktf/src/substacks/main.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cdktf/src/constructs/rds-postgres.ts b/cdktf/src/constructs/rds-postgres.ts index 9a32831..6d94709 100644 --- a/cdktf/src/constructs/rds-postgres.ts +++ b/cdktf/src/constructs/rds-postgres.ts @@ -54,7 +54,7 @@ export type RdsPostgresConfig = { */ engineVersion?: string; /** - * @default 'db.t4g' + * @default 'db.t4g.micro' */ instanceClass?: string; /** @@ -110,7 +110,7 @@ export class RdsPostgres extends Construct { caCertIdentifier = 'rds-ca-rsa4096-g1', engineVersion = '15.4', name: identifier, - instanceClass = 'db.t4g', + instanceClass = 'db.t4g.micro', maintenanceWindow = 'Sun:10:00-Sun:13:00', multiAz, port = 5432, @@ -174,6 +174,7 @@ export class RdsPostgres extends Construct { multiAz, port, publiclyAccessible: false, + username: 'postgres', skipFinalSnapshot: true, storageEncrypted: true, storageType, diff --git a/cdktf/src/substacks/main.ts b/cdktf/src/substacks/main.ts index 806c665..35f7cf6 100644 --- a/cdktf/src/substacks/main.ts +++ b/cdktf/src/substacks/main.ts @@ -74,7 +74,7 @@ export class GuardrailsValidationServiceSubStack extends Construct { const baseName = `${id}-guardrails-validation-service`; const lambdaFunctionName = truncate(`${baseName}-api-${environment}`, 59); // Allow 5 characters for appending `-role` - const dbName = `${baseName}-postgres-db`; + const dbName = truncate(`${baseName}-postgres-db`, 59); // 63 -sg const deploymentPipelineConfigRequired: DeploymentPipelineConfigRequired = { ...deploymentPipelineConfig, @@ -92,7 +92,7 @@ export class GuardrailsValidationServiceSubStack extends Construct { this._deploymentPipeline = new DeploymentPipeline(this, `${id}-ci-cd`, baseConfig, deploymentPipelineConfigRequired); - this._pgDatabase = new RdsPostgres(this, `${id}-pg-db`, rdsPostgresConfigRequired) + this._pgDatabase = new RdsPostgres(this, `${id}-pg`, rdsPostgresConfigRequired) const applicationConfig: ApplicationConfig = { ecrRepo, From 1476bfd184b92c809fd7c741f66cae368a634699 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 9 Nov 2023 11:05:31 -0600 Subject: [PATCH 15/31] secrets, certs, etc. --- src/clients/postgres_client.py | 42 +++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/clients/postgres_client.py b/src/clients/postgres_client.py index 851f1c6..472ca36 100644 --- a/src/clients/postgres_client.py +++ b/src/clients/postgres_client.py @@ -1,7 +1,9 @@ import boto3 +import json import os from flask import Flask from sqlalchemy import text +from typing import Tuple from src.models.base import db, INIT_EXTENSIONS @@ -13,25 +15,49 @@ def __new__(cls): cls._instance = super(PostgresClient, cls).__new__(cls) return cls._instance - def fetch_pg_secret(self, secret_arn: str) -> str: + def fetch_pg_secret(self, secret_arn: str) -> dict: client = boto3.client('secretsmanager') response: dict = client.get_secret_value( SecretId=secret_arn ) - return response.get('SecretString') + secret_string = response.get('SecretString') + try: + secret = json.loads(secret_string) + return secret + except Exception: + pass + + def get_pg_creds(self) -> Tuple[str, str]: + pg_user = None + pg_password = None + pg_password_secret = os.environ.get("PGPASSWORD_SECRET_ARN") + if pg_password_secret is not None: + pg_secret = self.fetch_pg_secret(pg_password_secret) or {} + pg_user = pg_secret.get('username') + pg_password = pg_secret.get('password') + + pg_user = pg_user or os.environ.get("PGUSER", "postgres") + pg_password = pg_password or os.environ.get("PGPASSWORD") + return pg_user, pg_password def initialize(self, app: Flask): - pg_user = os.environ.get("PGUSER", "postgres") - pg_password = os.environ.get("PGPASSWORD") - pg_password_secret = os.environ.get("PGPASSWORD_SECRET_ARN") - if pg_password is None and pg_password_secret is not None: - pg_password = self.fetch_pg_secret(pg_password_secret) + pg_user, pg_password = self.get_pg_creds() pg_host = os.environ.get("PGHOST", "localhost") pg_port = os.environ.get("PGPORT", "5432") pg_database = os.environ.get("PGDATABASE", "postgres") - conf = f"postgresql://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_database}" + pg_endpoint = ( + pg_host + if pg_host.endswith(f":{pg_port}") # FIXME: This is a cheap check; maybe use a regex instead? + else f"{pg_host}:{pg_port}" + ) + + conf = f"postgresql://{pg_user}:{pg_password}@{pg_endpoint}/{pg_database}" + + if os.environ.get("NODE_ENV") == "production": + conf = f"{conf}?sslmode=verify-ca&sslrootcert=global-bundle.pem" + app.config["SQLALCHEMY_DATABASE_URI"] = conf app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False From 013eb81f96382d55d67945626d47fc11454e932e Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 9 Nov 2023 13:38:28 -0600 Subject: [PATCH 16/31] add lambda adapter --- Dockerfile.prod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile.prod b/Dockerfile.prod index 72bd325..1aff5e6 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,5 +1,7 @@ FROM public.ecr.aws/docker/library/python:3.11-slim +COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter + # Create app directory WORKDIR /app From 79898c180bb4d49a84152e09b5400523a73b2ff0 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Mon, 13 Nov 2023 10:53:29 -0600 Subject: [PATCH 17/31] get collector working as extension --- .gitignore | 3 +- Dockerfile.prod | 4 +- buildspecs/build.sh | 12 +++++- cdktf/src/constructs/application.ts | 8 +++- configs/lambda-collector-config.yml | 57 +++++++++++++++++++++++++++++ prod-build.sh | 18 +++++++-- src/modules/otel_tracer/__init__.py | 11 +++++- 7 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 configs/lambda-collector-config.yml diff --git a/.gitignore b/.gitignore index 7612491..dbd20b4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ install*.sh guardrails-custom-validators guardrails-api-client guardrails -*.env \ No newline at end of file +*.env +opentelemetry-lambda-layer \ No newline at end of file diff --git a/Dockerfile.prod b/Dockerfile.prod index 1aff5e6..37f7011 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,6 +1,7 @@ FROM public.ecr.aws/docker/library/python:3.11-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 @@ -13,10 +14,11 @@ RUN python3 -m venv /opt/venv # Enable venv ENV PATH="/opt/venv/bin:$PATH" -# Install git +# Install git and curl RUN apt-get update RUN apt-get install -y git curl +# Copy pre-built api client COPY ./guard-rails-api-client ./guard-rails-api-client # Copy the requirements file diff --git a/buildspecs/build.sh b/buildspecs/build.sh index 61ca6db..dcd54dc 100644 --- a/buildspecs/build.sh +++ b/buildspecs/build.sh @@ -14,6 +14,15 @@ ecrImageUrl="${ecrEndpoint}/${repoName}"; 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; @@ -26,8 +35,7 @@ docker buildx build \ || exit 1; docker image tag "$imageName:$commitSha" "$ecrImageUrl:$commitSha"; -docker image tag "$imageName:$version" "$ecrImageUrl:$version"; docker image tag "$imageName:latest" "$ecrImageUrl:latest"; -echo "Publishing to ECR" +# echo "Publishing to ECR" docker push $ecrImageUrl --all-tags; \ No newline at end of file diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index 26dbbe7..07d75f0 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -166,10 +166,14 @@ export class Application extends Construct { OPENSEARCH_URL: opensearchDomain.endpoint, OTEL_SERVICE_NAME: 'guardrails-api', // OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4317', - OTEL_TRACES_EXPORTER: 'console', //'otlp', + OTEL_TRACES_EXPORTER: 'none', //'otlp', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', - OTEL_METRICS_EXPORTER: 'console', //'otlp', + OTEL_METRICS_EXPORTER: 'none', //'otlp', + OTEL_TRACE_SINK: '', + OTEL_METRIC_SINK: '', + OTEL_LOG_SINK: '', + // OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'configs/lambda-collector-config.yml' PGPORT: rdsPostgres.instance.port.toString(), PGDATABASE: rdsPostgres.instance.dbName, PGHOST: rdsPostgres.instance.endpoint, diff --git a/configs/lambda-collector-config.yml b/configs/lambda-collector-config.yml new file mode 100644 index 0000000..b398052 --- /dev/null +++ b/configs/lambda-collector-config.yml @@ -0,0 +1,57 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: 'localhost:4317' + http: + endpoint: 'localhost:4318' + +exporters: + logging: + loglevel: ${env:OTEL_PYTHON_LOG_LEVEL} + otlp/traces: + endpoint: ${env:OTEL_TRACE_SINK} + tls: + insecure: true + insecure_skip_verify: true + otlp/metrics: + endpoint: ${env:OTEL_METRIC_SINK} + tls: + insecure: true + insecure_skip_verify: true + otlp/logs: + endpoint: ${env:OTEL_LOG_SINK} + tls: + insecure: true + insecure_skip_verify: true + +service: + pipelines: + traces: + receivers: + - otlp + # processors: + # - batch + exporters: + - logging + # - otlp/traces + metrics: + receivers: + - otlp + # processors: + # - batch + exporters: + - logging + # - otlp/metrics + # - prometheus + logs: + receivers: + - otlp + # processors: + # - batch + exporters: + - logging + # - otlp/logs + telemetry: + logs: + level: info \ No newline at end of file diff --git a/prod-build.sh b/prod-build.sh index cef7794..7736733 100644 --- a/prod-build.sh +++ b/prod-build.sh @@ -1,6 +1,18 @@ +# Setup unpublished api client +echo "Building api client..." +# Speed things up for local testing +# bash build-sdk.sh + +# 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 \ - --no-cache \ - --build-arg CACHEBUST="$(date)" \ - -t "guardrails-api:prod" .; \ No newline at end of file + -t "guardrails-api:prod" .; + # --no-cache \ + # --build-arg CACHEBUST="$(date)" \ \ No newline at end of file diff --git a/src/modules/otel_tracer/__init__.py b/src/modules/otel_tracer/__init__.py index 37ed131..354ec13 100644 --- a/src/modules/otel_tracer/__init__.py +++ b/src/modules/otel_tracer/__init__.py @@ -5,9 +5,16 @@ # configure -OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://otel-collector:4317') +OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://localhost:4317') # 'http://otel-collector:4317') +OTEL_TRACE_SINK = os.environ.get('OTEL_TRACE_SINK') +endpoint = OTEL_TRACE_SINK or OTEL_EXPORTER_OTLP_ENDPOINT +print("OTEL_EXPORTER_OTLP_ENDPOINT: ", OTEL_EXPORTER_OTLP_ENDPOINT) +print("OTEL_TRACE_SINK: ", OTEL_TRACE_SINK) +print("endpoint: ", endpoint) +insecure = not endpoint.startswith('https://') provider = trace.get_tracer_provider() -processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=OTEL_EXPORTER_OTLP_ENDPOINT, insecure=True)) +processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint, insecure=insecure)) +print("exporter endpoint: ", processor.span_exporter._endpoint) provider.add_span_processor(processor) service_name = os.environ.get('OTEL_SERVICE_NAME', 'guardrails-api') From 4f9e6ec6559a6d95f68479cfedafc85cda13cc39 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Mon, 13 Nov 2023 16:36:21 -0600 Subject: [PATCH 18/31] ingestion pipeline --- cdktf/src/configs/open-search-config.ts | 1 + cdktf/src/constructs/application.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cdktf/src/configs/open-search-config.ts b/cdktf/src/configs/open-search-config.ts index aa2185a..88b6f81 100644 --- a/cdktf/src/configs/open-search-config.ts +++ b/cdktf/src/configs/open-search-config.ts @@ -9,4 +9,5 @@ import SecretsmanagerSecret = secretsmanagerSecretLib.SecretsmanagerSecret; export type OpenSearchConfig = { opensearchDomain: OpensearchDomain; credentials: SecretsmanagerSecret; + ingestionPipelineEndpoint: string; } \ No newline at end of file diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index 07d75f0..ba01ea1 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -57,7 +57,8 @@ export class Application extends Construct { const { credentials: openSearchClusterCredentials, - opensearchDomain + opensearchDomain, + ingestionPipelineEndpoint } = openSearchConfig; this._lambdaLogs = new CloudwatchLogGroup(this, `${id}-lambda-logs`, { @@ -170,10 +171,10 @@ export class Application extends Construct { OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', OTEL_METRICS_EXPORTER: 'none', //'otlp', - OTEL_TRACE_SINK: '', - OTEL_METRIC_SINK: '', - OTEL_LOG_SINK: '', - // OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'configs/lambda-collector-config.yml' + OTEL_TRACE_SINK: `${ingestionPipelineEndpoint}:21890`, + OTEL_METRIC_SINK: `${ingestionPipelineEndpoint}:21891`, + OTEL_LOG_SINK: `${ingestionPipelineEndpoint}:21892`, + OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'app/configs/lambda-collector-config.yml', PGPORT: rdsPostgres.instance.port.toString(), PGDATABASE: rdsPostgres.instance.dbName, PGHOST: rdsPostgres.instance.endpoint, From 9bf727ca2448f246a2a816457bdb0a4117e68714 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 14 Nov 2023 11:55:50 -0600 Subject: [PATCH 19/31] remove jsonified properties from xml dict --- src/classes/schema_element_struct.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/classes/schema_element_struct.py b/src/classes/schema_element_struct.py index d44fa08..1626669 100644 --- a/src/classes/schema_element_struct.py +++ b/src/classes/schema_element_struct.py @@ -42,6 +42,9 @@ def to_element(self) -> ElementStub: elem_dict[ validator_on_fail.get("validatorTag") ] = validator_on_fail.get("method") + elem_dict.pop('date_format', None) + elem_dict.pop('time_format', None) + elem_dict.pop('on_fails', None) return ElementStub(self.type, elem_dict) @classmethod From cbac9fa768879b4a9de8e20fb8cd022b989a042a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 14 Nov 2023 11:57:55 -0600 Subject: [PATCH 20/31] finish pipeline --- cdktf/src/configs/open-search-config.ts | 13 ++++- cdktf/src/constructs/application.ts | 66 ++++++++++--------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/cdktf/src/configs/open-search-config.ts b/cdktf/src/configs/open-search-config.ts index 88b6f81..6b6faac 100644 --- a/cdktf/src/configs/open-search-config.ts +++ b/cdktf/src/configs/open-search-config.ts @@ -9,5 +9,16 @@ import SecretsmanagerSecret = secretsmanagerSecretLib.SecretsmanagerSecret; export type OpenSearchConfig = { opensearchDomain: OpensearchDomain; credentials: SecretsmanagerSecret; - ingestionPipelineEndpoint: string; + 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/application.ts b/cdktf/src/constructs/application.ts index ba01ea1..4a78155 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -2,11 +2,9 @@ import { Construct } from 'constructs'; import { cloudwatchLogGroup as cloudwatchLogGroupLib, ecrRepository, - dataAwsIamPolicyDocument, iamRole, lambdaFunction as lambdaFunctionLib, lambdaFunctionUrl as lambdaFunctionUrlLib, - opensearchDomainPolicy as opensearchDomainPolicyLib, subnet, vpc as vpcLib } from '@cdktf/provider-aws'; @@ -14,11 +12,9 @@ import { DefaultedBaseConstructConfig, OpenSearchConfig } from '../configs'; import CloudwatchLogGroup = cloudwatchLogGroupLib.CloudwatchLogGroup; import EcrRepository = ecrRepository.EcrRepository; -import DataAwsIamPolicyDocument = dataAwsIamPolicyDocument.DataAwsIamPolicyDocument; import IamRole = iamRole.IamRole; import LambdaFunction = lambdaFunctionLib.LambdaFunction; import LambdaFunctionUrl = lambdaFunctionUrlLib.LambdaFunctionUrl; -import OpensearchDomainPolicy = opensearchDomainPolicyLib.OpensearchDomainPolicy; import Subnet = subnet.Subnet; import Vpc = vpcLib.Vpc; import { RdsPostgres } from './rds-postgres'; @@ -58,7 +54,9 @@ export class Application extends Construct { const { credentials: openSearchClusterCredentials, opensearchDomain, - ingestionPipelineEndpoint + traceIngestionPipeline, + logIngestionPipeline, + metricIngestionPipeline } = openSearchConfig; this._lambdaLogs = new CloudwatchLogGroup(this, `${id}-lambda-logs`, { @@ -145,6 +143,23 @@ export class Application extends Construct { ] }] }) + }, + { + name: 'ingestion-access', + policy: JSON.stringify({ + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Action: ['osis:Ingest'], + Resource: [ + traceIngestionPipeline.arn, + metricIngestionPipeline.arn, + logIngestionPipeline.arn + ] + } + ] + }) } ] }); @@ -160,20 +175,16 @@ export class Application extends Construct { AWS_LWA_READINESS_CHECK_PORT: '8000', LOGLEVEL: 'INFO', NODE_ENV: 'production', - // TODO: enable otlp via lambda extension: - // https://opentelemetry.io/docs/faas/lambda-collector/ - // https://aws.amazon.com/blogs/compute/working-with-lambda-layers-and-extensions-in-container-images/ OPENSEARCH_SECRET: openSearchClusterCredentials.arn, OPENSEARCH_URL: opensearchDomain.endpoint, OTEL_SERVICE_NAME: 'guardrails-api', - // OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4317', - OTEL_TRACES_EXPORTER: 'none', //'otlp', + OTEL_TRACES_EXPORTER: 'otlp', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', - OTEL_METRICS_EXPORTER: 'none', //'otlp', - OTEL_TRACE_SINK: `${ingestionPipelineEndpoint}:21890`, - OTEL_METRIC_SINK: `${ingestionPipelineEndpoint}:21891`, - OTEL_LOG_SINK: `${ingestionPipelineEndpoint}:21892`, + OTEL_METRICS_EXPORTER: 'otlp', + OTEL_TRACE_SINK: `https://${traceIngestionPipeline.endpoint}/traces/ingest`, + OTEL_METRIC_SINK: `https://${metricIngestionPipeline.endpoint}/metrics/ingest`, + OTEL_LOG_SINK: `https://${logIngestionPipeline.endpoint}/logs/ingest`, OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'app/configs/lambda-collector-config.yml', PGPORT: rdsPostgres.instance.port.toString(), PGDATABASE: rdsPostgres.instance.dbName, @@ -199,33 +210,6 @@ export class Application extends Construct { authorizationType: 'NONE' }); this._endpoint = this.lambdaUrl.functionUrl; - - // This needs to happen here because since the lambda role will be a principal, it must exist before creating the below policy. - // We make this depend on the lambda url to force it to launch last. - const policyDocument = new DataAwsIamPolicyDocument(this, `${id}-domain-policy-document`, { - statement: [ - { - effect: 'Allow', - principals: [ - { - type: 'AWS', - identifiers: [this.lambdaRole.arn] - } - ], - actions: ['es:ESHttp*'], - resources: [ - opensearchDomain.arn, - `${opensearchDomain.arn}/*` - ] - } - ], - dependsOn: [this.lambdaRole, opensearchDomain, this.lambdaUrl] - }); - new OpensearchDomainPolicy(this, `${id}-domain-policy`, { - domainName: opensearchDomain.domainName, - accessPolicies: policyDocument.json, - dependsOn: [this.lambdaRole, opensearchDomain, this.lambdaUrl] - }); } public get endpoint (): string { From 8dcb7ffb830932cd27a3dcea5aeb7c91067848b6 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 14 Nov 2023 14:40:59 -0600 Subject: [PATCH 21/31] add port, enable otlp exporters --- cdktf/src/constructs/application.ts | 8 ++++---- configs/lambda-collector-config.yml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index 4a78155..f218fbd 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -181,10 +181,10 @@ export class Application extends Construct { OTEL_TRACES_EXPORTER: 'otlp', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', - OTEL_METRICS_EXPORTER: 'otlp', - OTEL_TRACE_SINK: `https://${traceIngestionPipeline.endpoint}/traces/ingest`, - OTEL_METRIC_SINK: `https://${metricIngestionPipeline.endpoint}/metrics/ingest`, - OTEL_LOG_SINK: `https://${logIngestionPipeline.endpoint}/logs/ingest`, + OTEL_METRICS_EXPORTER: 'none', + OTEL_TRACE_SINK: `https://${traceIngestionPipeline.endpoint}:443/traces/ingest`, + OTEL_METRIC_SINK: `https://${metricIngestionPipeline.endpoint}:443/metrics/ingest`, + OTEL_LOG_SINK: `https://${logIngestionPipeline.endpoint}:443/logs/ingest`, OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'app/configs/lambda-collector-config.yml', PGPORT: rdsPostgres.instance.port.toString(), PGDATABASE: rdsPostgres.instance.dbName, diff --git a/configs/lambda-collector-config.yml b/configs/lambda-collector-config.yml index b398052..832f786 100644 --- a/configs/lambda-collector-config.yml +++ b/configs/lambda-collector-config.yml @@ -34,7 +34,7 @@ service: # - batch exporters: - logging - # - otlp/traces + - otlp/traces metrics: receivers: - otlp @@ -42,7 +42,7 @@ service: # - batch exporters: - logging - # - otlp/metrics + - otlp/metrics # - prometheus logs: receivers: @@ -51,7 +51,7 @@ service: # - batch exporters: - logging - # - otlp/logs + - otlp/logs telemetry: logs: level: info \ No newline at end of file From bb5ce9e06b27d3f01acc293b79856183310602c0 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 14 Nov 2023 16:19:46 -0600 Subject: [PATCH 22/31] remove ports, use aws provided config --- cdktf/src/constructs/application.ts | 6 ++-- configs/lambda-collector-config.yml | 52 ++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index f218fbd..0b8cbb9 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -182,9 +182,9 @@ export class Application extends Construct { OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: 'Accept-Encoding,User-Agent,Referer', OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: 'Last-Modified,Content-Type', OTEL_METRICS_EXPORTER: 'none', - OTEL_TRACE_SINK: `https://${traceIngestionPipeline.endpoint}:443/traces/ingest`, - OTEL_METRIC_SINK: `https://${metricIngestionPipeline.endpoint}:443/metrics/ingest`, - OTEL_LOG_SINK: `https://${logIngestionPipeline.endpoint}:443/logs/ingest`, + OTEL_TRACE_SINK: `https://${traceIngestionPipeline.endpoint}/traces/ingest`, + OTEL_METRIC_SINK: `https://${metricIngestionPipeline.endpoint}/metrics/ingest`, + OTEL_LOG_SINK: `https://${logIngestionPipeline.endpoint}/logs/ingest`, OPENTELEMETRY_COLLECTOR_CONFIG_FILE: 'app/configs/lambda-collector-config.yml', PGPORT: rdsPostgres.instance.port.toString(), PGDATABASE: rdsPostgres.instance.dbName, diff --git a/configs/lambda-collector-config.yml b/configs/lambda-collector-config.yml index 832f786..623b4d5 100644 --- a/configs/lambda-collector-config.yml +++ b/configs/lambda-collector-config.yml @@ -1,3 +1,11 @@ +# Based on https://docs.aws.amazon.com/opensearch-service/latest/developerguide/configure-client-otel.html +# Still had to add tls block +# Not tested for metrics or logs +extensions: + sigv4auth: + region: ${env:AWS_REGION} + service: "osis" + receivers: otlp: protocols: @@ -9,23 +17,32 @@ receivers: exporters: logging: loglevel: ${env:OTEL_PYTHON_LOG_LEVEL} - otlp/traces: - endpoint: ${env:OTEL_TRACE_SINK} - tls: - insecure: true - insecure_skip_verify: true - otlp/metrics: - endpoint: ${env:OTEL_METRIC_SINK} - tls: - insecure: true - insecure_skip_verify: true - otlp/logs: - endpoint: ${env:OTEL_LOG_SINK} + otlphttp: + traces_endpoint: ${env:OTEL_TRACE_SINK} + auth: + authenticator: sigv4auth + compression: none tls: - insecure: true + insecure: false insecure_skip_verify: true + # otlp/traces: + # endpoint: ${env:OTEL_TRACE_SINK} + # tls: + # insecure: true + # insecure_skip_verify: true + # otlp/metrics: + # endpoint: ${env:OTEL_METRIC_SINK} + # tls: + # insecure: true + # insecure_skip_verify: true + # otlp/logs: + # endpoint: ${env:OTEL_LOG_SINK} + # tls: + # insecure: true + # insecure_skip_verify: true service: + extensions: [sigv4auth] pipelines: traces: receivers: @@ -34,7 +51,8 @@ service: # - batch exporters: - logging - - otlp/traces + - otlphttp + # - otlp/traces metrics: receivers: - otlp @@ -42,7 +60,8 @@ service: # - batch exporters: - logging - - otlp/metrics + # - otlphttp + # - otlp/metrics # - prometheus logs: receivers: @@ -51,7 +70,8 @@ service: # - batch exporters: - logging - - otlp/logs + # - otlphttp + # - otlp/logs telemetry: logs: level: info \ No newline at end of file From 1d324b54241e038bc632994fb1d5fb7638dbc64f Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 14 Nov 2023 16:20:47 -0600 Subject: [PATCH 23/31] note on exporter --- configs/lambda-collector-config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/lambda-collector-config.yml b/configs/lambda-collector-config.yml index 623b4d5..67a16a1 100644 --- a/configs/lambda-collector-config.yml +++ b/configs/lambda-collector-config.yml @@ -17,6 +17,8 @@ receivers: exporters: logging: loglevel: ${env:OTEL_PYTHON_LOG_LEVEL} + # AWS OpenSearch Ingestion Pipeline forces you to use a path + # Because of that, you can't use the standard otlp exporter because it's grpc based otlphttp: traces_endpoint: ${env:OTEL_TRACE_SINK} auth: From 060bacd0c75b4c7aea430369cab84067aee6f916 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 15 Nov 2023 11:43:47 -0600 Subject: [PATCH 24/31] re-use auto-instr exporters --- configs/lambda-collector-config.yml | 6 ------ src/modules/otel_logger/__init__.py | 2 ++ src/modules/otel_meter/__init__.py | 2 ++ src/modules/otel_tracer/__init__.py | 21 +++++++++++++++------ 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/configs/lambda-collector-config.yml b/configs/lambda-collector-config.yml index 67a16a1..3581ce2 100644 --- a/configs/lambda-collector-config.yml +++ b/configs/lambda-collector-config.yml @@ -49,8 +49,6 @@ service: traces: receivers: - otlp - # processors: - # - batch exporters: - logging - otlphttp @@ -58,8 +56,6 @@ service: metrics: receivers: - otlp - # processors: - # - batch exporters: - logging # - otlphttp @@ -68,8 +64,6 @@ service: logs: receivers: - otlp - # processors: - # - batch exporters: - logging # - otlphttp diff --git a/src/modules/otel_logger/__init__.py b/src/modules/otel_logger/__init__.py index e13d5f8..5aa7854 100644 --- a/src/modules/otel_logger/__init__.py +++ b/src/modules/otel_logger/__init__.py @@ -1,3 +1,5 @@ +# !!! UPDATE ME !!! +# See Tracer setup import os import logging import opentelemetry._logs as logs diff --git a/src/modules/otel_meter/__init__.py b/src/modules/otel_meter/__init__.py index 2981135..a6468e5 100644 --- a/src/modules/otel_meter/__init__.py +++ b/src/modules/otel_meter/__init__.py @@ -1,3 +1,5 @@ +# !!! UPDATE ME !!! +# See Tracer setup import os from opentelemetry import metrics diff --git a/src/modules/otel_tracer/__init__.py b/src/modules/otel_tracer/__init__.py index 354ec13..d5a0b3a 100644 --- a/src/modules/otel_tracer/__init__.py +++ b/src/modules/otel_tracer/__init__.py @@ -1,8 +1,7 @@ import os from opentelemetry import trace -from opentelemetry.sdk.trace.export import BatchSpanProcessor -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter - +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as GrpcExporter # configure OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://localhost:4317') # 'http://otel-collector:4317') @@ -13,9 +12,19 @@ print("endpoint: ", endpoint) insecure = not endpoint.startswith('https://') provider = trace.get_tracer_provider() -processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint, insecure=insecure)) -print("exporter endpoint: ", processor.span_exporter._endpoint) -provider.add_span_processor(processor) + +span_processors = provider._active_span_processor._span_processors +span_processor = span_processors[0] +span_exporter = ( + span_processor.span_exporter + if span_processor is not None + else GrpcExporter() +) + +simple_processor = SimpleSpanProcessor(span_exporter) +is_lambda = os.environ.get('AWS_EXECUTION_ENV', '').startswith('AWS_Lambda_') +if is_lambda or not span_processor: + provider._active_span_processor._span_processors = (simple_processor,) service_name = os.environ.get('OTEL_SERVICE_NAME', 'guardrails-api') otel_tracer = trace.get_tracer(service_name) \ No newline at end of file From 16bfb44415ec73e3cf937d6832f7403aa4264a0c Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 15 Nov 2023 17:22:48 -0600 Subject: [PATCH 25/31] fix result --- .dockerignore | 2 +- .gitignore | 2 +- Dockerfile.dev | 6 ++- Dockerfile.prod | 4 +- buildspecs/build.sh | 3 ++ local.sh | 18 +++++++ requirements.txt | 103 +++++++++++++++++++++++++++++++++++++-- src/blueprints/guards.py | 6 ++- 8 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 local.sh diff --git a/.dockerignore b/.dockerignore index 7981043..9c0fe02 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,4 @@ -.venv +.venv* diagrams .github buildspecs diff --git a/.gitignore b/.gitignore index dbd20b4..897e60e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ **/__pycache__ -.venv +.venv* postgres venv guardrails-sdk diff --git a/Dockerfile.dev b/Dockerfile.dev index 29c9cde..3b19bc0 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM public.ecr.aws/docker/library/python:3.11-slim +FROM public.ecr.aws/docker/library/python:3.11.5-slim # Create app directory WORKDIR /app @@ -11,6 +11,10 @@ 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 gcc + COPY ./guardrails-sdk ./guardrails-sdk COPY ./guard-rails-api-client ./guard-rails-api-client diff --git a/Dockerfile.prod b/Dockerfile.prod index 37f7011..7d082cc 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,4 +1,4 @@ -FROM public.ecr.aws/docker/library/python:3.11-slim +FROM public.ecr.aws/docker/library/python:3.11.5-slim COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.7.1 /lambda-adapter /opt/extensions/lambda-adapter COPY ./opentelemetry-lambda-layer /opt @@ -16,7 +16,7 @@ ENV PATH="/opt/venv/bin:$PATH" # Install git and curl RUN apt-get update -RUN apt-get install -y git curl +RUN apt-get install -y git curl gcc # Copy pre-built api client COPY ./guard-rails-api-client ./guard-rails-api-client diff --git a/buildspecs/build.sh b/buildspecs/build.sh index dcd54dc..9394b83 100644 --- a/buildspecs/build.sh +++ b/buildspecs/build.sh @@ -29,10 +29,13 @@ docker login -u AWS -p $(aws ecr get-login-password --region $region) $ecrEndpoi docker buildx build \ --platform linux/arm64 \ --progress plain \ + --no-cache \ + --build-arg CACHEBUST="$(date)" \ -f Dockerfile.prod \ -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"; diff --git a/local.sh b/local.sh new file mode 100644 index 0000000..4d22c97 --- /dev/null +++ b/local.sh @@ -0,0 +1,18 @@ +export APP_ENVIRONMENT=local +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 OTEL_SERVICE_NAME=guardrails-api +export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 +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=otlp #,console +export PYTHONUNBUFFERED=1 +export LOGLEVEL=INFO +export GUARDRAILS_PROCESS_COUNT=1 +opentelemetry-instrument gunicorn --bind 0.0.0.0:8000 --timeout=5 --threads=10 "app:create_app()" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index dc33752..50db011 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,74 +1,131 @@ aiohttp==3.8.5 aiosignal==1.3.1 +alt-profanity-check==1.3.1 anyio==3.7.1 +appnope==0.1.3 astroid==2.15.6 +asttokens==2.4.0 async-timeout==4.0.2 attrs==23.1.0 autoflake==2.2.0 +Babel==2.13.0 +backcall==0.2.0 backoff==2.2.1 +beautifulsoup4==4.12.2 black==23.3.0 +bleach==6.0.0 blinker==1.6.2 boltons==23.0.0 boto3==1.28.81 botocore==1.31.81 build==0.10.0 CacheControl==0.12.14 +cairocffi==1.6.1 +CairoSVG==2.7.1 certifi==2023.7.22 cffi==1.15.1 +cfgv==3.4.0 charset-normalizer==3.1.0 -cleo==2.0.1 +cleo==2.1.0 click==8.1.3 colorama==0.4.6 colored==2.2.2 +comm==0.1.4 coverage==7.2.7 crashtest==0.4.1 +cssselect==1.2.0 +cssselect2==0.7.0 +debugpy==1.8.0 +decorator==5.1.1 +defusedxml==0.7.1 Deprecated==1.2.14 +detect-secrets==1.4.0 dill==0.3.6 distlib==0.3.6 +docformatter==1.7.5 +docutils==0.20.1 dulwich==0.21.5 eliot==1.14.0 eliot-tree==21.0.0 exceptiongroup==1.1.2 +executing==2.0.0 faiss-cpu==1.7.4 +fastjsonschema==2.18.1 filelock==3.12.2 flake8==6.0.0 Flask==2.3.2 Flask-Cors==4.0.0 Flask-SQLAlchemy==3.0.5 frozenlist==1.3.3 +ghp-import==2.1.0 +gitdb==4.0.10 +GitPython==3.1.37 googleapis-common-protos==1.59.1 greenlet==2.0.2 -griffe==0.29.1 +griffe==0.38.0 grpcio==1.58.0 gunicorn==20.1.0 h11==0.14.0 html5lib==1.1 httpcore==0.17.3 httpx==0.24.1 +identify==2.5.30 idna==3.4 importlib-metadata==6.0.1 iniconfig==2.0.0 +inspiredco==0.0.2 installer==0.7.0 +ipykernel==6.25.2 +ipython==8.16.1 iso8601==2.0.0 isort==5.12.0 itsdangerous==2.1.2 jaraco.classes==3.2.3 +jedi==0.19.1 Jinja2==3.1.2 jmespath==1.0.1 +joblib==1.3.2 jsonschema==4.18.0 jsonschema-specifications==2023.6.1 +jupyter_client==8.3.1 +jupyter_core==5.3.2 +jupyterlab-pygments==0.2.2 +jupytext==1.15.2 keyring==23.13.1 lazy-object-proxy==1.9.0 lockfile==0.12.2 lxml==4.9.2 +manifest-ml==0.1.8 +Markdown==3.4.4 markdown-it-py==3.0.0 +markdown2==2.4.10 MarkupSafe==2.1.3 +matplotlib-inline==0.1.6 mccabe==0.7.0 +mdit-py-plugins==0.4.0 mdurl==0.1.2 +mergedeep==1.3.4 +mistune==3.0.2 +mkdocs==1.5.3 +mkdocs-autorefs==0.5.0 +mkdocs-glightbox==0.3.4 +mkdocs-jupyter==0.24.5 +mkdocs-material==9.2.6 +mkdocs-material-extensions==1.2 +mkdocstrings==0.23.0 +mkdocstrings-python==1.7.1 +mknotebooks==0.8.0 more-itertools==9.1.0 msgpack==1.0.5 multidict==6.0.4 mypy-extensions==1.0.0 +nbclient==0.8.0 +nbconvert==7.8.0 +nbformat==5.9.2 +nest-asyncio==1.5.8 +nh3==0.2.14 +nltk==3.8.1 +nodeenv==1.8.0 numpy==1.25.1 openai==0.27.8 openapi-python-client==0.14.1 @@ -101,60 +158,100 @@ opentelemetry-sdk==1.20.0 opentelemetry-semantic-conventions==0.41b0 opentelemetry-util-http==0.41b0 packaging==23.1 +paginate==0.5.6 +pandocfilters==1.5.0 +parso==0.8.3 pathspec==0.11.1 pep8==1.7.1 pep8-naming==0.13.3 pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==10.0.1 pkginfo==1.9.6 platformdirs==3.8.1 pluggy==1.2.0 poetry==1.5.1 poetry-core==1.6.1 poetry-plugin-export==1.4.0 +pre-commit==3.4.0 +prompt-toolkit==3.0.39 protobuf==4.23.3 +psutil==5.9.5 psycopg2-binary==2.9.6 ptyprocess==0.7.0 +pure-eval==0.2.2 pycodestyle==2.10.0 pycparser==2.21 pydantic==1.10.9 pyflakes==3.0.1 Pygments==2.15.1 pylint==2.17.4 +pymdown-extensions==10.3 +pypdfium2==4.20.0 pyproject_hooks==1.0.0 +pyquery==2.0.0 pyrsistent==0.19.3 pytest==7.4.0 +pytest-asyncio==0.21.1 +pytest-cov==4.1.0 pytest-mock==3.11.1 python-dateutil==2.8.2 pytoolconfig==1.2.5 PyYAML==6.0 -rapidfuzz==2.15.1 +pyyaml_env_tag==0.1 +pyzmq==25.1.1 +rapidfuzz==3.5.2 +readme-renderer==42.0 +readtime==3.0.0 +redis==5.0.1 referencing==0.29.1 regex==2023.8.8 requests==2.31.0 requests-toolbelt==1.0.0 +rfc3986==2.0.0 rich==13.4.2 rope==1.9.0 rpds-py==0.8.8 s3transfer==0.7.0 +scikit-learn==1.3.1 +scipy==1.11.3 shellingham==1.5.0.post1 six==1.16.0 +smmap==5.0.1 sniffio==1.3.0 +soupsieve==2.5 SQLAlchemy==2.0.17 +sqlglot==18.11.0 +sqlitedict==2.1.0 +sqlvalidator==0.0.20 +stack-data==0.6.3 swagger-ui-py==22.7.13 tenacity==8.2.2 +thefuzz==0.20.0 +threadpoolctl==3.2.0 +tiktoken==0.5.1 +tinycss2==1.2.1 +toml==0.10.2 tomli==2.0.1 tomlkit==0.11.8 toolz==0.12.0 +tornado==6.3.3 tqdm==4.65.0 +traitlets==5.11.1 trove-classifiers==2023.7.6 +twine==4.0.2 typer==0.9.0 typing_extensions==4.6.3 +untokenize==0.1.1 urllib3==1.26.16 virtualenv==20.23.1 +watchdog==3.0.0 +wcwidth==0.2.8 webencodings==0.5.1 Werkzeug==2.3.6 wrapt==1.15.0 xattr==0.10.1 +xxhash==3.4.1 yarl==1.9.2 zipp==3.15.0 zope.interface==6.0 diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 070eb1d..635a34e 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -143,7 +143,11 @@ def validate(guard_name: str): ) guard_history = guard.state.most_recent_call - result = len(guard_history.failed_validations) > 0 + last_step_logs: GuardLogs = guard_history.history[-1] + validation_logs = last_step_logs.field_validation_logs.validator_logs + failed_validations = list([log for log in validation_logs if log.validation_result.outcome == 'fail']) + + result = len(failed_validations) == 0 raw_output = guard_history.output or raw_llm_response validation_output = ValidationOutput( From 10fab54e8e4ccf8e722f256e5725e3dbefc75864 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 16 Nov 2023 09:22:39 -0600 Subject: [PATCH 26/31] add nklt data, bump lambda mem size --- Dockerfile.prod | 3 +++ cdktf/src/constructs/application.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Dockerfile.prod b/Dockerfile.prod index 7d082cc..9c42d10 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -31,6 +31,9 @@ RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./ # Install app dependencies RUN pip install -r requirements.txt +# Download punkt data +RUN python -m nltk.downloader -d /opt/nltk_data punkt + # Freeze dependencies RUN pip freeze > requirements.txt diff --git a/cdktf/src/constructs/application.ts b/cdktf/src/constructs/application.ts index 0b8cbb9..7861888 100644 --- a/cdktf/src/constructs/application.ts +++ b/cdktf/src/constructs/application.ts @@ -175,6 +175,7 @@ export class Application extends Construct { AWS_LWA_READINESS_CHECK_PORT: '8000', LOGLEVEL: 'INFO', NODE_ENV: 'production', + NLTK_DATA: '/opt/nltk_data', OPENSEARCH_SECRET: openSearchClusterCredentials.arn, OPENSEARCH_URL: opensearchDomain.endpoint, OTEL_SERVICE_NAME: 'guardrails-api', @@ -196,7 +197,8 @@ export class Application extends Construct { } }, imageUri: `${ecrRepo.repositoryUrl}:latest`, - memorySize: 512, + // Because some of the extended dependencies require gcc which over doubles our image size + memorySize: 1024, packageType: 'Image', timeout: 60 * 15, vpcConfig: { From 98f081e4e36107a196a99e8a5131030367762b2b Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Thu, 16 Nov 2023 10:50:00 -0600 Subject: [PATCH 27/31] add nltk data to dev build --- Dockerfile.dev | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile.dev b/Dockerfile.dev index 3b19bc0..396145c 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -30,6 +30,9 @@ RUN pip install ./guard-rails-api-client # Install app dependencies RUN pip install -r requirements.txt +# Download punkt data +RUN python -m nltk.downloader -d /opt/nltk_data punkt + # Freeze dependencies RUN pip freeze > requirements.txt From 27a2f191af6e0e63910dcc8782ea3a97bdf1a1ac Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 20 Feb 2024 11:37:38 -0600 Subject: [PATCH 28/31] lint fixes --- app.py | 5 +++-- src/blueprints/guards.py | 28 ++++++++++++++++++---------- src/blueprints/root.py | 1 + src/classes/data_type_struct.py | 13 +++++++++---- src/classes/guard_struct.py | 8 +++++--- src/classes/schema_element_struct.py | 6 +++--- src/classes/schema_struct.py | 12 +++--------- src/clients/postgres_client.py | 23 ++++++++++++----------- src/modules/otel_logger/__init__.py | 24 +++++++++++++++++------- src/modules/otel_meter/__init__.py | 4 ++-- src/modules/otel_tracer/__init__.py | 18 +++++++++++------- src/modules/resource/__init__.py | 4 +--- src/utils/gather_request_metrics.py | 22 +++++++++++++++++----- src/utils/handle_error.py | 4 +++- src/utils/logger.py | 1 + src/utils/payload_validator.py | 20 +++++++++++--------- 16 files changed, 117 insertions(+), 76 deletions(-) diff --git a/app.py b/app.py index 8941739..6dde5c5 100644 --- a/app.py +++ b/app.py @@ -5,6 +5,7 @@ app = Flask(__name__) CORS(app) + def create_app(): api_doc( app, @@ -14,7 +15,7 @@ def create_app(): ) from src.clients.postgres_client import PostgresClient - + pg_client = PostgresClient() pg_client.initialize(app) @@ -24,4 +25,4 @@ def create_app(): app.register_blueprint(root_bp) app.register_blueprint(guards_bp) - return app \ No newline at end of file + return app diff --git a/src/blueprints/guards.py b/src/blueprints/guards.py index 635a34e..d7af81e 100644 --- a/src/blueprints/guards.py +++ b/src/blueprints/guards.py @@ -94,7 +94,9 @@ def validate(guard_name: str): llm_api = payload.pop("llmApi", None) args = payload.pop("args", []) - with otel_tracer.start_as_current_span(f"validate-{decoded_guard_name}") as validate_span: + with otel_tracer.start_as_current_span( + f"validate-{decoded_guard_name}" + ) as validate_span: guard: Guard = guard_struct.to_guard(openai_api_key, otel_tracer) validate_span.set_attribute("guardName", decoded_guard_name) @@ -131,7 +133,7 @@ def validate(guard_name: str): prompt_params=prompt_params, llm_api=llm_api, *args, - **payload + **payload, ) else: raw_llm_response, validated_output = guard( @@ -139,17 +141,23 @@ def validate(guard_name: str): prompt_params=prompt_params, num_reasks=num_reasks, *args, - **payload + **payload, ) guard_history = guard.state.most_recent_call last_step_logs: GuardLogs = guard_history.history[-1] validation_logs = last_step_logs.field_validation_logs.validator_logs - failed_validations = list([log for log in validation_logs if log.validation_result.outcome == 'fail']) - + failed_validations = list( + [ + log + for log in validation_logs + if log.validation_result.outcome == "fail" + ] + ) + result = len(failed_validations) == 0 raw_output = guard_history.output or raw_llm_response - + validation_output = ValidationOutput( result, validated_output, guard.state.all_histories, raw_output ) @@ -161,7 +169,7 @@ def validate(guard_name: str): ) if prompt: validate_span.set_attribute("prompt", prompt) - + instructions = ( guard.rail.instructions.format(**(prompt_params or {})).source if guard.rail.instructions @@ -173,7 +181,7 @@ def validate(guard_name: str): validation_status = "pass" if result is True else "fail" validate_span.set_attribute("validation_status", validation_status) validate_span.set_attribute("raw_llm_ouput", raw_output) - + # Use the serialization from the class instead of re-writing it valid_output: str = ( json.dumps(validation_output.validated_output) @@ -181,14 +189,14 @@ def validate(guard_name: str): else str(validation_output.validated_output) ) validate_span.set_attribute("validated_output", valid_output) - + final_step_logs: GuardLogs = guard_history.history[-1] final_response = final_step_logs.llm_response prompt_token_count = final_response.prompt_token_count or 0 response_token_count = final_response.response_token_count or 0 total_token_count = prompt_token_count + response_token_count validate_span.set_attribute("tokens_consumed", total_token_count) - + num_of_reasks = ( len(guard_history.history) - 1 if len(guard_history.history) > 0 diff --git a/src/blueprints/root.py b/src/blueprints/root.py index 7863a57..b42329f 100644 --- a/src/blueprints/root.py +++ b/src/blueprints/root.py @@ -5,6 +5,7 @@ from src.utils.handle_error import handle_error from src.utils.gather_request_metrics import gather_request_metrics from src.utils import logger + # from src.modules.otel_logger import logger root_bp = Blueprint("root", __name__, url_prefix="/") diff --git a/src/classes/data_type_struct.py b/src/classes/data_type_struct.py index ef5702e..3bbc707 100644 --- a/src/classes/data_type_struct.py +++ b/src/classes/data_type_struct.py @@ -1,4 +1,3 @@ -import inspect import re from typing import Any, Dict, List, Optional from operator import attrgetter @@ -256,7 +255,9 @@ def from_xml(cls, elem: _Element): return cls(children, formatters, element, plugins) - def to_xml(self, parent: _Element, as_parent: Optional[bool] = False) -> _Element: + def to_xml( + self, parent: _Element, as_parent: Optional[bool] = False + ) -> _Element: element = None if as_parent: element = parent @@ -277,7 +278,11 @@ def to_xml(self, parent: _Element, as_parent: Optional[bool] = False) -> _Elemen if plugins is not None: element.attrib["plugins"] = plugins - xml_data_type = element if as_parent else SubElement(parent, element.tag, element.attrib) + xml_data_type = ( + element + if as_parent + else SubElement(parent, element.tag, element.attrib) + ) self_is_list = self.element.type == "list" if self.children is not None: @@ -306,7 +311,7 @@ def get_all_plugins(self) -> List[str]: for child_key in children: plugins.extend(children[child_key].get_all_plugins()) return plugins - + @staticmethod def is_data_type_struct(other: Any) -> bool: if isinstance(other, dict): diff --git a/src/classes/guard_struct.py b/src/classes/guard_struct.py index 19ecbfb..596916d 100644 --- a/src/classes/guard_struct.py +++ b/src/classes/guard_struct.py @@ -1,5 +1,5 @@ from typing import Optional -from guardrails import Guard, Rail +from guardrails import Guard from opentelemetry.trace import Tracer from lxml.etree import tostring from src.classes.rail_spec_struct import RailSpecStruct @@ -32,7 +32,9 @@ def from_guard(cls, guard: Guard): guard.description, ) - def to_guard(self, openai_api_key: Optional[str] = None, tracer: Tracer = None) -> Guard: + def to_guard( + self, openai_api_key: Optional[str] = None, tracer: Tracer = None + ) -> Guard: rail_xml = self.railspec.to_xml() rail_string = tostring(rail_xml) guard = Guard.from_rail_string( @@ -40,7 +42,7 @@ def to_guard(self, openai_api_key: Optional[str] = None, tracer: Tracer = None) self.num_reasks, description=self.description, name=self.name, - tracer=tracer + tracer=tracer, ) guard.openai_api_key = openai_api_key return guard diff --git a/src/classes/schema_element_struct.py b/src/classes/schema_element_struct.py index 1626669..9fce64d 100644 --- a/src/classes/schema_element_struct.py +++ b/src/classes/schema_element_struct.py @@ -42,9 +42,9 @@ def to_element(self) -> ElementStub: elem_dict[ validator_on_fail.get("validatorTag") ] = validator_on_fail.get("method") - elem_dict.pop('date_format', None) - elem_dict.pop('time_format', None) - elem_dict.pop('on_fails', None) + elem_dict.pop("date_format", None) + elem_dict.pop("time_format", None) + elem_dict.pop("on_fails", None) return ElementStub(self.type, elem_dict) @classmethod diff --git a/src/classes/schema_struct.py b/src/classes/schema_struct.py index ed04fca..194d9ec 100644 --- a/src/classes/schema_struct.py +++ b/src/classes/schema_struct.py @@ -18,9 +18,7 @@ def from_schema(cls, schema: Schema): serialized_schema = {} if isinstance(schema, StringSchema): - serialized_schema = DataTypeStruct.from_data_type( - schema - ) + serialized_schema = DataTypeStruct.from_data_type(schema) else: for key in schema._schema: schema_element = schema._schema[key] @@ -53,9 +51,7 @@ def from_dict(cls, schema: dict): serialized_schema = {} inner_schema = schema["schema"] if DataTypeStruct.is_data_type_struct(inner_schema): - serialized_schema = DataTypeStruct.from_dict( - inner_schema - ) + serialized_schema = DataTypeStruct.from_dict(inner_schema) else: for key in inner_schema: schema_element = inner_schema[key] @@ -83,9 +79,7 @@ def from_request(cls, schema: dict): # StringSchema (or really just any PrimitiveSchema) if DataTypeStruct.is_data_type_struct(inner_schema): - serialized_schema = DataTypeStruct.from_request( - inner_schema - ) + serialized_schema = DataTypeStruct.from_request(inner_schema) else: # JsonSchema for key in inner_schema: diff --git a/src/clients/postgres_client.py b/src/clients/postgres_client.py index 472ca36..13a64d9 100644 --- a/src/clients/postgres_client.py +++ b/src/clients/postgres_client.py @@ -14,13 +14,11 @@ def __new__(cls): if cls._instance is None: cls._instance = super(PostgresClient, cls).__new__(cls) return cls._instance - + def fetch_pg_secret(self, secret_arn: str) -> dict: - client = boto3.client('secretsmanager') - response: dict = client.get_secret_value( - SecretId=secret_arn - ) - secret_string = response.get('SecretString') + client = boto3.client("secretsmanager") + response: dict = client.get_secret_value(SecretId=secret_arn) + secret_string = response.get("SecretString") try: secret = json.loads(secret_string) return secret @@ -33,14 +31,13 @@ def get_pg_creds(self) -> Tuple[str, str]: pg_password_secret = os.environ.get("PGPASSWORD_SECRET_ARN") if pg_password_secret is not None: pg_secret = self.fetch_pg_secret(pg_password_secret) or {} - pg_user = pg_secret.get('username') - pg_password = pg_secret.get('password') + pg_user = pg_secret.get("username") + pg_password = pg_secret.get("password") pg_user = pg_user or os.environ.get("PGUSER", "postgres") pg_password = pg_password or os.environ.get("PGPASSWORD") return pg_user, pg_password - def initialize(self, app: Flask): pg_user, pg_password = self.get_pg_creds() pg_host = os.environ.get("PGHOST", "localhost") @@ -49,11 +46,15 @@ def initialize(self, app: Flask): pg_endpoint = ( pg_host - if pg_host.endswith(f":{pg_port}") # FIXME: This is a cheap check; maybe use a regex instead? + if pg_host.endswith( + f":{pg_port}" + ) # FIXME: This is a cheap check; maybe use a regex instead? else f"{pg_host}:{pg_port}" ) - conf = f"postgresql://{pg_user}:{pg_password}@{pg_endpoint}/{pg_database}" + conf = ( + f"postgresql://{pg_user}:{pg_password}@{pg_endpoint}/{pg_database}" + ) if os.environ.get("NODE_ENV") == "production": conf = f"{conf}?sslmode=verify-ca&sslrootcert=global-bundle.pem" diff --git a/src/modules/otel_logger/__init__.py b/src/modules/otel_logger/__init__.py index 5aa7854..8eda3ca 100644 --- a/src/modules/otel_logger/__init__.py +++ b/src/modules/otel_logger/__init__.py @@ -3,22 +3,32 @@ import os import logging import opentelemetry._logs as logs -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter, SimpleLogRecordProcessor +# from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import ( + BatchLogRecordProcessor, + ConsoleLogExporter, + # SimpleLogRecordProcessor, +) from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter -OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://otel-collector:4317') +OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get( + "OTEL_EXPORTER_OTLP_ENDPOINT", "http://otel-collector:4317" +) logger_provider = logs.get_logger_provider() -otlp_logs_exporter = OTLPLogExporter(endpoint=OTEL_EXPORTER_OTLP_ENDPOINT, insecure=True) +otlp_logs_exporter = OTLPLogExporter( + endpoint=OTEL_EXPORTER_OTLP_ENDPOINT, insecure=True +) console_log_exporter = ConsoleLogExporter() -logger_provider.add_log_record_processor(BatchLogRecordProcessor(otlp_logs_exporter)) +logger_provider.add_log_record_processor( + BatchLogRecordProcessor(otlp_logs_exporter) +) # For Debugging # logger_provider.add_log_record_processor(SimpleLogRecordProcessor(otlp_logs_exporter)) # logger_provider.add_log_record_processor(SimpleLogRecordProcessor(console_log_exporter)) log_level = os.environ.get("LOGLEVEL", logging.INFO) logging.root.setLevel(log_level) -service_name = os.environ.get('OTEL_SERVICE_NAME', 'guardrails-api') -logger = logging.getLogger(service_name) \ No newline at end of file +service_name = os.environ.get("OTEL_SERVICE_NAME", "guardrails-api") +logger = logging.getLogger(service_name) diff --git a/src/modules/otel_meter/__init__.py b/src/modules/otel_meter/__init__.py index a6468e5..d88ecd1 100644 --- a/src/modules/otel_meter/__init__.py +++ b/src/modules/otel_meter/__init__.py @@ -3,6 +3,6 @@ import os from opentelemetry import metrics -service_name = os.environ.get('OTEL_SERVICE_NAME', 'guardrails-api') +service_name = os.environ.get("OTEL_SERVICE_NAME", "guardrails-api") provider = metrics.get_meter_provider() -otel_meter = provider.get_meter(service_name) \ No newline at end of file +otel_meter = provider.get_meter(service_name) diff --git a/src/modules/otel_tracer/__init__.py b/src/modules/otel_tracer/__init__.py index d5a0b3a..e7c04a4 100644 --- a/src/modules/otel_tracer/__init__.py +++ b/src/modules/otel_tracer/__init__.py @@ -1,16 +1,20 @@ import os from opentelemetry import trace from opentelemetry.sdk.trace.export import SimpleSpanProcessor -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as GrpcExporter +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( + OTLPSpanExporter as GrpcExporter, +) # configure -OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', 'http://localhost:4317') # 'http://otel-collector:4317') -OTEL_TRACE_SINK = os.environ.get('OTEL_TRACE_SINK') +OTEL_EXPORTER_OTLP_ENDPOINT = os.environ.get( + "OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317" +) # 'http://otel-collector:4317') +OTEL_TRACE_SINK = os.environ.get("OTEL_TRACE_SINK") endpoint = OTEL_TRACE_SINK or OTEL_EXPORTER_OTLP_ENDPOINT print("OTEL_EXPORTER_OTLP_ENDPOINT: ", OTEL_EXPORTER_OTLP_ENDPOINT) print("OTEL_TRACE_SINK: ", OTEL_TRACE_SINK) print("endpoint: ", endpoint) -insecure = not endpoint.startswith('https://') +insecure = not endpoint.startswith("https://") provider = trace.get_tracer_provider() span_processors = provider._active_span_processor._span_processors @@ -22,9 +26,9 @@ ) simple_processor = SimpleSpanProcessor(span_exporter) -is_lambda = os.environ.get('AWS_EXECUTION_ENV', '').startswith('AWS_Lambda_') +is_lambda = os.environ.get("AWS_EXECUTION_ENV", "").startswith("AWS_Lambda_") if is_lambda or not span_processor: provider._active_span_processor._span_processors = (simple_processor,) -service_name = os.environ.get('OTEL_SERVICE_NAME', 'guardrails-api') -otel_tracer = trace.get_tracer(service_name) \ No newline at end of file +service_name = os.environ.get("OTEL_SERVICE_NAME", "guardrails-api") +otel_tracer = trace.get_tracer(service_name) diff --git a/src/modules/resource/__init__.py b/src/modules/resource/__init__.py index c959418..c30a892 100644 --- a/src/modules/resource/__init__.py +++ b/src/modules/resource/__init__.py @@ -1,5 +1,3 @@ from opentelemetry.sdk.resources import Resource -resource = Resource(attributes={ - 'SERVICE_NAME': 'guardrails-api' -}) \ No newline at end of file +resource = Resource(attributes={"SERVICE_NAME": "guardrails-api"}) diff --git a/src/utils/gather_request_metrics.py b/src/utils/gather_request_metrics.py index 59d798b..36b0361 100644 --- a/src/utils/gather_request_metrics.py +++ b/src/utils/gather_request_metrics.py @@ -8,9 +8,7 @@ unit="1", ) requests_over_time = otel_meter.create_histogram( - name="http_requests_ot", - description="Histogram of requests", - unit="1" + name="http_requests_ot", description="Histogram of requests", unit="1" ) # status_2xx_counter = otel_meter.create_counter("2xx_statuses") # status_4xx_counter = otel_meter.create_counter("4xx_statuses") @@ -20,8 +18,22 @@ def gather_request_metrics(fn): @wraps(fn) def decorator(*args, **kwargs): - request_total.add(1, { "environment": os.environ.get("APP_ENVIRONMENT", "APP_ENVIRONMENT NOT SET") }) - requests_over_time.record(1, { "environment": os.environ.get("APP_ENVIRONMENT", "APP_ENVIRONMENT NOT SET") }) + request_total.add( + 1, + { + "environment": os.environ.get( + "APP_ENVIRONMENT", "APP_ENVIRONMENT NOT SET" + ) + }, + ) + requests_over_time.record( + 1, + { + "environment": os.environ.get( + "APP_ENVIRONMENT", "APP_ENVIRONMENT NOT SET" + ) + }, + ) return fn(*args, **kwargs) return decorator diff --git a/src/utils/handle_error.py b/src/utils/handle_error.py index b21796c..8da18af 100644 --- a/src/utils/handle_error.py +++ b/src/utils/handle_error.py @@ -15,7 +15,9 @@ def decorator(*args, **kwargs): traceback.print_exception(http_error) return http_error.to_dict(), http_error.status except HTTPException as http_exception: - http_error = HttpError(http_exception.code, http_exception.description) + http_error = HttpError( + http_exception.code, http_exception.description + ) return http_error.to_dict(), http_error.status except Exception as e: logger.error(e) diff --git a/src/utils/logger.py b/src/utils/logger.py index 09bb25d..a0cd8a4 100644 --- a/src/utils/logger.py +++ b/src/utils/logger.py @@ -1,5 +1,6 @@ import os import logging + # Shouldn't need this with auto-instrumentation # from src.modules.otel_logger import handler as otel_handler diff --git a/src/utils/payload_validator.py b/src/utils/payload_validator.py index f3e1fe1..5c75507 100644 --- a/src/utils/payload_validator.py +++ b/src/utils/payload_validator.py @@ -4,21 +4,23 @@ from referencing import Registry, jsonschema as jsonschema_ref from src.classes.http_error import HttpError -with open("./open-api-spec.yml", 'r') as open_api_spec: +with open("./open-api-spec.yml", "r") as open_api_spec: api_spec: Dict = yaml.safe_load(open_api_spec) -registry = Registry().with_resources([ - ( - "urn:guardrails-api-spec", - jsonschema_ref.DRAFT202012.create_resource(api_spec) - ) -]) +registry = Registry().with_resources( + [ + ( + "urn:guardrails-api-spec", + jsonschema_ref.DRAFT202012.create_resource(api_spec), + ) + ] +) guard_validator = Draft202012Validator( { "$ref": "urn:guardrails-api-spec#/components/schemas/Guard", }, - registry=registry + registry=registry, ) @@ -34,5 +36,5 @@ def validate_payload(payload: dict): 400, "BadRequest", "The request payload did not match the required schema.", - fields + fields, ) From de683c0aff3ba96bc279033414621226be635f5a Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 20 Feb 2024 11:40:12 -0600 Subject: [PATCH 29/31] install guardrails in dockerfile --- Dockerfile.prod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.prod b/Dockerfile.prod index 9c42d10..d49e663 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -25,7 +25,7 @@ COPY ./guard-rails-api-client ./guard-rails-api-client COPY requirements.txt . RUN pip install ./guard-rails-api-client -RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry +RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry --all-extras RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem # Install app dependencies From 439b2bc8d860b7447f3019610b985f1b16dccc54 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Tue, 20 Feb 2024 14:22:29 -0600 Subject: [PATCH 30/31] start fixing build and tests --- .github/workflows/pr_qa.yml | 2 +- Dockerfile.prod | 2 +- Makefile | 6 ++++++ src/blueprints/root.py | 2 +- tests/__init__.py | 0 tests/blueprints/__init__.py | 0 tests/blueprints/test_root.py | 10 +++++++++- 7 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/blueprints/__init__.py diff --git a/.github/workflows/pr_qa.yml b/.github/workflows/pr_qa.yml index 09428d4..baf2601 100644 --- a/.github/workflows/pr_qa.yml +++ b/.github/workflows/pr_qa.yml @@ -18,7 +18,7 @@ jobs: - name: Quality Checks run: | python -m pip install --upgrade pip; - pip install -r requirements.txt; + make build; make qa; ## diff --git a/Dockerfile.prod b/Dockerfile.prod index d49e663..9c42d10 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -25,7 +25,7 @@ COPY ./guard-rails-api-client ./guard-rails-api-client COPY requirements.txt . RUN pip install ./guard-rails-api-client -RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry --all-extras +RUN pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ./global-bundle.pem # Install app dependencies diff --git a/Makefile b/Makefile index e2144e6..843b34b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,12 @@ build-sdk: bash build-sdk.sh +build: + pip install -r requirements.txt; + pip install git+https://github.com/guardrails-ai/guardrails-internal.git@telemetry; + make build-sdk; + pip install ./guard-rails-api-client + dev: bash ./dev.sh diff --git a/src/blueprints/root.py b/src/blueprints/root.py index b42329f..28f52f7 100644 --- a/src/blueprints/root.py +++ b/src/blueprints/root.py @@ -4,7 +4,7 @@ from src.clients.postgres_client import PostgresClient from src.utils.handle_error import handle_error from src.utils.gather_request_metrics import gather_request_metrics -from src.utils import logger +from src.utils.logger import logger # from src.modules.otel_logger import logger diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/blueprints/__init__.py b/tests/blueprints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/blueprints/test_root.py b/tests/blueprints/test_root.py index b6237eb..e7e9a9d 100644 --- a/tests/blueprints/test_root.py +++ b/tests/blueprints/test_root.py @@ -26,8 +26,13 @@ class MockPg: def __init__(self): self.db = mock_pg.db mocker.patch("src.clients.postgres_client.PostgresClient", new=MockPg) + from src.blueprints.root import ( + health_check, + root_bp, + text, + PostgresClient + ) info_spy = mocker.spy(logger, "info") - from src.blueprints.root import health_check, root_bp, text, PostgresClient response = health_check() @@ -35,6 +40,9 @@ def __init__(self): assert root_bp.routes == ["/", "/health-check"] text.assert_called_once_with("SELECT count(datid) FROM pg_stat_activity;") assert mock_pg.db.session.queries == ["SELECT count(datid) FROM pg_stat_activity;"] + + assert info_spy.call_count == 1 + info_spy.assert_called_once_with("response: ", [(1,)]) assert response == { "status": 200, "message": "Ok" } From 56ba0260b96c18e4179ad0acfe36c1d229394163 Mon Sep 17 00:00:00 2001 From: Caleb Courier Date: Wed, 21 Feb 2024 13:17:12 -0600 Subject: [PATCH 31/31] fix tests --- tests/blueprints/test_guards.py | 1 + tests/blueprints/test_root.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/blueprints/test_guards.py b/tests/blueprints/test_guards.py index 83ae965..19f0fb6 100644 --- a/tests/blueprints/test_guards.py +++ b/tests/blueprints/test_guards.py @@ -13,6 +13,7 @@ def test_guards_get(mocker): mocker.patch("flask.Blueprint", new=MockBlueprint) mocker.patch("flask.request", mock_request) mocker.patch("src.clients.guard_client.GuardClient", new=MockGuardClient) + mocker.patch("opentelemetry.trace.get_tracer_provider") from src.blueprints.guards import guards, guards_bp response = guards() diff --git a/tests/blueprints/test_root.py b/tests/blueprints/test_root.py index e7e9a9d..a941c4f 100644 --- a/tests/blueprints/test_root.py +++ b/tests/blueprints/test_root.py @@ -40,10 +40,8 @@ def __init__(self): assert root_bp.routes == ["/", "/health-check"] text.assert_called_once_with("SELECT count(datid) FROM pg_stat_activity;") assert mock_pg.db.session.queries == ["SELECT count(datid) FROM pg_stat_activity;"] - - assert info_spy.call_count == 1 - - info_spy.assert_called_once_with("response: ", [(1,)]) + + info_spy.assert_called_once_with("response: %s", [(1,)]) assert response == { "status": 200, "message": "Ok" } mocker.resetall()