diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a894344..a93384e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -78,7 +78,7 @@ repos: args: - --max-line-length=120 - --extend-immutable-calls=Query,fastapi.Depends,fastapi.params.Depends - - --ignore=B008,E203 # Ignore error for function calls in argument defaults + - --ignore=B008,E203, W503 # Ignore error for function calls in argument defaults exclude: ^(__init__.py$|.*\/__init__.py$) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4569ff34..b1f22089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +# v3.4.0 +## Key Features +### Vector Store Support +- Implemented support for multiple vector stores of the same type. For example, you can now configure more than 1 OpenSearch vector store with LISA. +- Introduced granular access control for vector stores based on a list of provided IDP groups. If a list isn’t provided the vector store is available to all LISA users. +- Expanded APIs for vector store file management to now include file listing and removal capabilities. + +### Deployment Flexibility +- Enabled custom IAM role overrides with documented minimum permissions available on our [documentation site](https://awslabs.github.io/LISA/config/role-overrides) +- Introduced partition and domain override functionality + +## Other System Enhancements +- Enhanced create model validation to ensure data integrity +- Upgraded to Python 3.11 runtime for improved performance +- Updated various third-party dependencies to maintain security and functionality +- Updated the ChatUI: + - Refined ChatUI for improved message display + - Upgraded markdown parsing capabilities + - Implemented a copy feature for AI-generated responses + +## Coming soon +Happy Holidays! We have a lot in store for 2025. Our roadmap is customer driven. Please reach out to us via Github issues to talk more! Early in the new year you’ll see chatbot UI and vector store enhancements. + +## Acknowledgements +* @bedanley +* @estohlmann +* @dustins + +**Full Changelog**: https://github.com/awslabs/LISA/compare/v3.3.2...v3.4.0 + # v3.3.2 ## Bug Fixes - Resolved issue where invalid schema import was causing create model api calls to fail diff --git a/Makefile b/Makefile index 6b982bb0..7e2d51da 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ createTypeScriptEnvironment installTypeScriptRequirements \ deploy destroy \ clean cleanTypeScript cleanPython cleanCfn cleanMisc \ - help dockerCheck dockerLogin listStacks modelCheck buildEcsDeployer + help dockerCheck dockerLogin listStacks modelCheck buildNpmModules ################################################################################# # GLOBALS # @@ -56,11 +56,22 @@ ifeq (${REGION},) $(error region must be set in command line using REGION variable or config files) endif -# URL_SUFFIX - used for the docker login -ifeq ($(findstring iso,${REGION}),) -URL_SUFFIX := amazonaws.com +ifeq (${PARTITION},) +PARTITION := $(shell cat $(PROJECT_DIR)/config-custom.yaml | yq .partition ) +endif +ifeq (${PARTITION}, null) +PARTITION := aws +endif + +# DOMAIN - used for the docker login +ifeq (${DOMAIN},) +ifeq ($(findstring isob,${REGION}),isob) +DOMAIN := sc2s.sgov.gov +else ifeq ($(findstring iso,${REGION}),iso) +DOMAIN := c2s.ic.gov else -URL_SUFFIX := c2s.ic.gov +DOMAIN := amazonaws.com +endif endif # Arguments defined through config files @@ -117,16 +128,18 @@ MODEL_BUCKET := $(shell cat $(PROJECT_DIR)/config-custom.yaml | yq '.s3BucketMod ## Bootstrap AWS Account with CDK bootstrap bootstrap: - @printf "Bootstrapping: $(ACCOUNT_NUMBER) | $(REGION)\n" + @printf "Bootstrapping: $(ACCOUNT_NUMBER) | $(REGION) | $(PARTITION)\n" ifdef PROFILE @cdk bootstrap \ --profile $(PROFILE) \ aws://$(ACCOUNT_NUMBER)/$(REGION) \ + --partition $(PARTITION) \ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess else @cdk bootstrap \ aws://$(ACCOUNT_NUMBER)/$(REGION) \ + --partition $(PARTITION) \ --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess endif @@ -217,6 +230,7 @@ cleanTypeScript: @find . -type d -name "build" -exec rm -rf {} + @find . -type d -name ".tscache" -exec rm -rf {} + @find . -type d -name ".jest_cache" -exec rm -rf {} + + @find . -type d -name "node_modules" -exec rm -rf {} + ## Delete CloudFormation outputs @@ -233,11 +247,11 @@ cleanMisc: dockerLogin: dockerCheck ifdef PROFILE @$(foreach ACCOUNT,$(ACCOUNT_NUMBERS_ECR), \ - aws ecr get-login-password --region ${REGION} --profile ${PROFILE} | $(DOCKER_CMD) login --username AWS --password-stdin ${ACCOUNT}.dkr.ecr.${REGION}.${URL_SUFFIX} >/dev/null 2>&1; \ + aws ecr get-login-password --region ${REGION} --profile ${PROFILE} | $(DOCKER_CMD) login --username AWS --password-stdin ${ACCOUNT}.dkr.ecr.${REGION}.${DOMAIN} >/dev/null 2>&1; \ ) else @$(foreach ACCOUNT,$(ACCOUNT_NUMBERS_ECR), \ - aws ecr get-login-password --region ${REGION} | $(DOCKER_CMD) login --username AWS --password-stdin ${ACCOUNT}.dkr.ecr.${REGION}.${URL_SUFFIX} >/dev/null 2>&1; \ + aws ecr get-login-password --region ${REGION} | $(DOCKER_CMD) login --username AWS --password-stdin ${ACCOUNT}.dkr.ecr.${REGION}.${DOMAIN} >/dev/null 2>&1; \ ) endif @@ -245,8 +259,8 @@ endif listStacks: @npx cdk list -buildEcsDeployer: - @cd ./ecs_model_deployer && npm install && npm run build +buildNpmModules: + npm run build define print_config @printf "\n \ @@ -254,6 +268,8 @@ define print_config -----------------------------------\n \ Account Number $(ACCOUNT_NUMBER)\n \ Region $(REGION)\n \ + Partition $(PARTITION)\n \ + Domain $(DOMAIN)\n \ App Name $(APP_NAME)\n \ Deployment Stage $(DEPLOYMENT_STAGE)\n \ Deployment Name $(DEPLOYMENT_NAME)" @@ -264,7 +280,7 @@ define print_config endef ## Deploy all infrastructure -deploy: dockerCheck dockerLogin cleanMisc modelCheck buildEcsDeployer +deploy: dockerCheck dockerLogin cleanMisc modelCheck buildNpmModules $(call print_config) ifneq (,$(findstring true, $(HEADLESS))) npx cdk deploy ${STACK} $(if $(PROFILE),--profile ${PROFILE}) --require-approval never -c ${ENV}='$(shell echo '${${ENV}}')'; @@ -280,11 +296,16 @@ endif ## Tear down all infrastructure destroy: cleanMisc $(call print_config) +ifneq (,$(findstring true, $(HEADLESS))) + npx cdk destroy ${STACK} --force $(if $(PROFILE),--profile ${PROFILE}); +else @printf "Is the configuration correct? [y/N] "\ && read confirm_config &&\ if [ $${confirm_config:-'N'} = 'y' ]; then \ npx cdk destroy ${STACK} --force $(if $(PROFILE),--profile ${PROFILE}); \ fi; +endif + ################################################################################# diff --git a/VERSION b/VERSION index 47725433..18091983 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.2 +3.4.0 diff --git a/bin/lisa.ts b/bin/lisa.ts index fbc6b305..9e787e0d 100644 --- a/bin/lisa.ts +++ b/bin/lisa.ts @@ -40,6 +40,8 @@ const mappings: EnvMapping[] = [ ['PROFILE', 'profile'], ['DEPLOYMENT_NAME', 'deploymentName'], ['ACCOUNT_NUMBER', 'accountNumber'], + ['PARTITION', 'partition'], + ['DOMAIN', 'domain'], ['REGION', 'region'], ]; mappings.forEach(([envVar, configVar]) => { diff --git a/ecs_model_deployer/src/lib/ecs-schema.ts b/ecs_model_deployer/src/lib/ecs-schema.ts index 77e38653..de8c118e 100644 --- a/ecs_model_deployer/src/lib/ecs-schema.ts +++ b/ecs_model_deployer/src/lib/ecs-schema.ts @@ -233,6 +233,62 @@ export class Ec2Metadata { maxThroughput: 100, vCpus: 192, }, + 'g6.xlarge': { + memory: 16 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 10, + vCpus: 4, + }, + 'g6.2xlarge': { + memory: 32 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 10, + vCpus: 8, + }, + 'g6.4xlarge': { + memory: 64 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 16, + }, + 'g6.8xlarge': { + memory: 128 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 32, + }, + 'g6.16xlarge': { + memory: 256 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 64, + }, + 'g6.12xlarge': { + memory: 192 * 1000, + gpuCount: 4, + nvmePath: '/dev/nvme1n1', + maxThroughput: 40, + vCpus: 48, + }, + 'g6.24xlarge': { + memory: 384 * 1000, + gpuCount: 4, + nvmePath: '/dev/nvme1n1', + maxThroughput: 50, + vCpus: 96, + }, + 'g6.48xlarge': { + memory: 768 * 1000, + gpuCount: 8, + nvmePath: '/dev/nvme1n1', + maxThroughput: 100, + vCpus: 192, + }, 'p4d.24xlarge': { memory: 1152 * 1000, gpuCount: 8, diff --git a/ecs_model_deployer/src/lib/ecsCluster.ts b/ecs_model_deployer/src/lib/ecsCluster.ts index 17a26609..51e49f76 100644 --- a/ecs_model_deployer/src/lib/ecsCluster.ts +++ b/ecs_model_deployer/src/lib/ecsCluster.ts @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -*/ + */ // ECS Cluster Construct. import { CfnOutput, Duration, RemovalPolicy } from 'aws-cdk-lib'; @@ -38,25 +38,29 @@ import { Volume, } from 'aws-cdk-lib/aws-ecs'; import { ApplicationLoadBalancer, BaseApplicationListenerProps } from 'aws-cdk-lib/aws-elasticloadbalancingv2'; -import { IRole, ManagedPolicy, ServicePrincipal, Role } from 'aws-cdk-lib/aws-iam'; +import { IRole, ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; import { createCdkId } from './utils'; -import { BaseProps, ECSConfig, Ec2Metadata, EcsSourceType } from './ecs-schema'; +import { BaseProps, Ec2Metadata, ECSConfig, EcsSourceType } from './ecs-schema'; /** * Properties for the ECSCluster Construct. * * @property {IVpc} vpc - The virtual private cloud (VPC). - * @property {SecurityGroups} securityGroups - The security group that the ECS cluster should use. + * @property {ISecurityGroup} securityGroup - The security group that the ECS cluster should use. * @property {ECSConfig} ecsConfig - The configuration for the cluster. + * @property {string} taskRoleName? - The role applied to the task + * @property {string} executionRoleName? - The role used for executing the task */ type ECSClusterProps = { ecsConfig: ECSConfig; securityGroup: ISecurityGroup; vpc: IVpc; subnetSelection?: SubnetSelection; + taskRoleName?: string; + executionRoleName?: string; } & BaseProps; /** @@ -79,7 +83,7 @@ export class ECSCluster extends Construct { */ constructor (scope: Construct, id: string, props: ECSClusterProps) { super(scope, id); - const { config, vpc, securityGroup, ecsConfig, subnetSelection } = props; + const { config, vpc, securityGroup, ecsConfig, subnetSelection, taskRoleName, executionRoleName } = props; // Create ECS cluster const cluster = new Cluster(this, createCdkId([ecsConfig.identifier, 'Cl']), { @@ -182,30 +186,17 @@ export class ECSCluster extends Construct { environment.SSL_CERT_FILE = config.certificateAuthorityBundle; } - const taskPolicyId = createCdkId([config.deploymentName, 'ECSPolicy']); - const taskPolicyStringParam = StringParameter.fromStringParameterName(this, 'taskPolicyStringParam', - `${config.deploymentPrefix}/policies/${taskPolicyId}` - ); - const taskPolicy = ManagedPolicy.fromManagedPolicyArn(this, taskPolicyId, taskPolicyStringParam.stringValue); - const role_id = ecsConfig.identifier; - const roleName = createCdkId([config.deploymentName, role_id, 'Role']); - const taskRole = new Role(this, createCdkId([role_id, 'Role']), { - assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), - roleName, - description: `Allow ${role_id} ${role_id} ECS task access to AWS resources`, - managedPolicies: [taskPolicy], - }); - new StringParameter(this, createCdkId([config.deploymentName, role_id, 'SP']), { - parameterName: `${config.deploymentPrefix}/roles/${role_id}`, - stringValue: taskRole.roleArn, - description: `Role ARN for LISA ${role_id} ${role_id} ECS Task`, - }); + const roleId = ecsConfig.identifier; + const taskRole = taskRoleName ? + Role.fromRoleName(this, createCdkId([config.deploymentName, roleId]), taskRoleName) : + this.createTaskRole(config.deploymentName, config.deploymentPrefix, roleId); // Create ECS task definition - const taskDefinition = new Ec2TaskDefinition(this, createCdkId([ecsConfig.identifier, 'Ec2TaskDefinition']), { - family: createCdkId([config.deploymentName, ecsConfig.identifier], 32, 2), - taskRole: taskRole, + const taskDefinition = new Ec2TaskDefinition(this, createCdkId([roleId, 'Ec2TaskDefinition']), { + family: createCdkId([config.deploymentName, roleId], 32, 2), volumes: volumes, + taskRole, + ...(executionRoleName && { executionRole: Role.fromRoleName(this, createCdkId([config.deploymentName, roleId, 'EX']), executionRoleName) }), }); // Add container to task definition @@ -351,4 +342,28 @@ export class ECSCluster extends Construct { this.container = container; this.taskRole = taskRole; } + + createTaskRole (deploymentName: string, deploymentPrefix: string | undefined, roleId: string): IRole { + const taskPolicyId = createCdkId([deploymentName, 'ECSPolicy']); + const taskPolicyStringParam = StringParameter.fromStringParameterName(this, 'taskPolicyStringParam', + `${deploymentPrefix}/policies/${taskPolicyId}`, + ); + + const taskPolicy = ManagedPolicy.fromManagedPolicyArn(this, taskPolicyId, taskPolicyStringParam.stringValue); + const roleName = createCdkId([roleId, 'Role']); + const role = new Role(this, roleName, { + assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), + roleName, + description: `Allow ${roleId} ECS task access to AWS resources`, + managedPolicies: [taskPolicy], + }); + + new StringParameter(this, createCdkId([deploymentName, roleId, 'SP']), { + parameterName: `${deploymentPrefix}/roles/${roleId}`, + stringValue: role.roleArn, + description: `Role ARN for LISA ${roleId} ECS Task`, + }); + + return role; + } } diff --git a/lambda/authorizer/lambda_functions.py b/lambda/authorizer/lambda_functions.py index 3d535378..ace443cd 100644 --- a/lambda/authorizer/lambda_functions.py +++ b/lambda/authorizer/lambda_functions.py @@ -13,11 +13,12 @@ # limitations under the License. """Authorize for REST API.""" +import json import logging import os import ssl from functools import cache -from typing import Any, Dict +from typing import Any, Dict, Optional import boto3 import create_env_variables # noqa: F401 @@ -61,8 +62,9 @@ def lambda_handler(event: Dict[str, Any], context) -> Dict[str, Any]: # type: i if jwt_data := id_token_is_valid(id_token=id_token, client_id=client_id, authority=authority): is_admin_user = is_admin(jwt_data, admin_group, jwt_groups_property) + groups = get_property_path(jwt_data, jwt_groups_property) allow_policy = generate_policy(effect="Allow", resource=event["methodArn"], username=jwt_data["sub"]) - allow_policy["context"] = {"username": jwt_data["sub"]} + allow_policy["context"] = {"username": jwt_data["sub"], "groups": json.dumps(groups or [])} if requested_resource.startswith("/models") and not is_admin_user: # non-admin users can still list models @@ -142,14 +144,20 @@ def id_token_is_valid(*, id_token: str, client_id: str, authority: str) -> Dict[ def is_admin(jwt_data: dict[str, Any], admin_group: str, jwt_groups_property: str) -> bool: """Check if the user is an admin.""" - props = jwt_groups_property.split(".") - current_node = jwt_data + return admin_group in (get_property_path(jwt_data, jwt_groups_property) or []) + + +def get_property_path(data: dict[str, Any], property_path: str) -> Optional[Any]: + """Get the value represented by a property path.""" + props = property_path.split(".") + current_node = data for prop in props: if prop in current_node: current_node = current_node[prop] else: - return False - return admin_group in current_node + return None + + return current_node @cache diff --git a/lambda/models/domain_objects.py b/lambda/models/domain_objects.py index 8aa8c048..830ce2d0 100644 --- a/lambda/models/domain_objects.py +++ b/lambda/models/domain_objects.py @@ -14,10 +14,12 @@ """Domain objects for interacting with the model endpoints.""" +import time +import uuid from enum import Enum -from typing import Annotated, Dict, List, Optional, Union +from typing import Annotated, Any, Dict, List, Optional, Union -from pydantic import BaseModel, Field, NonNegativeInt, PositiveInt +from pydantic import BaseModel, ConfigDict, Field, NonNegativeInt, PositiveInt from pydantic.functional_validators import AfterValidator, field_validator, model_validator from typing_extensions import Self from utilities.validators import validate_all_fields_defined, validate_any_fields_defined, validate_instance_type @@ -292,3 +294,32 @@ class DeleteModelResponse(ApiResponseBase): """Response object when deleting a model.""" pass + + +class IngestionType(Enum): + AUTO = "auto" + MANUAL = "manual" + + +class RagDocument(BaseModel): + """Rag Document Entity for storing in DynamoDB.""" + + pk: Optional[str] = None + document_id: str = Field(default_factory=lambda: str(uuid.uuid4())) + repository_id: str + collection_id: str + document_name: str + source: str + sub_docs: List[str] = Field(default_factory=lambda: []) + ingestion_type: IngestionType = Field(default_factory=lambda: IngestionType.MANUAL) + upload_date: int = Field(default_factory=lambda: int(time.time())) + + model_config = ConfigDict(use_enum_values=True, validate_default=True) + + def __init__(self, **data: Any) -> None: + super().__init__(**data) + self.pk = self.createPartitionKey(self.repository_id, self.collection_id) + + @staticmethod + def createPartitionKey(repository_id: str, collection_id: str) -> str: + return f"{repository_id}#{collection_id}" diff --git a/lambda/models/handler/create_model_handler.py b/lambda/models/handler/create_model_handler.py index 1ff95aa3..a9ffbc1d 100644 --- a/lambda/models/handler/create_model_handler.py +++ b/lambda/models/handler/create_model_handler.py @@ -35,6 +35,8 @@ def __call__(self, create_request: CreateModelRequest) -> CreateModelResponse: if table_item: raise ModelAlreadyExistsError(f"Model '{model_id}' already exists. Please select another name.") + self.validate(create_request) + self._stepfunctions.start_execution( stateMachineArn=os.environ["CREATE_SFN_ARN"], input=create_request.model_dump_json() ) @@ -46,3 +48,37 @@ def __call__(self, create_request: CreateModelRequest) -> CreateModelResponse: } ) return CreateModelResponse(model=lisa_model) + + @staticmethod + def validate(create_request: CreateModelRequest) -> None: + # The below check ensures that the model is LISA hosted + if ( + create_request.containerConfig is not None + and create_request.autoScalingConfig is not None + and create_request.loadBalancerConfig is not None + ): + if create_request.containerConfig.image.baseImage is None: + raise ValueError("Base image must be provided for LISA hosted model.") + + # Validate values relative to current ASG. All conflicting request values have been validated as part of the + # AutoScalingInstanceConfig model validations, so those are not duplicated here. + if create_request.autoScalingConfig is not None: + # Min capacity can't be greater than the deployed ASG's max capacity + if ( + create_request.autoScalingConfig.minCapacity is not None + and create_request.autoScalingConfig.maxCapacity is not None + and create_request.autoScalingConfig.minCapacity > create_request.autoScalingConfig.maxCapacity + ): + raise ValueError( + f"Min capacity cannot exceed ASG max of {create_request.autoScalingConfig.maxCapacity}." + ) + + # Max capacity can't be less than the deployed ASG's min capacity + if ( + create_request.autoScalingConfig.maxCapacity is not None + and create_request.autoScalingConfig.minCapacity is not None + and create_request.autoScalingConfig.maxCapacity < create_request.autoScalingConfig.minCapacity + ): + raise ValueError( + f"Max capacity cannot be less than ASG min of {create_request.autoScalingConfig.minCapacity}." + ) diff --git a/lambda/repository/lambda_functions.py b/lambda/repository/lambda_functions.py index 912e3a19..58e7549b 100644 --- a/lambda/repository/lambda_functions.py +++ b/lambda/repository/lambda_functions.py @@ -22,19 +22,23 @@ import requests from botocore.config import Config from lisapy.langchain import LisaOpenAIEmbeddings +from models.domain_objects import IngestionType, RagDocument +from repository.rag_document_repo import RagDocumentRepository from utilities.common_functions import api_wrapper, get_cert_path, get_id_token, retry_config +from utilities.exceptions import HTTPException from utilities.file_processing import process_record from utilities.validation import validate_model_name, ValidationError -from utilities.vector_store import get_vector_store_client +from utilities.vector_store import find_repository_by_id, get_registered_repositories, get_vector_store_client logger = logging.getLogger(__name__) +region_name = os.environ["AWS_REGION"] session = boto3.Session() -ssm_client = boto3.client("ssm", region_name=os.environ["AWS_REGION"], config=retry_config) -secrets_client = boto3.client("secretsmanager", region_name=os.environ["AWS_REGION"], config=retry_config) -iam_client = boto3.client("iam", region_name=os.environ["AWS_REGION"], config=retry_config) +ssm_client = boto3.client("ssm", region_name, config=retry_config) +secrets_client = boto3.client("secretsmanager", region_name, config=retry_config) +iam_client = boto3.client("iam", region_name, config=retry_config) s3 = session.client( "s3", - region_name=os.environ["AWS_REGION"], + region_name, config=Config( retries={ "max_attempts": 3, @@ -45,6 +49,7 @@ ) lisa_api_endpoint = "" registered_repositories: List[Dict[str, Any]] = [] +doc_repo = RagDocumentRepository(os.environ["RAG_DOCUMENT_TABLE"]) def _get_embeddings(model_name: str, id_token: str) -> LisaOpenAIEmbeddings: @@ -183,16 +188,28 @@ def _get_embeddings_pipeline(model_name: str) -> Any: def list_all(event: dict, context: dict) -> List[Dict[str, Any]]: """Return info on all available repositories. - Currently there is not support for dynamic repositories so only a single OpenSearch repository + Currently, there is no support for dynamic repositories so only a single OpenSearch repository is returned. """ - global registered_repositories - if not registered_repositories: - registered_repositories_response = ssm_client.get_parameter(Name=os.environ["REGISTERED_REPOSITORIES_PS_NAME"]) - registered_repositories = json.loads(registered_repositories_response["Parameter"]["Value"]) + user_groups = json.loads(event["requestContext"]["authorizer"]["groups"]) or [] + registered_repositories = get_registered_repositories() - return registered_repositories + return list( + filter(lambda repository: user_has_group(user_groups, repository["allowedGroups"]), registered_repositories) + ) + + +def user_has_group(user_groups: List[str], allowed_groups: List[str]) -> bool: + """Returns if user groups has at least one intersections with allowed groups. + + If allowed groups is empty this will return True. + """ + + if len(allowed_groups) > 0: + return len(set(user_groups).intersection(set(allowed_groups))) > 0 + else: + return True @api_wrapper @@ -200,20 +217,36 @@ def similarity_search(event: dict, context: dict) -> Dict[str, Any]: """Return documents matching the query. Conducts similarity search against the vector store returning the top K - documents based on the specified query. 'topK' can be set as an optional - querystring parameter, if it is not specified the top 3 documents will be - returned. + documents based on the specified query. + + Args: + event (dict): The Lambda event object containing: + - queryStringParameters.modelName: Name of the embedding model + - queryStringParameters.query: Search query text + - queryStringParameters.repositoryType: Type of repository + - queryStringParameters.topK (optional): Number of results to return (default: 3) + context (dict): The Lambda context object + + Returns: + Dict[str, Any]: A dictionary containing: + - docs: List of matching documents with their content and metadata + + Raises: + ValidationError: If required parameters are missing or invalid """ query_string_params = event["queryStringParameters"] model_name = query_string_params["modelName"] query = query_string_params["query"] - repository_type = query_string_params["repositoryType"] top_k = query_string_params.get("topK", 3) + repository_id = event["pathParameters"]["repositoryId"] + + repository = find_repository_by_id(repository_id) + ensure_repository_access(event, repository) id_token = get_id_token(event) embeddings = _get_embeddings(model_name=model_name, id_token=id_token) - vs = get_vector_store_client(repository_type, index=model_name, embeddings=embeddings) + vs = get_vector_store_client(repository_id, index=model_name, embeddings=embeddings) docs = vs.similarity_search( query, k=top_k, @@ -225,53 +258,166 @@ def similarity_search(event: dict, context: dict) -> Dict[str, Any]: return doc_return +def ensure_repository_access(event: dict[str, Any], repository: dict[str, Any]) -> None: + "Ensures a user has access to the repository or else raises an HTTPException" + user_groups = json.loads(event["requestContext"]["authorizer"]["groups"]) or [] + if not user_has_group(user_groups, repository["allowedGroups"]): + raise HTTPException(status_code=403, message="User does not have permission to access this repository") + + @api_wrapper -def purge_document(event: dict, context: dict) -> Dict[str, Any]: - """Purge all records related to the specified document from the RAG repository.""" - user_id = event["requestContext"]["authorizer"]["username"] - repository_id = event["pathParameters"]["repositoryId"] - document_id = event["pathParameters"]["sessionId"] +def delete_document(event: dict, context: dict) -> Dict[str, Any]: + """Purge all records related to the specified document from the RAG repository. If a documentId is supplied, a + single document will be removed. If a documentName is supplied, all documents with that name will be removed - logger.info( - f"Purging records associated with document {document_id} " - f"(requesting user: {user_id}), repository: {repository_id}" - ) + Args: + event (dict): The Lambda event object containing: + - pathParameters.repositoryId: The repository id of VectorStore + - queryStringParameters.collectionId: The collection identifier + - queryStringParameters.repositoryType: Type of repository of VectorStore + - queryStringParameters.documentId (optional): Name of document to purge + - queryStringParameters.documentName (optional): Name of document to purge + context (dict): The Lambda context object + + Returns: + Dict[str, Any]: A dictionary containing: + - documentName (str): Name of the purged document + - recordsPurged (int): Number of records purged from VectorStore + + Raises: + ValueError: If document is not found in repository + """ + path_params = event.get("pathParameters", {}) + repository_id = path_params.get("repositoryId") + + query_string_params = event["queryStringParameters"] + collection_id = query_string_params["collectionId"] + document_id = query_string_params.get("documentId") + document_name = query_string_params.get("documentName") + + if not document_id and not document_name: + raise ValidationError("Either documentId or documentName must be specified") + if document_id and document_name: + raise ValidationError("Only one of documentId or documentName must be specified") + + docs = [] + if document_id: + docs = [doc_repo.find_by_id(document_id)] + elif document_name: + docs = doc_repo.find_by_name(repository_id, collection_id, document_name) + + if not docs: + raise ValueError(f"No documents found in repository collection {repository_id}:{collection_id}") + + # Grab all sub document ids related to the parent document(s) + subdoc_ids = [sub_doc for doc in docs for sub_doc in doc.get("sub_docs", [])] + + id_token = get_id_token(event) + embeddings = _get_embeddings(model_name=collection_id, id_token=id_token) + vs = get_vector_store_client(repository_id=repository_id, index=collection_id, embeddings=embeddings) + + vs.delete(ids=subdoc_ids) + + doc_repo.batch_delete(docs) - return {"documentId": document_id, "recordsPurged": 0} + return { + "documentName": docs[0].get("document_name"), + "removedDocuments": len(docs), + "removedDocumentChunks": len(subdoc_ids), + } @api_wrapper def ingest_documents(event: dict, context: dict) -> dict: - """Ingest a set of documents into the specified repository.""" + """Ingest documents into the RAG repository. + + Args: + event (dict): The Lambda event object containing: + - body.embeddingModel.modelName: Document collection id + - body.keys: List of s3 keys to ingest + - pathParameters.repositoryId: Repository id (VectorStore) + - queryStringParameters.repositoryType: Repository type (VectorStore) + - queryStringParameters.chunkSize (optional): Size of text chunks + - queryStringParameters.chunkOverlap (optional): Overlap between chunks + context (dict): The Lambda context object + + Returns: + dict: A dictionary containing: + - ids (list): List of generated document IDs + - count (int): Total number of documents ingested + + Raises: + ValidationError: If required parameters are missing or invalid + """ body = json.loads(event["body"]) embedding_model = body["embeddingModel"] model_name = embedding_model["modelName"] + path_params = event.get("pathParameters", {}) + repository_id = path_params.get("repositoryId") + query_string_params = event["queryStringParameters"] - repository_type = query_string_params["repositoryType"] chunk_size = int(query_string_params["chunkSize"]) if "chunkSize" in query_string_params else None chunk_overlap = int(query_string_params["chunkOverlap"]) if "chunkOverlap" in query_string_params else None + logger.info(f"using repository {repository_id}") - docs = process_record(s3_keys=body["keys"], chunk_size=chunk_size, chunk_overlap=chunk_overlap) + repository = find_repository_by_id(repository_id) + ensure_repository_access(event, repository) + + keys = body["keys"] + docs = process_record(s3_keys=keys, chunk_size=chunk_size, chunk_overlap=chunk_overlap) texts = [] # list of strings metadatas = [] # list of dicts + all_ids = [] + id_token = get_id_token(event) + embeddings = _get_embeddings(model_name=model_name, id_token=id_token) + vs = get_vector_store_client(repository_id, index=model_name, embeddings=embeddings) + # Batch document ingestion one parent document at a time for doc_list in docs: + document_name = doc_list[0].metadata.get("name") + doc_source = doc_list[0].metadata.get("source") for doc in doc_list: texts.append(doc.page_content) metadatas.append(doc.metadata) + # Ingest document into vector store + ids = vs.add_texts(texts=texts, metadatas=metadatas) - id_token = get_id_token(event) - embeddings = _get_embeddings(model_name=model_name, id_token=id_token) - vs = get_vector_store_client(repository_type, index=model_name, embeddings=embeddings) - ids = vs.add_texts(texts=texts, metadatas=metadatas) - return {"ids": ids, "count": len(ids)} + # Add document to RagDocTable + doc_entity = RagDocument( + repository_id=repository_id, + collection_id=model_name, + document_name=document_name, + source=doc_source, + sub_docs=ids, + ingestion_type=IngestionType.MANUAL, + ) + doc_repo.save(doc_entity) + + all_ids.extend(ids) + + return {"ids": all_ids, "count": len(all_ids)} @api_wrapper def presigned_url(event: dict, context: dict) -> dict: - """Generate a pre-signed URL for uploading files to the RAG ingest bucket.""" + """Generate a pre-signed URL for uploading files to the RAG ingest bucket. + + Args: + event (dict): The Lambda event object containing: + - body: The key for the file + - requestContext.authorizer.username: The authenticated username + context (dict): The Lambda context object + + Returns: + dict: A dictionary containing: + - response: The presigned URL response object with upload fields and URL + + Notes: + - URL expires in 3600 seconds (1 hour) + - Maximum file size is 52428800 bytes (50MB) + """ response = "" key = event["body"] @@ -294,3 +440,36 @@ def presigned_url(event: dict, context: dict) -> dict: ExpiresIn=3600, ) return {"response": response} + + +def get_groups(event: Any) -> List[str]: + groups: List[str] = json.loads(event["requestContext"]["authorizer"]["groups"]) + return groups + + +@api_wrapper +def list_docs(event: dict, context: dict) -> List[RagDocument]: + """List all documents for a given repository/collection. + + Args: + event (dict): The Lambda event object containing query parameters + - pathParameters.repositoryId: The repository id to list documents for + - queryStringParameters.collectionId: The collection id to list documents for + context (dict): The Lambda context object + + Returns: + list[RagDocument]: A list of RagDocument objects representing all documents + in the specified collection + + Raises: + KeyError: If collectionId is not provided in queryStringParameters + """ + + path_params = event.get("pathParameters", {}) + repository_id = path_params.get("repositoryId") + + query_string_params = event.get("queryStringParameters", {}) + collection_id = query_string_params.get("collectionId") + + docs: List[RagDocument] = doc_repo.list_all(repository_id, collection_id) + return docs diff --git a/lambda/repository/pipeline_ingest_documents.py b/lambda/repository/pipeline_ingest_documents.py index 816c493d..919b0e09 100644 --- a/lambda/repository/pipeline_ingest_documents.py +++ b/lambda/repository/pipeline_ingest_documents.py @@ -19,17 +19,20 @@ from typing import Any, Dict, List import boto3 +from repository.lambda_functions import RagDocumentRepository from utilities.common_functions import retry_config from utilities.file_processing import process_record from utilities.validation import validate_chunk_params, validate_model_name, validate_repository_type, ValidationError from utilities.vector_store import get_vector_store_client -from .lambda_functions import _get_embeddings_pipeline +from .lambda_functions import _get_embeddings_pipeline, IngestionType, RagDocument logger = logging.getLogger(__name__) session = boto3.Session() ssm_client = boto3.client("ssm", region_name=os.environ["AWS_REGION"], config=retry_config) +doc_repo = RagDocumentRepository(os.environ["RAG_DOCUMENT_TABLE"]) + def batch_texts(texts: List[str], metadatas: List[Dict], batch_size: int = 500) -> list[tuple[list[str], list[dict]]]: """ @@ -110,6 +113,7 @@ def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dic # Prepare texts and metadata texts = [] metadatas = [] + for doc_list in docs: for doc in doc_list: texts.append(doc.page_content) @@ -122,7 +126,7 @@ def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dic # Initialize vector store using model name as index, matching lambda_functions.py pattern vs = get_vector_store_client( - store=repository_type, # Changed from repository_type to store + repository_id, index=embedding_model, # Use model name as index to match lambda_functions.py embeddings=embeddings, ) @@ -147,6 +151,17 @@ def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dic logger.info(f"Successfully processed {len(all_ids)} chunks from {s3_key} for repository {repository_id}") + # Store RagDocument entry in Document Table + doc_entity = RagDocument( + repository_id=repository_id, + collection_id=embedding_model, + document_name=key, + source=docs[0][0].metadata.get("source"), + sub_docs=all_ids, + ingestion_type=IngestionType.AUTO, + ) + doc_repo.save(doc_entity) + return { "message": f"Successfully processed document {s3_key}", "repository_id": repository_id, diff --git a/lambda/repository/rag_document_repo.py b/lambda/repository/rag_document_repo.py new file mode 100644 index 00000000..c16ae5be --- /dev/null +++ b/lambda/repository/rag_document_repo.py @@ -0,0 +1,192 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from typing import Dict, List + +import boto3 +from boto3.dynamodb.conditions import Key +from botocore.exceptions import ClientError +from models.domain_objects import RagDocument + +logger = logging.getLogger(__name__) + + +class RagDocumentRepository: + """RAG Document repository for DynamoDB""" + + def __init__(self, table_name: str): + self.dynamodb = boto3.resource("dynamodb") + self.table = self.dynamodb.Table(table_name) + + def delete(self, pk: str, document_id: str) -> None: + """Delete a document using partition key and sort key. + + Args: + pk: Partition key value + document_id: Sort key value + + Returns: + Dict containing the response from DynamoDB + + Raises: + ClientError: If deletion fails + """ + try: + self.table.delete_item(Key={"pk": pk, "document_id": document_id}) + except ClientError as e: + print(f"Error deleting document: {e.response['Error']['Message']}") + raise + + def batch_delete(self, items: List[Dict[str, str]]) -> None: + """Delete multiple documents in a batch. + + Args: + items: List of dictionaries containing pk and document_id pairs + + Raises: + ClientError: If batch deletion fails + """ + try: + with self.table.batch_writer() as batch: + for item in items: + batch.delete_item(Key={"pk": item["pk"], "document_id": item["document_id"]}) + except ClientError as e: + print(f"Error in batch deletion: {e.response['Error']['Message']}") + raise + + def save(self, document: RagDocument) -> RagDocument: + """Save a document to DynamoDB. + + Args: + document: Dictionary containing document attributes + + Returns: + Dict containing the response from DynamoDB + + Raises: + ClientError: If save operation fails + """ + try: + response = self.table.put_item(Item=document.model_dump()) + return response + except ClientError as e: + print(f"Error saving document: {e.response['Error']['Message']}") + raise + + def batch_save(self, documents: List[RagDocument]) -> None: + """Save multiple documents in a batch. + + Args: + documents: List of document dictionaries + + Raises: + ClientError: If batch save operation fails + """ + try: + with self.table.batch_writer() as batch: + for doc in documents: + batch.put_item(Item=doc.model_dump()) + except ClientError as e: + print(f"Error in batch save: {e.response['Error']['Message']}") + raise + + def find_by_id(self, document_id: str) -> RagDocument: + """Query documents using GSI. + + Args: + document_id: Document ID to query + index_name: Name of the GSI + + Returns: + List of matching documents + + Raises: + ClientError: If query operation fails + """ + try: + response = self.table.query( + IndexName="document_index", + KeyConditionExpression="document_id = :document_id", + ExpressionAttributeValues={":document_id": document_id}, + ) + docs = response.get("Items") + if not docs: + raise KeyError(f"Document not found for document_id {document_id}") + if len(docs) > 1: + raise ValueError(f"Multiple items found for document_id {document_id}") + + logging.info(docs[0]) + + return docs[0] + except ClientError as e: + print(f"Error querying document: {e.response['Error']['Message']}") + raise + + def find_by_name(self, repository_id: str, collection_id: str, document_name: str) -> list[RagDocument]: + """Get a list of documents from the RagDocTable by name. + + Args: + document_name (str): The name of the documents to retrieve + repository_id (str): The repository id to list documents for + collection_id (str): The collection id to list documents for + + Returns: + list[RagDocument]: A list of document objects matching the specified name + + Raises: + KeyError: If no documents are found with the specified name + """ + pk = RagDocument.createPartitionKey(repository_id, collection_id) + response = self.table.query( + KeyConditionExpression=Key("pk").eq(pk), FilterExpression=Key("document_name").eq(document_name) + ) + docs: list[RagDocument] = response["Items"] + + # Handle paginated Dynamo results + while "LastEvaluatedKey" in response: + response = self.table.query( + KeyConditionExpression=Key("pk").eq(pk), + FilterExpression=Key("document_name").eq(document_name), + ExclusiveStartKey=response["LastEvaluatedKey"], + ) + docs.extend(response["Items"]) + + return docs + + def list_all(self, repository_id: str, collection_id: str) -> List[RagDocument]: + """List all documents in a collection. + + Args: + repository_id: Repository ID + collection_id: Collection ID + + Returns: + List of documents + """ + pk = RagDocument.createPartitionKey(repository_id, collection_id) + response = self.table.query( + KeyConditionExpression=Key("pk").eq(pk), + ) + docs: List[RagDocument] = response["Items"] + + # Handle paginated Dynamo results + while "LastEvaluatedKey" in response: + response = self.table.query( + KeyConditionExpression=Key("pk").eq(pk), + ExclusiveStartKey=response["LastEvaluatedKey"], + ) + docs.extend(response["Items"]) + + return docs diff --git a/lambda/repository/state_machine/pipeline_ingest_documents.py b/lambda/repository/state_machine/pipeline_ingest_documents.py index ec681144..ee1cd6f4 100644 --- a/lambda/repository/state_machine/pipeline_ingest_documents.py +++ b/lambda/repository/state_machine/pipeline_ingest_documents.py @@ -18,7 +18,11 @@ import boto3 from models.document_processor import DocumentProcessor +from models.domain_objects import IngestionType, RagDocument from models.vectorstore import VectorStore +from repository.lambda_functions import RagDocumentRepository + +doc_repo = RagDocumentRepository(os.environ["RAG_DOCUMENT_TABLE"]) def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dict[str, Any]: @@ -42,6 +46,7 @@ def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dic chunk_overlap = int(os.environ["CHUNK_OVERLAP"]) embedding_model = os.environ["EMBEDDING_MODEL"] collection_name = os.environ["COLLECTION_NAME"] + repository_id = os.environ["REPOSITORY_ID"] # Initialize document processor and vectorstore doc_processor = DocumentProcessor() @@ -54,9 +59,21 @@ def handle_pipeline_ingest_documents(event: Dict[str, Any], context: Any) -> Dic # Chunk document chunks = doc_processor.chunk_text(text=content, chunk_size=chunk_size, chunk_overlap=chunk_overlap) + source = f"s3://{bucket}/{key}" # Store chunks in vectorstore - vectorstore.add_texts(texts=chunks, metadata={"source": f"s3://{bucket}/{key}"}) + ids = vectorstore.add_texts(texts=chunks, metadata={"source": source}) + + # Store in DocTable + doc_entity = RagDocument( + repository_id=repository_id, + collection_id=collection_name, + document_name=key, + source=source, + sub_docs=ids, + ingestion_type=IngestionType.AUTO, + ) + doc_repo.save(doc_entity) return { "statusCode": 200, diff --git a/lambda/utilities/exceptions.py b/lambda/utilities/exceptions.py index 0571d77b..ae88844c 100644 --- a/lambda/utilities/exceptions.py +++ b/lambda/utilities/exceptions.py @@ -17,3 +17,10 @@ class RagUploadException(Exception): """RAG upload error exception.""" + + +class HTTPException(Exception): + def __init__(self, status_code: int = 400, message: str = "Bad Request") -> None: + self.http_status_code = status_code + self.message = message + super().__init__(self.message) diff --git a/lambda/utilities/file_processing.py b/lambda/utilities/file_processing.py index f82640c2..98c5776b 100644 --- a/lambda/utilities/file_processing.py +++ b/lambda/utilities/file_processing.py @@ -33,8 +33,8 @@ s3 = session.client("s3") -def _get_metadata(s3_uri: str) -> dict: - return {"source": s3_uri} +def _get_metadata(s3_uri: str, name: str) -> dict: + return {"source": s3_uri, "name": name} def _get_s3_uri(bucket: str, key: str) -> str: @@ -153,7 +153,11 @@ def process_record( raise e s3_uri = _get_s3_uri(bucket=bucket, key=key) extracted_text = _extract_text_by_content_type(content_type=content_type, s3_object=s3_object) - docs = [Document(page_content=extracted_text, metadata=_get_metadata(s3_uri=s3_uri))] - chunks.append(_generate_chunks(docs, chunk_size=chunk_size, chunk_overlap=chunk_overlap)) + docs = [Document(page_content=extracted_text, metadata=_get_metadata(s3_uri=s3_uri, name=key))] + doc_chunks = _generate_chunks(docs, chunk_size=chunk_size, chunk_overlap=chunk_overlap) + # Update part number of doc metadata + for i, doc in enumerate(doc_chunks): + doc.metadata["part"] = i + 1 + chunks.append(doc_chunks) return chunks diff --git a/lambda/utilities/vector_store.py b/lambda/utilities/vector_store.py index 4f53598e..f5d4b21e 100644 --- a/lambda/utilities/vector_store.py +++ b/lambda/utilities/vector_store.py @@ -16,6 +16,7 @@ import json import logging import os +from typing import Any, Dict, List import boto3 import create_env_variables # noqa: F401 @@ -32,14 +33,44 @@ session = boto3.Session() ssm_client = boto3.client("ssm", region_name=os.environ["AWS_REGION"], config=retry_config) secretsmanager_client = boto3.client("secretsmanager", region_name=os.environ["AWS_REGION"], config=retry_config) +registered_repositories: List[Dict[str, Any]] = [] -def get_vector_store_client(store: str, index: str, embeddings: Embeddings) -> VectorStore: +def get_registered_repositories() -> List[dict]: + """Get a list of all registered RAG repositories.""" + global registered_repositories + if not registered_repositories: + registered_repositories_response = ssm_client.get_parameter(Name=os.environ["REGISTERED_REPOSITORIES_PS_NAME"]) + registered_repositories = json.loads(registered_repositories_response["Parameter"]["Value"]) + + return registered_repositories + + +def find_repository_by_id(repository_id: str) -> Dict[str, Any]: + """Find a RAG repository by id.""" + repository = next( + (repository for repository in get_registered_repositories() if repository["repositoryId"] == repository_id), + None, + ) + if repository is None: + raise ValueError(f"Repository with ID '{repository_id}' not found") + + return repository + + +def get_vector_store_client(repository_id: str, index: str, embeddings: Embeddings) -> VectorStore: """Return Langchain VectorStore corresponding to the specified store. Creates a langchain vector store based on the specified embeddigs adapter and backing store. """ - if store == "opensearch": + + repository = find_repository_by_id(repository_id) + repository_type = repository.get("type", None) + + prefix = os.environ["REGISTERED_REPOSITORIES_PS_PREFIX"] + connection_info = ssm_client.get_parameter(Name=f"{prefix}{repository_id}") + + if repository_type == "opensearch": service = "es" session = boto3.Session() credentials = session.get_credentials() @@ -52,11 +83,7 @@ def get_vector_store_client(store: str, index: str, embeddings: Embeddings) -> V session_token=credentials.token, ) - global opensearch_endpoint - - if not opensearch_endpoint: - opensearch_param_response = ssm_client.get_parameter(Name=os.environ["OPENSEARCH_ENDPOINT_PS_NAME"]) - opensearch_endpoint = f'https://{opensearch_param_response["Parameter"]["Value"]}' + opensearch_endpoint = f'https://{connection_info["Parameter"]["Value"]}' return OpenSearchVectorSearch( opensearch_url=opensearch_endpoint, @@ -69,11 +96,8 @@ def get_vector_store_client(store: str, index: str, embeddings: Embeddings) -> V connection_class=RequestsHttpConnection, ) - elif store == "pgvector": - connection_info = json.loads( - ssm_client.get_parameter(Name=os.environ["RDS_CONNECTION_INFO_PS_NAME"])["Parameter"]["Value"] - ) - + elif repository_type == "pgvector": + connection_info = json.loads(connection_info["Parameter"]["Value"]) secrets_response = secretsmanager_client.get_secret_value(SecretId=connection_info["passwordSecretId"]) password = json.loads(secrets_response["SecretString"])["password"] @@ -91,4 +115,4 @@ def get_vector_store_client(store: str, index: str, embeddings: Embeddings) -> V embedding_function=embeddings, ) - raise ValueError(f"Unrecognized RAG store: '{store}'") + raise ValueError(f"Unrecognized RAG store: '{repository_id}'") diff --git a/lib/api-base/authorizer.ts b/lib/api-base/authorizer.ts index 6008ff8d..bc9ba813 100644 --- a/lib/api-base/authorizer.ts +++ b/lib/api-base/authorizer.ts @@ -18,7 +18,7 @@ import * as cdk from 'aws-cdk-lib'; import { RequestAuthorizer, IdentitySource } from 'aws-cdk-lib/aws-apigateway'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { IRole } from 'aws-cdk-lib/aws-iam'; -import { Code, Function, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Code, Function, LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; @@ -27,6 +27,7 @@ import { createCdkId } from '../core/utils'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; import { Vpc } from '../networking/vpc'; import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { getDefaultRuntime } from './utils'; /** * Properties for RestApiGateway Construct. @@ -37,7 +38,7 @@ import { Queue } from 'aws-cdk-lib/aws-sqs'; * @property {ISecurityGroup[]} securityGroups - Security groups for Lambdas * @property {Map} importedSubnets for Lambdas */ -type AuthorizerProps = { +export type AuthorizerProps = { role?: IRole; vpc: Vpc; securityGroups: ISecurityGroup[]; @@ -81,7 +82,7 @@ export class CustomAuthorizer extends Construct { queueName: 'AuthorizerLambdaDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'authorizer.lambda_functions.lambda_handler', functionName: `${cdk.Stack.of(this).stackName}-lambda-authorizer`, code: Code.fromAsset('./lambda'), diff --git a/lib/api-base/ecsCluster.ts b/lib/api-base/ecsCluster.ts index f7b2c2b0..b53c4d87 100644 --- a/lib/api-base/ecsCluster.ts +++ b/lib/api-base/ecsCluster.ts @@ -177,6 +177,13 @@ export class ECSCluster extends Construct { environment.SSL_CERT_FILE = config.certificateAuthorityBundle; } + // Retrieve execution role if it has been overridden + const executionRole = config.roles ? Role.fromRoleArn( + this, + createCdkId([ecsConfig.identifier, 'ER']), + StringParameter.valueForStringParameter(this, `${config.deploymentPrefix}/roles/${ecsConfig.identifier}EX`), + ) : undefined; + // Create ECS task definition const taskRole = Role.fromRoleArn( this, @@ -185,8 +192,9 @@ export class ECSCluster extends Construct { ); const taskDefinition = new Ec2TaskDefinition(this, createCdkId([ecsConfig.identifier, 'Ec2TaskDefinition']), { family: createCdkId([config.deploymentName, ecsConfig.identifier], 32, 2), - taskRole: taskRole, - volumes: volumes, + volumes, + ...(taskRole && {taskRole}), + ...(executionRole && {executionRole}), }); // Add container to task definition diff --git a/lib/api-base/fastApiContainer.ts b/lib/api-base/fastApiContainer.ts index 8509f974..a7870480 100644 --- a/lib/api-base/fastApiContainer.ts +++ b/lib/api-base/fastApiContainer.ts @@ -68,7 +68,7 @@ export class FastApiContainer extends Construct { const { config, securityGroup, tokenTable, vpc } = props; const buildArgs: Record | undefined = { - BASE_IMAGE: 'python:3.10', + BASE_IMAGE: 'python:3.11', PYPI_INDEX_URL: config.pypiConfig.indexUrl, PYPI_TRUSTED_HOST: config.pypiConfig.trustedHost, LITELLM_CONFIG: yamlDump(config.litellmConfig), @@ -115,7 +115,7 @@ export class FastApiContainer extends Construct { buildArgs, containerConfig: { image: { - baseImage: 'python:3.10', + baseImage: 'python:3.11', path: 'lib/serve/rest-api', type: EcsSourceType.ASSET }, diff --git a/lib/api-base/index.ts b/lib/api-base/index.ts index 613f40d3..890db067 100644 --- a/lib/api-base/index.ts +++ b/lib/api-base/index.ts @@ -14,5 +14,5 @@ limitations under the License. */ -export { RestApiGateway } from './rest-api-gw'; -export { Authorizer } from './authorizer'; +export { RestApiGateway, RestApiGatewayProps } from './rest-api-gw'; +export { CustomAuthorizer, AuthorizerProps } from './authorizer'; diff --git a/lib/api-base/rest-api-gw.ts b/lib/api-base/rest-api-gw.ts index f0ae0b53..0042dadb 100644 --- a/lib/api-base/rest-api-gw.ts +++ b/lib/api-base/rest-api-gw.ts @@ -22,7 +22,7 @@ import { BaseProps } from '../schema'; /** * Properties for RestApiGateway Construct. */ -type RestApiGatewayProps = {} & BaseProps; +export type RestApiGatewayProps = {} & BaseProps; /** * RestApiGateway Stack. diff --git a/lib/api-base/utils.ts b/lib/api-base/utils.ts index 19b11e15..1ff641b0 100644 --- a/lib/api-base/utils.ts +++ b/lib/api-base/utils.ts @@ -156,3 +156,7 @@ function getOrCreateResource (scope: Construct, parentResource: IResource, path: } return resource; } + +export function getDefaultRuntime (): Runtime{ + return Runtime.PYTHON_3_11; +} diff --git a/lib/chat/api/configuration.ts b/lib/chat/api/configuration.ts index 7e95eccb..9655c21b 100644 --- a/lib/chat/api/configuration.ts +++ b/lib/chat/api/configuration.ts @@ -17,16 +17,16 @@ import { IAuthorizer, RestApi } from 'aws-cdk-lib/aws-apigateway'; import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; -import { Role } from 'aws-cdk-lib/aws-iam'; -import { LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; -import { PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; +import { getDefaultRuntime, PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; import { BaseProps } from '../../schema'; import { createLambdaRole } from '../../core/utils'; import { Vpc } from '../../networking/vpc'; import { AwsCustomResource, PhysicalResourceId } from 'aws-cdk-lib/custom-resources'; +import { IRole } from 'aws-cdk-lib/aws-iam'; /** * Properties for ConfigurationApi Construct. @@ -78,7 +78,7 @@ export class ConfigurationApi extends Construct { removalPolicy: config.removalPolicy, }); - const lambdaRole: Role = createLambdaRole(this, config.deploymentName, 'ConfigurationApi', configTable.tableArn); + const lambdaRole: IRole = createLambdaRole(this, config.deploymentName, 'ConfigurationApi', configTable.tableArn, config.roles?.LambdaConfigurationApiExecutionRole); // Populate the App Config table with default config const date = new Date(); @@ -156,7 +156,7 @@ export class ConfigurationApi extends Construct { './lambda', [commonLambdaLayer], f, - Runtime.PYTHON_3_10, + getDefaultRuntime(), vpc, securityGroups, lambdaRole, diff --git a/lib/chat/api/session.ts b/lib/chat/api/session.ts index 25bc9fd0..61c767ed 100644 --- a/lib/chat/api/session.ts +++ b/lib/chat/api/session.ts @@ -16,13 +16,13 @@ import { IAuthorizer, RestApi } from 'aws-cdk-lib/aws-apigateway'; import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import { IRole } from 'aws-cdk-lib/aws-iam'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; -import { Role } from 'aws-cdk-lib/aws-iam'; -import { LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; -import { PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; +import { getDefaultRuntime, PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; import { BaseProps } from '../../schema'; import { createLambdaRole } from '../../core/utils'; import { Vpc } from '../../networking/vpc'; @@ -146,7 +146,7 @@ export class SessionApi extends Construct { }, ]; - const lambdaRole: Role = createLambdaRole(this, config.deploymentName, 'SessionApi', sessionTable.tableArn); + const lambdaRole: IRole = createLambdaRole(this, config.deploymentName, 'SessionApi', sessionTable.tableArn, config.roles?.LambdaExecutionRole); apis.forEach((f) => { const lambdaFunction = registerAPIEndpoint( @@ -156,7 +156,7 @@ export class SessionApi extends Construct { './lambda', [commonLambdaLayer], f, - Runtime.PYTHON_3_10, + getDefaultRuntime(), vpc, securityGroups, lambdaRole, diff --git a/lib/core/api_base.ts b/lib/core/api_base.ts index f50ea17f..09bac891 100644 --- a/lib/core/api_base.ts +++ b/lib/core/api_base.ts @@ -21,6 +21,7 @@ import { Construct } from 'constructs'; import { CustomAuthorizer } from '../api-base/authorizer'; import { BaseProps } from '../schema'; import { Vpc } from '../networking/vpc'; +import { Role } from 'aws-cdk-lib/aws-iam'; type LisaApiBaseStackProps = { vpc: Vpc; @@ -63,6 +64,10 @@ export class LisaApiBaseStack extends Stack { config: config, securityGroups: [vpc.securityGroups.lambdaSg], vpc, + ...(config.roles && + { + role: Role.fromRoleName(this, 'AuthorizerRole', config.roles.RestApiAuthorizerRole), + }) }); this.restApi = restApi; diff --git a/lib/core/api_deployment.ts b/lib/core/api_deployment.ts index 83ac3586..98d1fffa 100644 --- a/lib/core/api_deployment.ts +++ b/lib/core/api_deployment.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { Stack, StackProps } from 'aws-cdk-lib'; +import { CfnOutput, Stack, StackProps, Aws } from 'aws-cdk-lib'; import { Deployment, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { Construct } from 'constructs'; @@ -42,5 +42,10 @@ export class LisaApiDeploymentStack extends Stack { // Hack to allow deploying to an existing stage // https://github.com/aws/aws-cdk/issues/25582 (deployment as any).resource.stageName = config.deploymentStage; + + new CfnOutput(this, 'ApiUrl', { + value: `https://${restApiId}.execute-api.${this.region}.${Aws.URL_SUFFIX}/${config.deploymentStage}`, + description: 'API Gateway URL' + }); } } diff --git a/lib/core/iam/roles.ts b/lib/core/iam/roles.ts new file mode 100644 index 00000000..c880c284 --- /dev/null +++ b/lib/core/iam/roles.ts @@ -0,0 +1,74 @@ +/** + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +export const ROLE = 'Role'; + +/** + * List of all roles used for overrides with their corresponding RoleId + */ +export enum Roles { + DOCKER_IMAGE_BUILDER_DEPLOYMENT_ROLE = 'DockerImageBuilderDeploymentRole', + DOCKER_IMAGE_BUILDER_EC2_ROLE = 'DockerImageBuilderEC2Role', + DOCKER_IMAGE_BUILDER_ROLE = 'DockerImageBuilderRole', + DOCS_DEPLOYER_ROLE = 'DocsDeployerRole', + DOCS_ROLE = 'DocsRole', + ECS_MODEL_DEPLOYER_ROLE = 'ECSModelDeployerRole', + ECS_MODEL_TASK_ROLE = 'ECSModelTaskRole', + ECS_REST_API_EX_ROLE = 'ECSRestApiExRole', + ECS_REST_API_ROLE = 'ECSRestApiRole', + LAMBDA_CONFIGURATION_API_EXECUTION_ROLE = 'LambdaConfigurationApiExecutionRole', + LAMBDA_EXECUTION_ROLE = 'LambdaExecutionRole', + MODEL_API_ROLE = 'ModelApiRole', + MODEL_SFN_LAMBDA_ROLE = 'ModelsSfnLambdaRole', + MODEL_SFN_ROLE = 'ModelSfnRole', + RAG_LAMBDA_EXECUTION_ROLE = 'LisaRagLambdaExecutionRole', + REST_API_AUTHORIZER_ROLE = 'RestApiAuthorizerRole', + S3_READER_ROLE = 'S3ReaderRole', + UI_DEPLOYMENT_ROLE = 'UIDeploymentRole', +} + +/** + * This is the RoleName used with roles, which can differ from the RoleNameId. This represents the existing deployed names for backwards compatibility. + */ +export const RoleNames: Record = { + [Roles.DOCKER_IMAGE_BUILDER_DEPLOYMENT_ROLE]: 'DockerImageBuilderDeploymentRole', + [Roles.DOCKER_IMAGE_BUILDER_EC2_ROLE]: 'DockerImageBuilderEC2Role', + [Roles.DOCKER_IMAGE_BUILDER_ROLE]: 'DockerImageBuilderRole', + [Roles.DOCS_DEPLOYER_ROLE]: 'DocsDeployerRole', + [Roles.DOCS_ROLE]: 'DocsRole', + [Roles.ECS_MODEL_DEPLOYER_ROLE]: 'ECSModelDeployerRole', + [Roles.ECS_MODEL_TASK_ROLE]: 'ECSModelTaskRole', + [Roles.ECS_REST_API_EX_ROLE]: 'ECSRestApiExRole', + [Roles.ECS_REST_API_ROLE]: 'ECSRestApiRole', + [Roles.LAMBDA_CONFIGURATION_API_EXECUTION_ROLE]: 'LambdaConfigurationApiExecutionRole', + [Roles.LAMBDA_EXECUTION_ROLE]: 'LambdaExecutionRole', + [Roles.MODEL_API_ROLE]: 'ModelApiRole', + [Roles.MODEL_SFN_LAMBDA_ROLE]: 'ModelsSfnLambdaRole', + [Roles.MODEL_SFN_ROLE]: 'ModelSfnRole', + [Roles.RAG_LAMBDA_EXECUTION_ROLE]: 'RAGRole', + [Roles.REST_API_AUTHORIZER_ROLE]: 'RestApiAuthorizerRole', + [Roles.S3_READER_ROLE]: 'S3ReaderRole', + [Roles.UI_DEPLOYMENT_ROLE]: 'UIDeploymentRole', +}; + +export function getRoleId (key: string): Roles { + const keys = Object.keys(Roles).filter((x) => x === key); + if (keys.length > 0) + return Roles[keys[0] as keyof typeof Roles] as Roles; + else { + throw Error(`No Roles entry exists for ${key}`); + } +} diff --git a/lib/core/layers/index.ts b/lib/core/layers/index.ts index adf1200f..726f96a7 100644 --- a/lib/core/layers/index.ts +++ b/lib/core/layers/index.ts @@ -15,11 +15,12 @@ */ import { BundlingOutput } from 'aws-cdk-lib'; -import { Architecture, Code, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Architecture, Code, LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { Asset } from 'aws-cdk-lib/aws-s3-assets'; import { Construct } from 'constructs'; import { BaseProps } from '../../schema'; +import { getDefaultRuntime } from '../../api-base/utils'; /** * Properties for Layer Construct. @@ -84,7 +85,7 @@ export class Layer extends Construct { const layerAsset = new Asset(this, 'LayerAsset', { path, bundling: { - image: Runtime.PYTHON_3_10.bundlingImage, + image: getDefaultRuntime().bundlingImage, platform: architecture.dockerPlatform, command: ['bash', '-c', `set -e ${args.join(' ')}`], outputType: BundlingOutput.AUTO_DISCOVER, @@ -97,7 +98,7 @@ export class Layer extends Construct { const layer = new LayerVersion(this, 'Layer', { code: layerCode, - compatibleRuntimes: [Runtime.PYTHON_3_10], + compatibleRuntimes: [getDefaultRuntime()], removalPolicy: config.removalPolicy, description: description, }); diff --git a/lib/core/utils.ts b/lib/core/utils.ts index d994ba9a..483b20e9 100644 --- a/lib/core/utils.ts +++ b/lib/core/utils.ts @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -*/ + */ // Utility functions. import * as fs from 'fs'; @@ -20,8 +20,15 @@ import * as path from 'path'; import * as cdk from 'aws-cdk-lib'; -import { Config } from '../schema'; -import { Effect, ManagedPolicy, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { + Effect, + IRole, + ManagedPolicy, + PolicyDocument, + PolicyStatement, + Role, + ServicePrincipal, +} from 'aws-cdk-lib/aws-iam'; import { Construct } from 'constructs'; const IAM_DIR = path.join(__dirname, 'iam'); @@ -36,11 +43,10 @@ type JSONPolicyStatement = { /** * Extract policy statements from JSON file. * - * @param {Config} config - The application configuration. * @param {string} serviceName - AWS service name. * @returns {PolicyStatement[]} - Extracted IAM policy statements. */ -const extractPolicyStatementsFromJson = (config: Config, serviceName: string): PolicyStatement[] => { +const extractPolicyStatementsFromJson = (serviceName: string): PolicyStatement[] => { const statementData = fs.readFileSync(path.join(IAM_DIR, `${serviceName.toLowerCase()}.json`), 'utf8'); const statements = JSON.parse(statementData).Statement; @@ -61,18 +67,22 @@ const extractPolicyStatementsFromJson = (config: Config, serviceName: string): P /** * Wrapper to get IAM policy statements. - * @param {Config} config - The application configuration. * @param {string} serviceName - AWS service name. * @returns {PolicyStatement[]} - Extracted IAM policy statements. */ -export const getIamPolicyStatements = (config: Config, serviceName: string): PolicyStatement[] => { - return extractPolicyStatementsFromJson(config, serviceName); +export const getIamPolicyStatements = (serviceName: string): PolicyStatement[] => { + return extractPolicyStatementsFromJson(serviceName); }; -export const createLambdaRole = (construct: Construct, deploymentName: string, lambdaName: string, tableArn: string = '') => { - return new Role(construct, `Lisa${lambdaName}LambdaExecutionRole`, { +export const createLambdaRole = (construct: Construct, deploymentName: string, lambdaName: string, tableArn: string = '', roleOverride?: string): IRole => { + const roleId = `Lisa${lambdaName}LambdaExecutionRole`; + if (roleOverride) { + return Role.fromRoleName(construct, roleId, roleOverride); + } + + return new Role(construct, roleId, { assumedBy: new ServicePrincipal('lambda.amazonaws.com'), - roleName: createCdkId([deploymentName, `Lisa${lambdaName}LambdaExecutionRole`]), + roleName: createCdkId([deploymentName, roleId]), description: `Role used by LISA ${lambdaName} lambdas to access AWS resources`, managedPolicies: [ ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), @@ -110,14 +120,18 @@ export const createLambdaRole = (construct: Construct, deploymentName: string, l * TODO: Make sure all IDs are valid for AWS resources like ECR, CFN, etc. * * @param {string[]} idParts - The name of the resource. + * @param maxLength + * @param truncationIdx * @throws {Error} Throws an error if the generated CDK ID is longer than 64 characters. * @returns {string} The generated CDK ID for the model resource. */ -export function createCdkId (idParts: string[], maxLength: number = 64, truncationIdx: number = -1): string { +export function createCdkId (idParts: string[], maxLength: number = 64, truncationIdx?: number): string { let cdkId = idParts.join('-'); const length = cdkId.length; if (length > maxLength) { + console.log(`${cdkId} is too long (>${maxLength}). Truncating...`); + truncationIdx = typeof truncationIdx === 'undefined' ? idParts.length - 1 : truncationIdx; idParts[truncationIdx] = idParts[truncationIdx].slice(0, maxLength - length); cdkId = idParts.join('-'); } diff --git a/lib/docs/.vitepress/config.mts b/lib/docs/.vitepress/config.mts index 6f147bc9..cc2048bd 100644 --- a/lib/docs/.vitepress/config.mts +++ b/lib/docs/.vitepress/config.mts @@ -48,7 +48,15 @@ const navLinks = [ { text: 'Usage & Features', link: '/config/usage' }, { text: 'RAG Vector Stores', link: '/config/vector-stores' }, { text: 'Branding', link: '/config/branding' }, - { text: 'Configuration Schema', link: '/config/configuration' }, + { + text: 'Configuration Schema', + link: '/config/configuration', + items: [ + { text: 'VPC & Subnet Overrides', link: '/config/vpc-overrides' }, + { text: 'Security Group Overrides', link: '/config/security-group-overrides' }, + { text: 'Role Overrides', link: '/config/role-overrides' }, + ], + }, ], }, { diff --git a/lib/docs/admin/getting-started.md b/lib/docs/admin/getting-started.md index 31d995a5..a38e0f45 100644 --- a/lib/docs/admin/getting-started.md +++ b/lib/docs/admin/getting-started.md @@ -164,6 +164,7 @@ When deploying for dev and testing you can use a self-signed certificate for the ```bash export REGION= +export DOMAIN= #Optional if not running in 'aws' partition ./scripts/gen-certs.sh aws iam upload-server-certificate --server-certificate-name --certificate-body file://scripts/server.pem --private-key file://scripts/server.key ``` @@ -172,7 +173,7 @@ Update your `config-custom.yaml` with the certificate ARN: ```yaml restApiConfig: - sslCertIamArn: arn:aws:iam:::server-certificate/ + sslCertIamArn: arn::iam:::server-certificate/ ``` ## Step 9: Customize Model Deployment diff --git a/lib/docs/config/role-overrides.md b/lib/docs/config/role-overrides.md new file mode 100644 index 00000000..6eafa8c6 --- /dev/null +++ b/lib/docs/config/role-overrides.md @@ -0,0 +1,1982 @@ +# IAM Role Override Configuration Guide + +## Overview + +This guide explains how to configure IAM role overrides for your environment. Role overrides allow you to customize +permissions and policies for various service components including Lambda functions, ECS tasks, and API Gateway +integrations. By default, LISA will generate all required roles. + +**NOTE** +Some roles cannot be overridden as they aren't exposed via CDK constructs: + +- S3 lifecycle policy roles +- Auto Scaling Group roles attached to ECS clusters + +## Configuration Example + +The example provided is an export from a deployed LISA instance based on Least Privilege Access. + +```json +{ + "RestApiAuthorizerRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ] + } + }, + "RestApiAuthorizerRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "LisaApiAuthorizerAuthorizerLambdaDLQAE1E4673", + "Arn" + ] + } + }, + { + "Action": [ + "secretsmanager:DescribeSecret", + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:secretsmanager:${REGION}:${ACCOUNT}:secret:", + { + "Ref": "LisaApiAuthorizerLisaApiAuthorizermanagementKeyStringParameterParameter5998CD79" + }, + "-??????" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "RestApiAuthorizerRoleDefaultPolicy", + "Roles": [ + { + "Ref": "RestApiAuthorizerRole" + } + ] + } + }, + "ECSRestApiExRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ECSRestApiExRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:GetDownloadUrlForLayer" + ], + "Effect": "Allow", + "Resource": "arn:aws:ecr:${REGION}:${ACCOUNT}:repository/cdk-hnb659fds-container-assets-${ACCOUNT}-${REGION}" + }, + { + "Action": "ecr:GetAuthorizationToken", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "RestApiECSClusterRESTEc2TaskDefinitionRESTContainerLogGroup01AB5F5D", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ECSRestApiExRoleDefaultPolicy", + "Roles": [ + { + "Ref": "ECSRestApiExRole" + } + ] + } + }, + "S3ReaderRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Allows API gateway to proxy static website assets", + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/AmazonS3ReadOnlyAccess" + ] + ] + } + ], + "RoleName": "app-lisa-ui-dev-s3-reader-role" + } + }, + "ECSModelDeployerRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "ECSModelDeployerRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + "arn:aws:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}", + "arn:aws:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}/*" + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ECSModelDeployerRoleDefaultPolicy", + "Roles": [ + { + "Ref": "ECSModelDeployerRole" + } + ] + } + }, + "LambdaExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role used by LISA SessionApi lambdas to access AWS resources", + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:Scan" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaPermissions" + } + ], + "RoleName": "app-LisaSessionApiLambdaExecutionRole" + } + }, + "LambdaExecutionRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SessionApiapplisachatdevsessiondeletesessionDLQ3EEC4880", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SessionApiapplisachatdevsessiondeleteusersessionsDLQ8138C58A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SessionApiapplisachatdevsessiongetsessionDLQAB1127BE", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SessionApiapplisachatdevsessionlistsessionsDLQD00F489B", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "SessionApiapplisachatdevsessionputsessionDLQ2C63E706", + "Arn" + ] + } + ] + }, + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:Scan" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + "/index/*" + ] + ] + } + ] + }, + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:PutItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:UpdateItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + "/index/*" + ] + ] + } + ] + }, + { + "Action": [ + "dynamodb:BatchWriteItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + "dynamodb:PutItem", + "dynamodb:UpdateItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "SessionApiSessionsTableDA695141", + "Arn" + ] + }, + "/index/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaExecutionRoleDefaultPolicy", + "Roles": [ + { + "Ref": "LambdaExecutionRole" + } + ] + } + }, + "LambdaConfigurationApiExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role used by LISA ConfigurationApi lambdas to access AWS resources", + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:Scan" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ConfigurationApiConfigurationTable4B2B7EE1", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ConfigurationApiConfigurationTable4B2B7EE1", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaPermissions" + } + ], + "RoleName": "app-LisaConfigurationApiLambdaExecutionRole" + } + }, + "LambdaConfigurationApiExecutionRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ConfigurationApiapplisachatdevconfigurationgetconfigurationDLQ62A925F5", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ConfigurationApiapplisachatdevconfigurationupdateconfigurationDLQ886900A4", + "Arn" + ] + } + ] + }, + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:Scan" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ConfigurationApiConfigurationTable4B2B7EE1", + "Arn" + ] + }, + { + "Ref": "AWS::NoValue" + } + ] + }, + { + "Action": [ + "dynamodb:BatchWriteItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + "dynamodb:PutItem", + "dynamodb:UpdateItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ConfigurationApiConfigurationTable4B2B7EE1", + "Arn" + ] + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaConfigurationApiExecutionRoleDefaultPolicy", + "Roles": [ + { + "Ref": "LambdaConfigurationApiExecutionRole" + } + ] + } + }, + "RagLambdaExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role used by RAG API lambdas to access AWS resources", + "ManagedPolicyArns": [ + { + "Ref": "appRAGPolicy07A18B09" + } + ], + "RoleName": "app-LisaRagLambdaExecutionRole" + } + }, + "ECSRestApiRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ecs-tasks.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Allow REST API task access to AWS resources", + "ManagedPolicyArns": [ + { + "Ref": "appECSPolicy361D8A62" + } + ], + "RoleName": "app-REST-Role" + } + }, + "DocsDeployerRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "DocsDeployerRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + "arn:${PARTITION}:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}", + "arn:${PARTITION}:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}/*" + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "DocsBucketECEA003F", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "DocsBucketECEA003F", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DocsDeployerRoleDefaultPolicy", + "Roles": [ + { + "Ref": "DocsDeployerRole" + } + ] + } + }, + "DocsRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Allows API gateway to proxy static website assets" + } + }, + "DocsRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "DocsBucketECEA003F", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "DocsBucketECEA003F", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DocsRoleDefaultPolicy", + "Roles": [ + { + "Ref": "DocsRole" + } + ] + } + }, + "UIDeploymentRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "UIDeploymentRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*" + ], + "Effect": "Allow", + "Resource": [ + "arn:${PARTITION}:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}", + "arn:${PARTITION}:s3:::cdk-hnb659fds-assets-${ACCOUNT}-${REGION}/*" + ] + }, + { + "Action": [ + "s3:Abort*", + "s3:DeleteObject*", + "s3:GetBucket*", + "s3:GetObject*", + "s3:List*", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectTagging", + "s3:PutObjectVersionTagging" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "Bucket83908E77", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "UIDeploymentRoleDefaultPolicy", + "Roles": [ + { + "Ref": "UIDeploymentRole" + } + ] + } + }, + "DockerImageBuilderDeploymentRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Resource": "arn:*:iam::*:role/cdk-*" + }, + { + "Action": [ + "ec2:AssignPrivateIpAddresses", + "ec2:CreateNetworkInterface", + "ec2:DeleteNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeSubnets", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaPermissions" + } + ] + } + }, + "DockerImageBuilderEC2Role": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "ec2.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "RoleName": "app-lisa-models-dev-docker-image-builder-ec2-role" + } + }, + "DockerImageBuilderEC2RoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:GetObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApidockerimagebuilderapplisamodelsdevdockerimagebuilderec2bucket08754F14", + "Arn" + ] + }, + "/*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ModelsApidockerimagebuilderapplisamodelsdevdockerimagebuilderec2bucket08754F14", + "Arn" + ] + } + }, + { + "Action": [ + "ecr:BatchCheckLayerAvailability", + "ecr:CompleteLayerUpload", + "ecr:GetAuthorizationToken", + "ecr:InitiateLayerUpload", + "ecr:PutImage", + "ecr:UploadLayerPart" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DockerImageBuilderEC2RoleDefaultPolicy", + "Roles": [ + { + "Ref": "DockerImageBuilderEC2Role" + } + ] + } + }, + "DockerImageBuilderRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "RoleName": "app-lisa-models-dev-docker_image_builder_role" + } + }, + "DockerImageBuilderRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:AssignPrivateIpAddresses", + "ec2:CreateNetworkInterface", + "ec2:CreateTags", + "ec2:DeleteNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeSubnets", + "ec2:RunInstances", + "ec2:UnassignPrivateIpAddresses" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "iam:PassRole", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "DockerImageBuilderEC2Role", + "Arn" + ] + } + }, + { + "Action": "ssm:GetParameter", + "Effect": "Allow", + "Resource": "arn:*:ssm:*::parameter/aws/service/*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DockerImageBuilderRoleDefaultPolicy", + "Roles": [ + { + "Ref": "DockerImageBuilderRole" + } + ] + } + }, + "DockerImageBuilderRoleSQSPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "ModelsApidockerimagebuilderdockerimagebuilderDLQC5B63450", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "DockerImageBuilderRoleSQSPolicy", + "Roles": [ + { + "Ref": "DockerImageBuilderRole" + } + ] + } + }, + "ModelsSfnLambdaRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApiModelTable72B9582E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiModelTable72B9582E", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + }, + { + "Action": [ + "cloudformation:CreateStack", + "cloudformation:DeleteStack", + "cloudformation:DescribeStacks" + ], + "Effect": "Allow", + "Resource": "arn:*:cloudformation:*:*:stack/*" + }, + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApidockerimagebuilderapplisamodelsdevdockerimagebuilder9B580919", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiecsmodeldeployerapplisamodelsdevecsmodeldeployer6051670E", + "Arn" + ] + } + ] + }, + { + "Action": [ + "autoscaling:DescribeAutoScalingGroups", + "autoscaling:UpdateAutoScalingGroup", + "ec2:AssignPrivateIpAddresses", + "ec2:CreateNetworkInterface", + "ec2:DeleteNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeSubnets", + "ec2:UnassignPrivateIpAddresses", + "ecr:DescribeImages" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "ec2:TerminateInstances", + "Condition": { + "StringEquals": { + "aws:ResourceTag/lisa_temporary_instance": "true" + } + }, + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": "ssm:GetParameter", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:${PARTITION}:ssm:${REGION}:${ACCOUNT}:parameter", + { + "Fn::ImportValue": "app-lisa-serve-dev:ExportsOutputRefLisaServeRestApiUriStringParameterF8D56C8BA0E6222B" + } + ] + ] + } + }, + { + "Action": "secretsmanager:GetSecretValue", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:${PARTITION}:secretsmanager:${REGION}:${ACCOUNT}:secret:", + { + "Ref": "SsmParameterValuedevapplisamanagementKeySecretNameC96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "-??????" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaPermissions" + } + ] + } + }, + "ModelsSfnLambdaRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowAddModelToLitellmDLQ3B4AE9BA", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowHandleFailureDLQ17AD8525", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollCreateStackDLQB2DDE435", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollDockerImageAvailableDLQ682A018B", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowSetModelToCreatingDLQ5B85AD0A", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCopyDockerImageDLQ6A0BAD15", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCreateStackDLQ75DFA17E", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromDdbDLQ598B9790", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromLitellmDLQA9867D0B", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteStackDLQ75E2E6A7", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowMonitorDeleteStackDLQ705504AE", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowSetModelToDeletingDLQAEC29C62", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleFinishUpdateDLQD33B3816", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleJobIntakeDLQ64D8E67D", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandlePollCapacityDLQB87E1908", + "Arn" + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ModelsSfnLambdaRoleDefaultPolicy", + "Roles": [ + { + "Ref": "ModelsSfnLambdaRole" + } + ] + } + }, + "ModelApiRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "states.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ModelApiRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "lambda:InvokeFunction", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowAddModelToLitellmFunc6B8DBAE6", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowHandleFailureFunc7CC3D0A8", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollCreateStackFunc3B3660A0", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollDockerImageAvailableFuncF23F9A33", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowSetModelToCreatingFunc4E8D1CA0", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCopyDockerImageFuncE508BA76", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCreateStackFuncCEE91381", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowAddModelToLitellmFunc6B8DBAE6", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowHandleFailureFunc7CC3D0A8", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollCreateStackFunc3B3660A0", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowPollDockerImageAvailableFuncF23F9A33", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowSetModelToCreatingFunc4E8D1CA0", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCopyDockerImageFuncE508BA76", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiCreateModelWorkflowStartCreateStackFuncCEE91381", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromDdbFuncAB2B6BFB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromLitellmFunc75B8FA09", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteStackFunc0B8E2D75", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowMonitorDeleteStackFunc2CE43E62", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowSetModelToDeletingFuncCA1C7F8D", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromDdbFuncAB2B6BFB", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteFromLitellmFunc75B8FA09", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowDeleteStackFunc0B8E2D75", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowMonitorDeleteStackFunc2CE43E62", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiDeleteModelWorkflowSetModelToDeletingFuncCA1C7F8D", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleFinishUpdateFunc92E550FB", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleJobIntakeFuncA1438F67", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandlePollCapacityFunc5376513F", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleFinishUpdateFunc92E550FB", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandleJobIntakeFuncA1438F67", + "Arn" + ] + }, + ":*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiUpdateModelWorkflowHandlePollCapacityFunc5376513F", + "Arn" + ] + }, + ":*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ModelApiRoleDefaultPolicy", + "Roles": [ + { + "Ref": "ModelApiRole" + } + ] + } + }, + "ModelSfnRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Role used by LISA ModelApi lambdas to access AWS resources", + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" + ] + ] + } + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:Scan" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApiModelTable72B9582E", + "Arn" + ] + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "ModelsApiModelTable72B9582E", + "Arn" + ] + }, + "/*" + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "lambdaPermissions" + } + ], + "RoleName": "app-LisaModelApiLambdaExecutionRole" + } + }, + "ModelSfnRoleDefaultPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "ModelsApiapplisamodelsdevmodelsdocsDLQF4F4BAC8", + "Arn" + ] + }, + { + "Fn::GetAtt": [ + "ModelsApiapplisamodelsdevmodelshandlerDLQB5638333", + "Arn" + ] + } + ] + }, + { + "Action": [ + "ssm:DescribeParameters", + "ssm:GetParameter", + "ssm:GetParameterHistory", + "ssm:GetParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:${PARTITION}:ssm:${REGION}:${ACCOUNT}:parameter", + { + "Fn::ImportValue": "app-lisa-serve-dev:ExportsOutputRefLisaServeRestApiUriStringParameterF8D56C8BA0E6222B" + } + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "ModelSfnRoleDefaultPolicy", + "Roles": [ + { + "Ref": "ModelSfnRole" + } + ] + } + } +} + +``` diff --git a/lib/docs/config/security-group-overrides.md b/lib/docs/config/security-group-overrides.md new file mode 100644 index 00000000..dab4d327 --- /dev/null +++ b/lib/docs/config/security-group-overrides.md @@ -0,0 +1,239 @@ +# Security Group Overrides User Guide + +## Overview +This guide explains how to configure security group overrides in your environment. When you enable security group overrides, you'll need to override all security groups that are deployed across your stacks. + +## Prerequisites +Before configuring security groups, you'll need the following values ready: + +- Private VPC ID `[PRIVATE_VPC_ID]` +- Private Subnet CIDR 1/2 `[PRIVATE_SUBNET_CIDR_1, PRIVATE_SUBNET_CIDR_2]` +- Private Isolated Subnet CIDR 1/2 `[PRIVATE_ISOLATED_SUBNET_CIDR_1, PRIVATE_ISOLATED_SUBNET_CIDR_2]` + +## Configuration Example + +```json +{ + "liteLlmDbSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for LiteLLM dynamic model management database", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-LiteLLMScalingSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-LiteLLMScalingSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-LiteLLMScalingSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-LiteLLMScalingSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + }, + "modelSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for ECS model application load balancer", + "GroupName": "app-ECS-ALB-SG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": { + "Fn::GetAtt": [ + "PRIVATE_VPC_ID", + "CidrBlock" + ] + }, + "Description": "Allow VPC traffic on port 80", + "FromPort": 80, + "IpProtocol": "tcp", + "ToPort": 80 + }, + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow any traffic on port 443", + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + }, + "restAlbSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for REST API application load balancer", + "GroupName": "app-RestAPI-ALB-SG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow from anyone on port 443", + "FromPort": 443, + "IpProtocol": "tcp", + "ToPort": 443 + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + }, + "lambdaSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for authorizer and API Lambdas", + "GroupName": "app-Lambda-SG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + }, + "pgVectorSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for RAG PGVector database", + "GroupName": "LISA-PGVector-SG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + }, + "openSearchSecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "Security group for RAG OpenSearch database", + "GroupName": "LISA-OpenSearch-SG", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "SecurityGroupIngress": [ + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_ISOLATED_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_1", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + }, + { + "CidrIp": "PRIVATE_SUBNET_CIDR_2", + "Description": "Allow REST API private subnets to communicate with LISA-PGVectorSg", + "FromPort": 5432, + "IpProtocol": "tcp", + "ToPort": 5432 + } + ], + "VpcId": { + "Ref": "PRIVATE_VPC_ID" + } + } + } +} +``` diff --git a/lib/docs/config/vpc-overrides.md b/lib/docs/config/vpc-overrides.md new file mode 100644 index 00000000..c7024e69 --- /dev/null +++ b/lib/docs/config/vpc-overrides.md @@ -0,0 +1,128 @@ +# VPC and Subnet Configuration Overrides + +## Overview + +This guide will help you configure VPC and subnet overrides for your environment. The configuration allows you to +customize network settings including CIDR blocks, availability zones, and subnet configurations. + +## Network Architecture + +The configuration supports the following subnet types: + +- Public Subnets `[ publicSubnet1, publicSubnet2 ]` +- Private Subnets `[privateSubnet1, privateSubnet2]` +- Private Isolated Subnets `[privateIsolatedSubnet1, privateIsolatedSubnet2 ]` + +## Configuration Example + +Below is a sample VPC and subnet configuration exported from a running LISA deployment. You can use this as a template +for your overrides: + +```json + +{ + "Description": "LISA-networking: app-dev", + "Resources": { + "Vpc": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/22", + "EnableDnsHostnames": true, + "EnableDnsSupport": true + }, + "publicSubnet1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.0.0.0/26", + "MapPublicIpOnLaunch": true + } + }, + "publicSubnet2": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2b", + "CidrBlock": "10.0.0.64/26", + "MapPublicIpOnLaunch": true, + "VpcId": { + "Ref": "VpcVPC8B8C4E4B" + } + } + }, + "VpcVPCprivateIsolatedSubnet1Subnet595DCC9B": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.0.0.128/26", + "MapPublicIpOnLaunch": false, + "VpcId": { + "Ref": "VpcVPC8B8C4E4B" + } + } + }, + "VpcVPCprivateIsolatedSubnet2SubnetFD505B6C": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2b", + "CidrBlock": "10.0.0.192/26", + "MapPublicIpOnLaunch": false, + "VpcId": { + "Ref": "VpcVPC8B8C4E4B" + } + } + }, + "VpcVPCprivateSubnet1Subnet29B9FADC": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.0.1.0/26", + "MapPublicIpOnLaunch": false, + "VpcId": { + "Ref": "VpcVPC8B8C4E4B" + } + } + }, + "VpcVPCprivateSubnet2Subnet63498DC1": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AvailabilityZone": "us-west-2b", + "CidrBlock": "10.0.1.64/26", + "MapPublicIpOnLaunch": false, + "VpcId": { + "Ref": "VpcVPC8B8C4E4B" + } + } + } + } + } +} +``` + +## Required Properties + +For each subnet, you must specify: + +- AvailabilityZone +- CidrBlock +- VpcId +- MapPublicIpOnLaunch (true for public subnets, false for private) + +## Best Practices + +1. Distribute subnets across different Availability Zones for high availability + +2. Ensure CIDR blocks don't overlap between subnets + +3. Plan IP address space according to your workload requirements + +4. Enable DNS support and hostnames in the VPC + +``` +VPC (10.0.0.0/22) +├── Public Subnet 1 (10.0.0.0/26) - AZ a +├── Public Subnet 2 (10.0.0.64/26) - AZ b +├── Private Subnet 1 (10.0.0.128/26) - AZ a +├── Private Subnet 2 (10.0.0.192/26) - AZ b +├── Isolated Subnet 1 (10.0.1.0/26) - AZ a +└── Isolated Subnet 2 (10.0.1.64/26) - AZ b +``` diff --git a/lib/docs/index.ts b/lib/docs/index.ts index 4baab261..bff9bf5e 100644 --- a/lib/docs/index.ts +++ b/lib/docs/index.ts @@ -19,11 +19,12 @@ import * as fs from 'node:fs'; import { CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; import { AwsIntegration, EndpointType, RestApi } from 'aws-cdk-lib/aws-apigateway'; -import { Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; -import { BlockPublicAccess, Bucket, BucketEncryption } from 'aws-cdk-lib/aws-s3'; +import { IRole, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { BlockPublicAccess, Bucket, BucketEncryption, IBucket } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; import { Construct } from 'constructs'; import { BaseProps } from '../schema'; +import { Roles } from '../core/iam/roles'; /** * Properties for DocsStack Construct. @@ -60,18 +61,21 @@ export class LisaDocsStack extends Stack { if (!fs.existsSync(docsPath)) { fs.mkdirSync(docsPath); } + // Deploy local folder to S3 new BucketDeployment(this, 'DeployDocsWebsite', { sources: [Source.asset(docsPath)], destinationBucket: docsBucket, + ...(config.roles?.DocsDeployerRole && + { + role: Role.fromRoleName(this, Roles.DOCS_DEPLOYER_ROLE, config.roles.DocsDeployerRole), + }) }); // REST API GW S3 role - const apiGatewayRole = new Role(this, `${Stack.of(this).stackName}-s3-reader-role`, { - assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), - description: 'Allows API gateway to proxy static website assets', - }); - docsBucket.grantRead(apiGatewayRole); + const apiGatewayRole = config.roles?.DocsRole ? + Role.fromRoleName(this, Roles.DOCS_ROLE, config.roles.DocsRole) : + this.createApiRole(docsBucket); // Create API Gateway const api = new RestApi(this, 'DocsApi', { @@ -175,4 +179,19 @@ export class LisaDocsStack extends Stack { description: 'API Gateway URL', }); } + + /** + * Create API Gateway role to access S3 bucket + * @param bucket + * @returns role + */ + createApiRole (bucket: IBucket): IRole { + const role = new Role(this, `${Stack.of(this).stackName}-s3-reader-role`, { + assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), + description: 'Allows API gateway to proxy static website assets', + }); + bucket.grantRead(role); + return role; + } + } diff --git a/lib/docs/package-lock.json b/lib/docs/package-lock.json index 587c5e5a..bf4b3c01 100644 --- a/lib/docs/package-lock.json +++ b/lib/docs/package-lock.json @@ -1796,9 +1796,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { diff --git a/lib/iam_stack.ts b/lib/iam_stack.ts index 323c8d1c..64230ccf 100644 --- a/lib/iam_stack.ts +++ b/lib/iam_stack.ts @@ -12,17 +12,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -*/ - -// LISA-serve Stack. + */ import { Stack, StackProps } from 'aws-cdk-lib'; -import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { IManagedPolicy, IRole, ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { NagSuppressions } from 'cdk-nag'; import { Construct } from 'constructs'; import { createCdkId, getIamPolicyStatements } from './core/utils'; -import { BaseProps } from './schema'; +import { BaseProps, Config } from './schema'; +import { getRoleId, ROLE, Roles } from './core/iam/roles'; /** * Properties for the LisaServeIAMStack Construct. @@ -30,16 +29,16 @@ import { BaseProps } from './schema'; type LisaIAMStackProps = {} & BaseProps & StackProps; /** - * Properties for the Task Role Information interface. - * @param {string} modelName - Model name for Task. - * @param {iam.Role} role - IAM Role Model Task. + * Properties for the ECS Role definitions */ -type RoleInfo = { - modelName: string; - roleName: string; - roleArn: string; +type ECSRole = { + id: string; + type: ECSTaskType; }; +/** + * ECS Task types + */ enum ECSTaskType { API = 'API', } @@ -48,14 +47,12 @@ enum ECSTaskType { * LisaServe IAM stack. */ export class LisaServeIAMStack extends Stack { - /** - * @param {Construct} scope - The parent or owner of the construct. - * @param {string} id - The unique identifier for the construct within its scope. - * @param {LisaIAMStackProps} props - Properties for the Stack. - */ - public readonly taskRoles: RoleInfo[] = []; - public readonly autoScalingGroupIamRole: Role; + /** + * @param {Construct} scope - The parent or owner of the construct. + * @param {string} id - The unique identifier for the construct within its scope. + * @param {LisaIAMStackProps} props - Properties for the Stack. + */ constructor (scope: Construct, id: string, props: LisaIAMStackProps) { super(scope, id, props); const { config } = props; @@ -67,75 +64,109 @@ export class LisaServeIAMStack extends Stack { }, ]); - // role for auto scaling group for ECS cluster - this.autoScalingGroupIamRole = new Role(this, createCdkId([config.deploymentName, 'ASGRole']), { - roleName: createCdkId([config.deploymentName, 'ASGRole']), - assumedBy: new ServicePrincipal('ec2.amazonaws.com'), - }); - this.autoScalingGroupIamRole.addManagedPolicy( - ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), - ); - - /** - * Create role for Lambda execution if deploying RAG - */ + /* + * Create role for Lambda execution if deploying RAG + */ if (config.deployRag) { - const lambdaPolicyStatements = getIamPolicyStatements(config, 'rag'); - const lambdaRagPolicy = new ManagedPolicy(this, createCdkId([config.deploymentName, 'RAGPolicy']), { - managedPolicyName: createCdkId([config.deploymentName, 'RAGPolicy']), - statements: lambdaPolicyStatements, - }); - const ragLambdaRoleName = createCdkId([config.deploymentName, 'RAGRole']); - const ragLambdaRole = new Role(this, 'LisaRagLambdaExecutionRole', { - assumedBy: new ServicePrincipal('lambda.amazonaws.com'), - roleName: ragLambdaRoleName, - description: 'Role used by RAG API lambdas to access AWS resources', - managedPolicies: [lambdaRagPolicy], - }); - new StringParameter(this, createCdkId(['LisaRagRole', 'StringParameter']), { - parameterName: `${config.deploymentPrefix}/roles/${ragLambdaRoleName}`, - stringValue: ragLambdaRole.roleArn, - description: `Role ARN for LISA ${ragLambdaRoleName}`, - }); + this.createRagLambdaRole(config); } - /** - * Create roles for ECS tasks. Currently all deployed models and all API ECS tasks use - * an identical role. In the future it's possible the models and API containers may need - * specific roles - */ - const statements = getIamPolicyStatements(config, 'ecs'); - const taskPolicyId = createCdkId([config.deploymentName, 'ECSPolicy']); - const taskPolicy = new ManagedPolicy(this, taskPolicyId, { - managedPolicyName: createCdkId([config.deploymentName, 'ECSPolicy']), - statements, - }); - const ecsRoles = [ + /* + * Create roles for ECS tasks. Currently, all deployed models and all API ECS tasks use + * an identical role. In the future it's possible the models and API containers may need + * specific roles + */ + const taskPolicy = this.createTaskPolicy(config.deploymentName, config.deploymentPrefix); + + const ecsRoles: ECSRole[] = [ { id: 'REST', type: ECSTaskType.API, }, ]; - new StringParameter(this, createCdkId(['ECSPolicy', 'SP']), { - parameterName: `${config.deploymentPrefix}/policies/${taskPolicyId}`, - stringValue: taskPolicy.managedPolicyArn, - description: `Managed Policy ARN for LISA ${taskPolicyId}`, - }); - ecsRoles.forEach((role) => { - const roleName = createCdkId([config.deploymentName, role.id, 'Role']); - const taskRole = new Role(this, createCdkId([role.id, 'Role']), { - assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), - roleName, - description: `Allow ${role.id} ${role.type} ECS task access to AWS resources`, - managedPolicies: [taskPolicy], - }); + const taskRoleOverride = getRoleId(`ECS_${role.id}_${role.type}_ROLE`.toUpperCase()); + const taskRoleId = createCdkId([role.id, ROLE]); + const taskRoleName = createCdkId([config.deploymentName, role.id, ROLE]); + const taskRole = config.roles ? + // @ts-expect-error - dynamic key lookup of object + Role.fromRoleName(this, taskRoleId, config.roles[taskRoleOverride]) : + this.createEcsTaskRole(role, taskRoleId, taskRoleName, taskPolicy); + new StringParameter(this, createCdkId([config.deploymentName, role.id, 'SP']), { parameterName: `${config.deploymentPrefix}/roles/${role.id}`, stringValue: taskRole.roleArn, description: `Role ARN for LISA ${role.type} ${role.id} ECS Task`, }); + + if (config.roles) { + const executionRoleOverride = getRoleId(`ECS_${role.id}_${role.type}_EX_ROLE`.toUpperCase()); + // @ts-expect-error - dynamic key lookup of object + const executionRole = Role.fromRoleName(this, createCdkId([role.id, 'ExRole']), config.roles[executionRoleOverride]); + + new StringParameter(this, createCdkId([config.deploymentName, role.id, 'EX', 'SP']), { + parameterName: `${config.deploymentPrefix}/roles/${role.id}EX`, + stringValue: executionRole.roleArn, + description: `Role ARN for LISA ${role.type} ${role.id} ECS Execution`, + }); + } + }); + } + + private createTaskPolicy (deploymentName: string, deploymentPrefix?: string): IManagedPolicy { + const statements = getIamPolicyStatements('ecs'); + const taskPolicyId = createCdkId([deploymentName, 'ECSPolicy']); + const taskPolicy = new ManagedPolicy(this, taskPolicyId, { + managedPolicyName: createCdkId([deploymentName, 'ECSPolicy']), + statements, + }); + + new StringParameter(this, createCdkId(['ECSPolicy', 'SP']), { + parameterName: `${deploymentPrefix}/policies/${taskPolicyId}`, + stringValue: taskPolicy.managedPolicyArn, + description: `Managed Policy ARN for LISA ${taskPolicyId}`, + }); + + return taskPolicy; + } + + private createRagLambdaRole (config: Config): IRole { + const ragLambdaRoleId = createCdkId([config.deploymentName, Roles.RAG_LAMBDA_EXECUTION_ROLE]); + const ragLambdaRole = config.roles?.RagLambdaExecutionRole ? + Role.fromRoleName(this, ragLambdaRoleId, config.roles.RagLambdaExecutionRole) : + this.createRagLambdaExecutionRole(config.deploymentName, ragLambdaRoleId); + + new StringParameter(this, createCdkId(['LisaRagRole', 'StringParameter']), { + parameterName: `${config.deploymentPrefix}/roles/${ragLambdaRoleId}`, + stringValue: ragLambdaRole.roleArn, + description: `Role ARN for LISA ${ragLambdaRoleId}`, + }); + + return ragLambdaRole; + } + + private createRagLambdaExecutionRole (deploymentName: string, roleName: string) { + const lambdaPolicyStatements = getIamPolicyStatements('rag'); + const lambdaRagPolicy = new ManagedPolicy(this, createCdkId([deploymentName, 'RAGPolicy']), { + managedPolicyName: createCdkId([deploymentName, 'RAGPolicy']), + statements: lambdaPolicyStatements, + }); + + return new Role(this, Roles.RAG_LAMBDA_EXECUTION_ROLE, { + roleName, + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + description: 'Role used by RAG API lambdas to access AWS resources', + managedPolicies: [lambdaRagPolicy], + }); + } + + private createEcsTaskRole (role: ECSRole, roleId: string, roleName: string, taskPolicy: IManagedPolicy): IRole { + return new Role(this, roleId, { + assumedBy: new ServicePrincipal('ecs-tasks.amazonaws.com'), + roleName, + description: `Allow ${role.id} ${role.type} task access to AWS resources`, + managedPolicies: [taskPolicy], }); } } diff --git a/lib/models/docker-image-builder.ts b/lib/models/docker-image-builder.ts index d52d1d73..48d4f006 100644 --- a/lib/models/docker-image-builder.ts +++ b/lib/models/docker-image-builder.ts @@ -1,37 +1,40 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { Construct } from 'constructs'; -import { Code, Function, Runtime } from 'aws-cdk-lib/aws-lambda'; import { - Role, InstanceProfile, - ServicePrincipal, + IRole, ManagedPolicy, Policy, - PolicyStatement + PolicyStatement, + Role, + ServicePrincipal, } from 'aws-cdk-lib/aws-iam'; -import { Stack, Duration } from 'aws-cdk-lib'; +import { Code, Function } from 'aws-cdk-lib/aws-lambda'; +import { Duration, Stack } from 'aws-cdk-lib'; import { Bucket } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { createCdkId } from '../core/utils'; import { BaseProps } from '../schema'; import { Vpc } from '../networking/vpc'; +import { Roles } from '../core/iam/roles'; import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { getDefaultRuntime } from '../api-base/utils'; export type DockerImageBuilderProps = BaseProps & { ecrUri: string; @@ -48,15 +51,74 @@ export class DockerImageBuilder extends Construct { const stackName = Stack.of(scope).stackName; - const ec2InstanceProfileRole = new Role(this, createCdkId([stackName, 'docker-image-builder-ec2-role']), { - roleName: createCdkId([stackName, 'docker-image-builder-ec2-role']), - assumedBy: new ServicePrincipal('ec2.amazonaws.com') - }); + const { config } = props; const ec2DockerBucket = new Bucket(this, createCdkId([stackName, 'docker-image-builder-ec2-bucket'])); + new BucketDeployment(this, createCdkId([stackName, 'docker-image-builder-ec2-dplmnt']), { sources: [Source.asset('./lib/serve/ecs-model/')], - destinationBucket: ec2DockerBucket + destinationBucket: ec2DockerBucket, + ...(config.roles && + { + role: Role.fromRoleName(this, createCdkId([stackName, Roles.DOCKER_IMAGE_BUILDER_DEPLOYMENT_ROLE]), config.roles.DockerImageBuilderDeploymentRole), + }), + }); + + const ec2InstanceRoleName = createCdkId([stackName, 'docker-image-builder-ec2-role']); + const ec2InstanceProfileRole = config.roles ? + Role.fromRoleName(this, ec2InstanceRoleName, config.roles.DockerImageBuilderEC2Role) : + this.createEc2InstanceRole(stackName, ec2InstanceRoleName, ec2DockerBucket.bucketArn); + + + const ec2BuilderRoleName = createCdkId([stackName, 'docker_image_builder_role']); + const ec2BuilderRole = config.roles ? + Role.fromRoleName(this, ec2BuilderRoleName, config.roles.DockerImageBuilderRole) : + this.createEc2BuilderRole(stackName, ec2BuilderRoleName, ec2InstanceProfileRole.roleArn); + + const ec2InstanceProfileId = createCdkId([stackName, 'docker-image-builder-profile']); + const ec2InstanceProfile = new InstanceProfile(this, ec2InstanceProfileId, { + instanceProfileName: ec2InstanceProfileId, + role: ec2InstanceProfileRole, + }); + + const functionId = createCdkId([stackName, 'docker-image-builder']); + this.dockerImageBuilderFn = new Function(this, functionId, { + deadLetterQueueEnabled: true, + deadLetterQueue: new Queue(this, 'docker-image-builderDLQ', { + queueName: 'docker-image-builderDLQ', + enforceSSL: true, + }), + functionName: functionId, + runtime: getDefaultRuntime(), + handler: 'dockerimagebuilder.handler', + code: Code.fromAsset('./lambda/'), + timeout: Duration.minutes(1), + reservedConcurrentExecutions: 10, + memorySize: 1024, + role: ec2BuilderRole, + environment: { + 'LISA_DOCKER_BUCKET': ec2DockerBucket.bucketName, + 'LISA_ECR_URI': props.ecrUri, + 'LISA_INSTANCE_PROFILE': ec2InstanceProfile.instanceProfileArn, + 'LISA_MOUNTS3_DEB_URL': props.mountS3DebUrl, + }, + vpc: props.vpc.vpc, + vpcSubnets: props.vpc.subnetSelection, + securityGroups: props.securityGroups, + }); + } + + /** + * Create EC2 instance role + * @param stackName - deployment stack name + * @param roleName - role name + * @param dockerBucketArn - bucket arn containing docker images + * @returns new role + */ + createEc2InstanceRole (stackName: string, roleName: string, dockerBucketArn: string): IRole { + const role = new Role(this, roleName, { + roleName, + assumedBy: new ServicePrincipal('ec2.amazonaws.com'), }); const ec2InstanceProfilePolicy = new Policy(this, createCdkId([stackName, 'docker-image-builder-ec2-policy']), { @@ -65,13 +127,13 @@ export class DockerImageBuilder extends Construct { actions: [ 's3:GetObject', ], - resources: [`${ec2DockerBucket.bucketArn}/*`] + resources: [`${dockerBucketArn}/*`], }), new PolicyStatement({ actions: [ 's3:ListBucket', ], - resources: [ec2DockerBucket.bucketArn] + resources: [dockerBucketArn], }), new PolicyStatement({ actions: [ @@ -80,18 +142,29 @@ export class DockerImageBuilder extends Construct { 'ecr:UploadLayerPart', 'ecr:CompleteLayerUpload', 'ecr:PutImage', - 'ecr:BatchCheckLayerAvailability' + 'ecr:BatchCheckLayerAvailability', ], - resources: ['*'] - }) - ] + resources: ['*'], + }), + ], }); - ec2InstanceProfileRole.attachInlinePolicy(ec2InstanceProfilePolicy); + role.attachInlinePolicy(ec2InstanceProfilePolicy); - const role = new Role(this, createCdkId([stackName, 'docker_image_builder_role']), { - roleName: createCdkId([stackName, 'docker_image_builder_role']), - assumedBy: new ServicePrincipal('lambda.amazonaws.com') + return role; + } + + /** + * Create EC2 builder role + * @param stackName - deployment stack name + * @param roleName - role name + * @param ec2InstanceRoleArn - EC2 Instance role arn + * @returns new role + */ + createEc2BuilderRole (stackName: string, roleName: string, ec2InstanceRoleArn: string): IRole { + const role = new Role(this, roleName, { + roleName, + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), }); const assumeCdkPolicy = new Policy(this, createCdkId([stackName, 'docker-image-builder-policy']), { @@ -107,54 +180,24 @@ export class DockerImageBuilder extends Construct { 'ec2:AssignPrivateIpAddresses', 'ec2:UnassignPrivateIpAddresses' ], - resources: ['*'] + resources: ['*'], }), new PolicyStatement({ actions: ['iam:PassRole'], - resources: [ec2InstanceProfileRole.roleArn] + resources: [ec2InstanceRoleArn], }), new PolicyStatement({ actions: ['ssm:GetParameter'], - resources: ['arn:*:ssm:*::parameter/aws/service/*'] - }) - ] + resources: ['arn:*:ssm:*::parameter/aws/service/*'], + }), + ], }); role.attachInlinePolicy(assumeCdkPolicy); role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')); role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole')); - const ec2InstanceProfileId = createCdkId([stackName, 'docker-image-builder-profile']); - const ec2InstanceProfile = new InstanceProfile(this, ec2InstanceProfileId, { - instanceProfileName: ec2InstanceProfileId, - role: ec2InstanceProfileRole - }); - - const functionId = createCdkId([stackName, 'docker-image-builder']); - this.dockerImageBuilderFn = new Function(this, functionId, { - deadLetterQueueEnabled: true, - deadLetterQueue: new Queue(this, 'docker-image-builderDLQ', { - queueName: 'docker-image-builderDLQ', - enforceSSL: true, - }), - functionName: functionId, - runtime: Runtime.PYTHON_3_10, - handler: 'dockerimagebuilder.handler', - code: Code.fromAsset('./lambda/'), - timeout: Duration.minutes(1), - memorySize: 1024, - reservedConcurrentExecutions: 10, - role: role, - environment: { - 'LISA_DOCKER_BUCKET': ec2DockerBucket.bucketName, - 'LISA_ECR_URI': props.ecrUri, - 'LISA_INSTANCE_PROFILE': ec2InstanceProfile.instanceProfileArn, - 'LISA_MOUNTS3_DEB_URL': props.mountS3DebUrl, - ...(props.config?.subnets && {'LISA_SUBNET_ID': props.config.subnets[0].subnetId}) - }, - vpc: props.vpc.vpc, - vpcSubnets: props.vpc.subnetSelection, - securityGroups: props.securityGroups, - }); + return role; } + } diff --git a/lib/models/ecs-model-deployer.ts b/lib/models/ecs-model-deployer.ts index 285469dc..841ada0a 100644 --- a/lib/models/ecs-model-deployer.ts +++ b/lib/models/ecs-model-deployer.ts @@ -1,30 +1,31 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { Construct } from 'constructs'; import { DockerImageCode, DockerImageFunction, IFunction } from 'aws-cdk-lib/aws-lambda'; import { - Role, - ServicePrincipal, + Effect, + IRole, ManagedPolicy, + PolicyDocument, PolicyStatement, - Effect, - PolicyDocument + Role, + ServicePrincipal, } from 'aws-cdk-lib/aws-iam'; -import { Stack, Duration, Size } from 'aws-cdk-lib'; +import { Duration, Size, Stack } from 'aws-cdk-lib'; import { createCdkId } from '../core/utils'; import { BaseProps, Config } from '../schema'; @@ -38,38 +39,15 @@ export type ECSModelDeployerProps = { export class ECSModelDeployer extends Construct { readonly ecsModelDeployerFn: IFunction; + constructor (scope: Construct, id: string, props: ECSModelDeployerProps) { super(scope, id); const stackName = Stack.of(scope).stackName; - const role = new Role(this, createCdkId([stackName, 'ecs-model-deployer-role']), { - assumedBy: new ServicePrincipal('lambda.amazonaws.com'), - managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), - ], - inlinePolicies: { - lambdaPermissions: new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: ['sts:AssumeRole'], - resources: ['arn:*:iam::*:role/cdk-*'] - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DescribeSubnets', - 'ec2:DeleteNetworkInterface', - 'ec2:AssignPrivateIpAddresses', - 'ec2:UnassignPrivateIpAddresses' - ], - resources: ['*'], - }) - ] - }) + const { config } = props; - } - }); + const role = config.roles ? + Role.fromRoleName(this, createCdkId([stackName, 'ecs-model-deployer-role']), config.roles.ECSModelDeployerRole) : + this.createRole(stackName); const stripped_config = { 'appName': props.config.appName, @@ -80,7 +58,8 @@ export class ECSModelDeployer extends Construct { 's3BucketModels': props.config.s3BucketModels, 'mountS3DebUrl': props.config.mountS3DebUrl, 'permissionsBoundaryAspect': props.config.permissionsBoundaryAspect, - 'subnets': props.config.subnets + 'subnets': props.config.subnets, + 'taskRole': props.config.roles?.ECSModelTaskRole, }; const functionId = createCdkId([stackName, 'ecs_model_deployer']); @@ -90,15 +69,53 @@ export class ECSModelDeployer extends Construct { timeout: Duration.minutes(10), ephemeralStorageSize: Size.mebibytes(2048), memorySize: 1024, - role: role, + role, environment: { 'LISA_VPC_ID': props.vpc.vpc.vpcId, 'LISA_SECURITY_GROUP_ID': props.securityGroupId, - 'LISA_CONFIG': JSON.stringify(stripped_config) + 'LISA_CONFIG': JSON.stringify(stripped_config), }, vpcSubnets: props.vpc.subnetSelection, vpc: props.vpc.vpc, - securityGroups: [props.vpc.securityGroups.lambdaSg] + securityGroups: [props.vpc.securityGroups.lambdaSg], + }); + } + + + /** + * Create ECS Model Deployer role + * @param stackName - deployment stack name + * @returns new role + */ + createRole (stackName: string): IRole { + return new Role(this, createCdkId([stackName, 'ecs-model-deployer-role']), { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), + ], + inlinePolicies: { + lambdaPermissions: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['sts:AssumeRole'], + resources: ['arn:*:iam::*:role/cdk-*'], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ec2:CreateNetworkInterface', + 'ec2:DescribeNetworkInterfaces', + 'ec2:DescribeSubnets', + 'ec2:DeleteNetworkInterface', + 'ec2:AssignPrivateIpAddresses', + 'ec2:UnassignPrivateIpAddresses', + ], + resources: ['*'], + }), + ], + }), + + }, }); } } diff --git a/lib/models/model-api.ts b/lib/models/model-api.ts index 994ed88b..33782b41 100644 --- a/lib/models/model-api.ts +++ b/lib/models/model-api.ts @@ -21,6 +21,7 @@ import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { Repository } from 'aws-cdk-lib/aws-ecr'; import { Effect, + IRole, ManagedPolicy, Policy, PolicyDocument, @@ -28,11 +29,11 @@ import { Role, ServicePrincipal, } from 'aws-cdk-lib/aws-iam'; -import { LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; -import { PythonLambdaFunction, registerAPIEndpoint } from '../api-base/utils'; +import { getDefaultRuntime, PythonLambdaFunction, registerAPIEndpoint } from '../api-base/utils'; import { BaseProps } from '../schema'; import { Vpc } from '../networking/vpc'; @@ -44,6 +45,7 @@ import { CreateModelStateMachine } from './state-machine/create-model'; import { UpdateModelStateMachine } from './state-machine/update-model'; import { Secret } from 'aws-cdk-lib/aws-secretsmanager'; import { createLambdaRole } from '../core/utils'; +import { Roles } from '../core/iam/roles'; /** * Properties for ModelsApi Construct. @@ -126,105 +128,14 @@ export class ModelsApi extends Construct { const managementKeyName = StringParameter.valueForStringParameter(this, `${config.deploymentPrefix}/managementKeySecretName`); - const stateMachinesLambdaRole = new Role(this, 'ModelsSfnLambdaRole', { - assumedBy: new ServicePrincipal('lambda.amazonaws.com'), - managedPolicies: [ - ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), - ], - inlinePolicies: { - lambdaPermissions: new PolicyDocument({ - statements: [ - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'dynamodb:DeleteItem', - 'dynamodb:GetItem', - 'dynamodb:PutItem', - 'dynamodb:UpdateItem', - ], - resources: [ - modelTable.tableArn, - `${modelTable.tableArn}/*`, - ] - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'cloudformation:CreateStack', - 'cloudformation:DeleteStack', - 'cloudformation:DescribeStacks', - ], - resources: [ - 'arn:*:cloudformation:*:*:stack/*', - ], - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'lambda:InvokeFunction' - ], - resources: [ - dockerImageBuilder.dockerImageBuilderFn.functionArn, - ecsModelDeployer.ecsModelDeployerFn.functionArn - ] - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'ecr:DescribeImages' - ], - resources: ['*'] - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'ec2:CreateNetworkInterface', - 'ec2:DescribeNetworkInterfaces', - 'ec2:DescribeSubnets', - 'ec2:DeleteNetworkInterface', - 'ec2:AssignPrivateIpAddresses', - 'ec2:UnassignPrivateIpAddresses' - ], - resources: ['*'], - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'ec2:TerminateInstances' - ], - resources: ['*'], - conditions: { - 'StringEquals': {'aws:ResourceTag/lisa_temporary_instance': 'true'} - } - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'ssm:GetParameter' - ], - resources: [ - lisaServeEndpointUrlPs.parameterArn - ], - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'secretsmanager:GetSecretValue' - ], - resources: [`${Secret.fromSecretNameV2(this, 'ManagementKeySecret', managementKeyName).secretArn}-??????`], // question marks required to resolve the ARN correctly - }), - new PolicyStatement({ - effect: Effect.ALLOW, - actions: [ - 'autoscaling:DescribeAutoScalingGroups', - 'autoscaling:UpdateAutoScalingGroup', - ], - resources: ['*'], // We do not know the ASG names in advance - }), - ] - }), - } - }); + const stateMachineExecutionRole = config.roles ? + { executionRole: Role.fromRoleName(this, Roles.MODEL_SFN_ROLE, config.roles.ModelSfnRole) } : + undefined; + + const stateMachinesLambdaRole = config.roles ? + Role.fromRoleName(this, Roles.MODEL_SFN_LAMBDA_ROLE, config.roles.ModelsSfnLambdaRole) : + this.createStateMachineLambdaRole(modelTable.tableArn, dockerImageBuilder.dockerImageBuilderFn.functionArn, + ecsModelDeployer.ecsModelDeployerFn.functionArn, lisaServeEndpointUrlPs.parameterArn, managementKeyName); const createModelStateMachine = new CreateModelStateMachine(this, 'CreateModelWorkflow', { config: config, @@ -238,6 +149,7 @@ export class ModelsApi extends Construct { ecsModelImageRepository: ecsModelBuildRepo, restApiContainerEndpointPs: lisaServeEndpointUrlPs, managementKeyName: managementKeyName, + ...(stateMachineExecutionRole), }); const deleteModelStateMachine = new DeleteModelStateMachine(this, 'DeleteModelWorkflow', { @@ -249,6 +161,7 @@ export class ModelsApi extends Construct { securityGroups: securityGroups, restApiContainerEndpointPs: lisaServeEndpointUrlPs, managementKeyName: managementKeyName, + ...(stateMachineExecutionRole), }); const updateModelStateMachine = new UpdateModelStateMachine(this, 'UpdateModelWorkflow', { @@ -260,6 +173,7 @@ export class ModelsApi extends Construct { securityGroups: securityGroups, restApiContainerEndpointPs: lisaServeEndpointUrlPs, managementKeyName: managementKeyName, + ...(stateMachineExecutionRole), }); const environment = { @@ -272,7 +186,7 @@ export class ModelsApi extends Construct { MODEL_TABLE_NAME: modelTable.tableName, }; - const lambdaRole: Role = createLambdaRole(this, config.deploymentName, 'ModelApi', modelTable.tableArn); + const lambdaRole: IRole = createLambdaRole(this, config.deploymentName, 'ModelApi', modelTable.tableArn, config.roles?.ModelApiRole); // create proxy handler const lambdaFunction = registerAPIEndpoint( this, @@ -288,7 +202,7 @@ export class ModelsApi extends Construct { method: 'ANY', environment }, - Runtime.PYTHON_3_10, + getDefaultRuntime(), vpc, securityGroups, lambdaRole, @@ -360,7 +274,7 @@ export class ModelsApi extends Construct { './lambda', [commonLambdaLayer], f, - Runtime.PYTHON_3_10, + getDefaultRuntime(), vpc, securityGroups, lambdaRole, @@ -402,4 +316,124 @@ export class ModelsApi extends Construct { }); lambdaFunction.role!.attachInlinePolicy(workflowPermissions); } + + /** + * Creates a role for the state machine lambdas + * @param modelTableArn - Arn of the model table + * @param dockerImageBuilderFnArn - Arn of the docker image builder lambda + * @param ecsModelDeployerFnArn - Arn of the ecs model deployer lambda + * @param lisaServeEndpointUrlParamArn - Arn of the lisa serve endpoint url parameter + * @param managementKeyName - Name of the management key secret + * @returns The created role + */ + createStateMachineLambdaRole (modelTableArn: string, dockerImageBuilderFnArn: string, ecsModelDeployerFnArn: string, lisaServeEndpointUrlParamArn: string, managementKeyName: string): IRole { + return new Role(this, Roles.MODEL_SFN_LAMBDA_ROLE, { + assumedBy: new ServicePrincipal('lambda.amazonaws.com'), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaVPCAccessExecutionRole'), + ], + inlinePolicies: { + lambdaPermissions: new PolicyDocument({ + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'dynamodb:DeleteItem', + 'dynamodb:GetItem', + 'dynamodb:PutItem', + 'dynamodb:UpdateItem', + ], + resources: [ + modelTableArn, + `${modelTableArn}/*`, + ] + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'cloudformation:CreateStack', + 'cloudformation:DeleteStack', + 'cloudformation:DescribeStacks', + ], + resources: [ + 'arn:*:cloudformation:*:*:stack/*', + ], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'lambda:InvokeFunction' + ], + resources: [ + dockerImageBuilderFnArn, + ecsModelDeployerFnArn + ] + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ecr:DescribeImages' + ], + resources: ['*'] + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ec2:CreateNetworkInterface', + 'ec2:DescribeNetworkInterfaces', + 'ec2:DescribeSubnets', + 'ec2:DeleteNetworkInterface', + 'ec2:AssignPrivateIpAddresses', + 'ec2:UnassignPrivateIpAddresses' + ], + resources: ['*'], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ec2:TerminateInstances' + ], + resources: ['*'], + conditions: { + 'StringEquals': {'aws:ResourceTag/lisa_temporary_instance': 'true'} + } + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'ssm:GetParameter' + ], + resources: [ + lisaServeEndpointUrlParamArn + ], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'secretsmanager:GetSecretValue' + ], + resources: [`${Secret.fromSecretNameV2(this, 'ManagementKeySecret', managementKeyName).secretArn}-??????`], // question marks required to resolve the ARN correctly + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'autoscaling:DescribeAutoScalingGroups', + 'autoscaling:UpdateAutoScalingGroup', + ], + resources: ['*'], // We do not know the ASG names in advance + }), + ] + }), + } + }); + } + + createStateMachineExecutionRole (): IRole { + return new Role(this, Roles.MODEL_SFN_ROLE, { + assumedBy: new ServicePrincipal('states.amazonaws.com'), + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaRole'), + ] + }); + } } diff --git a/lib/models/state-machine/create-model.ts b/lib/models/state-machine/create-model.ts index e01b1f02..1900c2d1 100644 --- a/lib/models/state-machine/create-model.ts +++ b/lib/models/state-machine/create-model.ts @@ -27,7 +27,7 @@ import { Construct } from 'constructs'; import { Duration } from 'aws-cdk-lib'; import { BaseProps } from '../../schema'; import { ITable } from 'aws-cdk-lib/aws-dynamodb'; -import { Code, Function, ILayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Code, Function, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { IRole } from 'aws-cdk-lib/aws-iam'; import { LAMBDA_MEMORY, LAMBDA_TIMEOUT, OUTPUT_PATH, POLLING_TIMEOUT } from './constants'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; @@ -36,6 +36,7 @@ import { Repository } from 'aws-cdk-lib/aws-ecr'; import { IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Vpc } from '../../networking/vpc'; import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { getDefaultRuntime } from '../../api-base/utils'; type CreateModelStateMachineProps = BaseProps & { modelTable: ITable, @@ -43,11 +44,12 @@ type CreateModelStateMachineProps = BaseProps & { dockerImageBuilderFnArn: string; ecsModelDeployerFnArn: string; ecsModelImageRepository: Repository; - role?: IRole, vpc: Vpc, securityGroups: ISecurityGroup[]; restApiContainerEndpointPs: IStringParameter; - managementKeyName: string + managementKeyName: string; + role?: IRole, + executionRole?: IRole; }; /** @@ -59,7 +61,7 @@ export class CreateModelStateMachine extends Construct { constructor (scope: Construct, id: string, props: CreateModelStateMachineProps) { super(scope, id); - const {config, modelTable, lambdaLayers, dockerImageBuilderFnArn, ecsModelDeployerFnArn, ecsModelImageRepository, role, vpc, securityGroups, restApiContainerEndpointPs, managementKeyName} = props; + const { config, modelTable, lambdaLayers, dockerImageBuilderFnArn, ecsModelDeployerFnArn, ecsModelImageRepository, role, vpc, securityGroups, restApiContainerEndpointPs, managementKeyName, executionRole } = props; const environment = { DOCKER_IMAGE_BUILDER_FN_ARN: dockerImageBuilderFnArn, @@ -80,7 +82,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'SetModelToCreatingDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_set_model_to_creating', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -105,7 +107,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'StartCopyDockerImageDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_start_copy_docker_image', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -128,7 +130,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'PollDockerImageAvailableDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_poll_docker_image_available', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -151,7 +153,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'HandleFailureDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_failure', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -180,7 +182,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'StartCreateStackDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_start_create_stack', code: Code.fromAsset('./lambda'), timeout: Duration.minutes(8), @@ -203,7 +205,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'PollCreateStackDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_poll_create_stack', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -232,7 +234,7 @@ export class CreateModelStateMachine extends Construct { queueName: 'AddModelToLitellmDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.create_model.handle_add_model_to_litellm', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -291,6 +293,10 @@ export class CreateModelStateMachine extends Construct { const stateMachine = new StateMachine(this, 'CreateModelSM', { definitionBody: DefinitionBody.fromChainable(setModelToCreating), + ...(executionRole && + { + role: executionRole + }) }); this.stateMachineArn = stateMachine.stateMachineArn; diff --git a/lib/models/state-machine/delete-model.ts b/lib/models/state-machine/delete-model.ts index e2b4294e..beb2ce62 100644 --- a/lib/models/state-machine/delete-model.ts +++ b/lib/models/state-machine/delete-model.ts @@ -25,7 +25,7 @@ import { Succeed, Wait, } from 'aws-cdk-lib/aws-stepfunctions'; -import { Code, Function, ILayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Code, Function, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { BaseProps } from '../../schema'; import { IRole } from 'aws-cdk-lib/aws-iam'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; @@ -34,15 +34,17 @@ import { LAMBDA_MEMORY, LAMBDA_TIMEOUT, OUTPUT_PATH, POLLING_TIMEOUT } from './c import { IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Vpc } from '../../networking/vpc'; import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { getDefaultRuntime } from '../../api-base/utils'; type DeleteModelStateMachineProps = BaseProps & { modelTable: ITable, lambdaLayers: ILayerVersion[], - role?: IRole, vpc: Vpc, securityGroups: ISecurityGroup[]; restApiContainerEndpointPs: IStringParameter; managementKeyName: string; + role?: IRole, + executionRole?: IRole; }; @@ -55,7 +57,7 @@ export class DeleteModelStateMachine extends Construct { constructor (scope: Construct, id: string, props: DeleteModelStateMachineProps) { super(scope, id); - const { config, modelTable, lambdaLayers, role, vpc, securityGroups, restApiContainerEndpointPs, managementKeyName } = props; + const { config, modelTable, lambdaLayers, role, vpc, securityGroups, restApiContainerEndpointPs, managementKeyName, executionRole } = props; const environment = { // Environment variables to set in all Lambda functions MODEL_TABLE_NAME: modelTable.tableName, @@ -74,7 +76,7 @@ export class DeleteModelStateMachine extends Construct { queueName: 'SetModelToDeletingDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.delete_model.handle_set_model_to_deleting', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -97,7 +99,7 @@ export class DeleteModelStateMachine extends Construct { queueName: 'DeleteFromLitellmDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.delete_model.handle_delete_from_litellm', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -120,7 +122,7 @@ export class DeleteModelStateMachine extends Construct { queueName: 'DeleteStackDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.delete_model.handle_delete_stack', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -143,7 +145,7 @@ export class DeleteModelStateMachine extends Construct { queueName: 'MonitorDeleteStackDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.delete_model.handle_monitor_delete_stack', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -166,7 +168,7 @@ export class DeleteModelStateMachine extends Construct { queueName: 'DeleteFromDdbDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.delete_model.handle_delete_from_ddb', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -212,6 +214,10 @@ export class DeleteModelStateMachine extends Construct { const stateMachine = new StateMachine(this, 'DeleteModelSM', { definitionBody: DefinitionBody.fromChainable(setModelToDeleting), + ...(executionRole && + { + role: executionRole + }) }); this.stateMachineArn = stateMachine.stateMachineArn; diff --git a/lib/models/state-machine/update-model.ts b/lib/models/state-machine/update-model.ts index eaafdcff..fd05208d 100644 --- a/lib/models/state-machine/update-model.ts +++ b/lib/models/state-machine/update-model.ts @@ -17,26 +17,36 @@ import { BaseProps } from '../../schema'; import { ITable } from 'aws-cdk-lib/aws-dynamodb'; -import { Code, Function, ILayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Code, Function, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { IRole } from 'aws-cdk-lib/aws-iam'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { IStringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; import { LambdaInvoke } from 'aws-cdk-lib/aws-stepfunctions-tasks'; import { LAMBDA_MEMORY, LAMBDA_TIMEOUT, OUTPUT_PATH, POLLING_TIMEOUT } from './constants'; -import { Choice, Condition, DefinitionBody, StateMachine, Succeed, Wait, WaitTime } from 'aws-cdk-lib/aws-stepfunctions'; +import { + Choice, + Condition, + DefinitionBody, + StateMachine, + Succeed, + Wait, + WaitTime, +} from 'aws-cdk-lib/aws-stepfunctions'; import { Vpc } from '../../networking/vpc'; import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { getDefaultRuntime } from '../../api-base/utils'; type UpdateModelStateMachineProps = BaseProps & { modelTable: ITable, lambdaLayers: ILayerVersion[], - role?: IRole, vpc: Vpc, securityGroups: ISecurityGroup[]; restApiContainerEndpointPs: IStringParameter; managementKeyName: string; + role?: IRole, + executionRole?: IRole; }; @@ -57,7 +67,8 @@ export class UpdateModelStateMachine extends Construct { vpc, securityGroups, restApiContainerEndpointPs, - managementKeyName + managementKeyName, + executionRole } = props; const environment = { // Environment variables to set in all Lambda functions @@ -75,7 +86,7 @@ export class UpdateModelStateMachine extends Construct { queueName: 'HandleJobIntakeDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.update_model.handle_job_intake', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -98,7 +109,7 @@ export class UpdateModelStateMachine extends Construct { queueName: 'HandlePollCapacityDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.update_model.handle_poll_capacity', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -121,7 +132,7 @@ export class UpdateModelStateMachine extends Construct { queueName: 'HandleFinishUpdateDLQ', enforceSSL: true, }), - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'models.state_machine.update_model.handle_finish_update', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -169,6 +180,10 @@ export class UpdateModelStateMachine extends Construct { const stateMachine = new StateMachine(this, 'UpdateModelSM', { definitionBody: DefinitionBody.fromChainable(handleJobIntake), + ...(executionRole && + { + role: executionRole + }) }); this.stateMachineArn = stateMachine.stateMachineArn; diff --git a/lib/networking/vpc/security-group-factory.ts b/lib/networking/vpc/security-group-factory.ts index 1f61b5ed..f9ffaaa4 100644 --- a/lib/networking/vpc/security-group-factory.ts +++ b/lib/networking/vpc/security-group-factory.ts @@ -29,6 +29,7 @@ export class SecurityGroupFactory { /** * Creates a security group for the VPC. * + * @param construct - Construct for security group * @param securityGroupOverride - security group override * @param {string} securityGroupId - The name of the security group. * @param {string} deploymentName - The deployment name. @@ -54,13 +55,11 @@ export class SecurityGroupFactory { return sg; } else { const securityGroupName = SecurityGroupNames[securityGroupId]; - const sg = new SecurityGroup(construct, securityGroupId, { + return new SecurityGroup(construct, securityGroupId, { vpc: vpc, description: `Security group for ${description}`, ...(securityGroupName && {securityGroupName: createCdkId(deploymentName ? [deploymentName, securityGroupName] : [securityGroupName])}), }); - - return sg; } } @@ -93,13 +92,14 @@ export class SecurityGroupFactory { securityGroup: ISecurityGroup, securityGroupName: string, vpc: Vpc, - config: Config): void { + config: Config, + port: number): void { const subNets = config.subnets && config.vpcId ? vpc.subnetSelection?.subnets : vpc.vpc.isolatedSubnets.concat(vpc.vpc.privateSubnets); subNets?.forEach((subnet) => { securityGroup.connections.allowFrom( Peer.ipv4(config.subnets ? config.subnets.filter((filteredSubnet: { subnetId: string; }) => filteredSubnet.subnetId === subnet.subnetId)?.[0]?.ipv4CidrBlock : subnet.ipv4CidrBlock), - Port.tcp(config.restApiConfig.rdsConfig.dbPort), + Port.tcp(port), `Allow REST API private subnets to communicate with ${securityGroupName}`, ); }); diff --git a/lib/rag/api/repository.ts b/lib/rag/api/repository.ts index f6dd7aa3..e0e05534 100644 --- a/lib/rag/api/repository.ts +++ b/lib/rag/api/repository.ts @@ -18,10 +18,10 @@ import { Duration } from 'aws-cdk-lib'; import { IAuthorizer, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { IRole } from 'aws-cdk-lib/aws-iam'; -import { ILayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { Construct } from 'constructs'; -import { PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; +import { getDefaultRuntime, PythonLambdaFunction, registerAPIEndpoint } from '../../api-base/utils'; import { BaseProps } from '../../schema'; import { Vpc } from '../../networking/vpc'; @@ -31,11 +31,11 @@ import { Vpc } from '../../networking/vpc'; * @property {IAuthorizer} authorizer - APIGW authorizer * @property {Record} baseEnvironment - Default environment properties applied to all * lambdas - * @property {LayerVersion[]} commonLayers - Lambda layers for all Lambdas. + * @property {ILayerVersion[]} commonLayers - Lambda layers for all Lambdas. * @property {IRole} lambdaExecutionRole - Execution role for lambdas - * @property {IRestApi} restAPI - REST APIGW for UI and Lambdas + * @property {string} restApiId - REST APIGW for UI and Lambdas * @property {ISecurityGroup[]} securityGroups - Security groups for Lambdas - * @property {IVpc} vpc - Stack VPC + * @property {Vpc} vpc - Stack VPC */ type RepositoryApiProps = { authorizer: IAuthorizer; @@ -84,11 +84,21 @@ export class RepositoryApi extends Construct { }, }, { - name: 'purge_document', + name: 'presigned_url', resource: 'repository', - description: 'Purges all records associated with a document from the repository', - path: 'repository/{repositoryId}/{documentId}', - method: 'DELETE', + description: 'Generates a presigned url for uploading files to RAG', + path: 'repository/presigned-url', + method: 'POST', + environment: { + ...baseEnvironment, + }, + }, + { + name: 'similarity_search', + resource: 'repository', + description: 'Run a similarity search against the specified repository using the specified query', + path: 'repository/{repositoryId}/similaritySearch', + method: 'GET', environment: { ...baseEnvironment, }, @@ -105,20 +115,20 @@ export class RepositoryApi extends Construct { }, }, { - name: 'presigned_url', + name: 'delete_document', resource: 'repository', - description: 'Generates a presigned url for uploading files to RAG', - path: 'repository/presigned-url', - method: 'POST', + description: 'Deletes all records associated with a document from the repository', + path: 'repository/{repositoryId}/document', + method: 'DELETE', environment: { ...baseEnvironment, }, }, { - name: 'similarity_search', + name: 'list_docs', resource: 'repository', - description: 'Run a similarity search against the specified repository using the specified query', - path: 'repository/{repositoryId}/similaritySearch', + description: 'List all docs for a repository', + path: 'repository/{repositoryId}/document', method: 'GET', environment: { ...baseEnvironment, @@ -133,7 +143,7 @@ export class RepositoryApi extends Construct { './lambda', commonLayers, f, - Runtime.PYTHON_3_10, + getDefaultRuntime(), vpc, securityGroups, lambdaExecutionRole, diff --git a/lib/rag/index.ts b/lib/rag/index.ts index 89320abe..316bb824 100644 --- a/lib/rag/index.ts +++ b/lib/rag/index.ts @@ -24,11 +24,12 @@ import { CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; import { IAuthorizer } from 'aws-cdk-lib/aws-apigateway'; import { ISecurityGroup } from 'aws-cdk-lib/aws-ec2'; import { AnyPrincipal, CfnServiceLinkedRole, Effect, PolicyStatement, Role } from 'aws-cdk-lib/aws-iam'; -import { Code, LayerVersion, Runtime, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; +import { Code, ILayerVersion, LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { Domain, EngineVersion, IDomain } from 'aws-cdk-lib/aws-opensearchservice'; import { Credentials, DatabaseInstance, DatabaseInstanceEngine } from 'aws-cdk-lib/aws-rds'; import { Bucket, HttpMethods } from 'aws-cdk-lib/aws-s3'; import { ISecret, Secret } from 'aws-cdk-lib/aws-secretsmanager'; +import { AttributeType, Table } from 'aws-cdk-lib/aws-dynamodb'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; @@ -40,8 +41,9 @@ import { Vpc } from '../networking/vpc'; import { BaseProps, RagRepositoryType } from '../schema'; import { SecurityGroupEnum } from '../core/iam/SecurityGroups'; import { SecurityGroupFactory } from '../networking/vpc/security-group-factory'; - import { IngestPipelineStateMachine } from './state_machine/ingest-pipeline'; +import { Roles } from '../core/iam/roles'; +import { getDefaultRuntime } from '../api-base/utils'; const HERE = path.resolve(__dirname); const RAG_LAYER_PATH = path.join(HERE, 'layer'); @@ -63,6 +65,10 @@ type LisaRagStackProps = CustomLisaRagStackProps & StackProps; * LisaChat RAG stack. */ export class LisaRagStack extends Stack { + + // Used to link service role if OpenSeach is used + openSearchRegion?: string; + /** * @param {Construct} scope - The parent or owner of the construct. * @param {string} id - The unique identifier for the construct within its scope. @@ -83,6 +89,7 @@ export class LisaRagStack extends Stack { const bucket = new Bucket(this, createCdkId(['LISA', 'RAG', config.deploymentName, config.deploymentStage]), { removalPolicy: config.removalPolicy, autoDeleteObjects: config.removalPolicy === RemovalPolicy.DESTROY, + enforceSSL: true, cors: [ { allowedMethods: [HttpMethods.GET, HttpMethods.POST], @@ -93,6 +100,25 @@ export class LisaRagStack extends Stack { ], }); + const docTable = new Table(this, createCdkId([config.deploymentName, 'RagDocumentTable']), { + partitionKey: { + name: 'pk', // Composite of repo/collection ids + type: AttributeType.STRING, + }, + sortKey: { + name: 'document_id', + type: AttributeType.STRING + }, + deletionProtection: true, + }); + docTable.addGlobalSecondaryIndex({ + indexName: 'document_index', + partitionKey: { + name: 'document_id', + type: AttributeType.STRING, + }, + }); + const baseEnvironment: Record = { REGISTERED_MODELS_PS_NAME: modelsPs.parameterName, BUCKET_NAME: bucket.bucketName, @@ -100,6 +126,7 @@ export class LisaRagStack extends Stack { CHUNK_OVERLAP: config.ragFileProcessingConfig!.chunkOverlap.toString(), LISA_API_URL_PS_NAME: endpointUrl.parameterName, REST_API_VERSION: 'v2', + RAG_DOCUMENT_TABLE: docTable.tableName, }; // Add REST API SSL Cert ARN if it exists to be used to verify SSL calls to REST API @@ -109,10 +136,10 @@ export class LisaRagStack extends Stack { const lambdaRole = Role.fromRoleArn( this, - 'LISARagAPILambdaExecutionRole', + Roles.RAG_LAMBDA_EXECUTION_ROLE, StringParameter.valueForStringParameter( this, - `${config.deploymentPrefix}/roles/${createCdkId([config.deploymentName, 'RAGRole'])}`, + `${config.deploymentPrefix}/roles/${createCdkId([config.deploymentName, Roles.RAG_LAMBDA_EXECUTION_ROLE])}`, ), ); bucket.grantRead(lambdaRole); @@ -133,37 +160,48 @@ export class LisaRagStack extends Stack { if (config.lambdaLayerAssets?.sdkLayerPath) { sdkLayer = new LayerVersion(this, 'SdkLayer', { code: Code.fromAsset(config.lambdaLayerAssets?.sdkLayerPath), - compatibleRuntimes: [Runtime.PYTHON_3_10], + compatibleRuntimes: [getDefaultRuntime()], removalPolicy: config.removalPolicy, description: 'LISA SDK common layer', }); } else { sdkLayer = new PythonLayerVersion(this, 'SdkLayer', { entry: SDK_PATH, - compatibleRuntimes: [Runtime.PYTHON_3_10], + compatibleRuntimes: [getDefaultRuntime()], removalPolicy: config.removalPolicy, description: 'LISA SDK common layer', }); } const registeredRepositories = []; + let pgvectorSg = undefined; + let openSearchSg = undefined; + const connectionParamName = 'LisaServeRagConnectionInfo'; + baseEnvironment['REGISTERED_REPOSITORIES_PS_PREFIX'] = `${config.deploymentPrefix}/${connectionParamName}/`; + const registeredRepositoriesParamName = `${config.deploymentPrefix}/registeredRepositories`; for (const ragConfig of config.ragRepositories) { + registeredRepositories.push({ repositoryId: ragConfig.repositoryId, repositoryName: ragConfig.repositoryName, type: ragConfig.type, allowedGroups: ragConfig.allowedGroups }); + // Create opensearch cluster for RAG if (ragConfig.type === RagRepositoryType.OPENSEARCH && ragConfig.opensearchConfig) { - const openSearchSg = SecurityGroupFactory.createSecurityGroup( - this, - config.securityGroupConfig?.openSearchSecurityGroupId, - SecurityGroupEnum.OPEN_SEARCH_SG, - config.deploymentName, - vpc.vpc, - 'RAG OpenSearch domain', - ); - if (!config.securityGroupConfig?.openSearchSecurityGroupId) { - SecurityGroupFactory.addIngress(openSearchSg, SecurityGroupEnum.OPEN_SEARCH_SG, vpc, config); + if (!openSearchSg) { + + openSearchSg = SecurityGroupFactory.createSecurityGroup( + this, + config.securityGroupConfig?.openSearchSecurityGroupId, + SecurityGroupEnum.OPEN_SEARCH_SG, + config.deploymentName, + vpc.vpc, + 'RAG OpenSearch domain', + ); + + if (!config.securityGroupConfig?.openSearchSecurityGroupId) { + SecurityGroupFactory.addIngress(openSearchSg, SecurityGroupEnum.OPEN_SEARCH_SG, vpc, config, 80); + SecurityGroupFactory.addIngress(openSearchSg, SecurityGroupEnum.OPEN_SEARCH_SG, vpc, config, 443); + } } - registeredRepositories.push({ repositoryId: ragConfig.repositoryId, type: ragConfig.type }); let openSearchDomain: IDomain; if ('endpoint' in ragConfig.opensearchConfig) { @@ -174,26 +212,10 @@ export class LisaRagStack extends Stack { ); } else { // Service-linked role that Amazon OpenSearch Service will use - (async () => { - const iam = new IAMClient({ - region: config.region, - }); - const response = await iam.send( - new ListRolesCommand({ - PathPrefix: '/aws-service-role/opensearchservice.amazonaws.com/', - }), - ); + this.openSearchRegion = config.region; - // Only if the role for OpenSearch Service doesn't exist, it will be created. - if (response.Roles && response.Roles?.length === 0) { - new CfnServiceLinkedRole(this, 'OpensearchServiceLinkedRole', { - awsServiceName: 'opensearchservice.amazonaws.com', - }); - } - })(); - - openSearchDomain = new Domain(this, 'LisaServeRagRepository', { - domainName: 'lisa-rag', + openSearchDomain = new Domain(this, createCdkId(['LisaServeRagRepository', ragConfig.repositoryId]), { + domainName: ['lisa-rag', ragConfig.repositoryId].join('-'), // 2.9 is the latest available in ADC regions as of 1/11/24 version: EngineVersion.OPENSEARCH_2_9, enableVersionUpgrade: true, @@ -202,6 +224,7 @@ export class LisaRagStack extends Stack { ebs: { enabled: true, volumeSize: ragConfig.opensearchConfig.volumeSize, + volumeType: ragConfig.opensearchConfig.volumeType, }, zoneAwareness: { availabilityZoneCount: vpc.vpc.privateSubnets.length, @@ -232,15 +255,15 @@ export class LisaRagStack extends Stack { openSearchDomain.grantPathReadWrite('*', lambdaRole); openSearchDomain.grantReadWrite(lambdaRole); - new CfnOutput(this, 'opensearchRagRepositoryEndpoint', { + new CfnOutput(this, createCdkId(['opensearchRagRepositoryEndpoint', ragConfig.repositoryId]), { value: openSearchDomain.domainEndpoint, }); const openSearchEndpointPs = new StringParameter( this, - createCdkId(['LisaServeRagRepositoryEndpoint', 'StringParameter']), + createCdkId([connectionParamName, ragConfig.repositoryId, 'StringParameter']), { - parameterName: `${config.deploymentPrefix}/lisaServeRagRepositoryEndpoint`, + parameterName: `${config.deploymentPrefix}/${connectionParamName}/${ragConfig.repositoryId}`, stringValue: openSearchDomain.domainEndpoint, description: 'Endpoint for LISA Serve OpenSearch Rag Repository', }, @@ -249,42 +272,42 @@ export class LisaRagStack extends Stack { // Add explicit dependency on OpenSearch Domain being created openSearchEndpointPs.node.addDependency(openSearchDomain); openSearchEndpointPs.grantRead(lambdaRole); - // Add parameter as lambda environment variable for RagAPI - baseEnvironment['OPENSEARCH_ENDPOINT_PS_NAME'] = openSearchEndpointPs.parameterName; } else if (ragConfig.type === RagRepositoryType.PGVECTOR && ragConfig.rdsConfig) { - registeredRepositories.push({ repositoryId: ragConfig.repositoryId, type: ragConfig.type }); - const connectionParamName = 'LisaServeRagPGVectorConnectionInfo'; let rdsPasswordSecret: ISecret; let rdsConnectionInfoPs: StringParameter; // if dbHost and passwordSecretId are defined, then connect to DB with existing params if (!!ragConfig.rdsConfig.dbHost && !!ragConfig.rdsConfig.passwordSecretId) { - rdsConnectionInfoPs = new StringParameter(this, createCdkId([connectionParamName, 'StringParameter']), { - parameterName: `${config.deploymentPrefix}/${connectionParamName}`, + rdsConnectionInfoPs = new StringParameter(this, createCdkId([connectionParamName, ragConfig.repositoryId, 'StringParameter']), { + parameterName: `${config.deploymentPrefix}/${connectionParamName}/${ragConfig.repositoryId}`, stringValue: JSON.stringify(ragConfig.rdsConfig), description: 'Connection info for LISA Serve PGVector database', }); rdsPasswordSecret = Secret.fromSecretNameV2( this, - createCdkId([config.deploymentName, 'RagRDSPwdSecret']), + createCdkId([config.deploymentName, ragConfig.repositoryId, 'RagRDSPwdSecret']), ragConfig.rdsConfig.passwordSecretId, ); } else { - // Create new DB and SG - const pgvectorSg = SecurityGroupFactory.createSecurityGroup( - this, - config.securityGroupConfig?.pgVectorSecurityGroupId, - SecurityGroupEnum.PG_VECTOR_SG, - undefined, - vpc.vpc, - 'RAG PGVector database', - ); - if (!config.securityGroupConfig?.pgVectorSecurityGroupId) { - SecurityGroupFactory.addIngress(pgvectorSg, SecurityGroupEnum.PG_VECTOR_SG, vpc, config); + // only create one security group + if (!pgvectorSg) { + // Create new DB and SG + pgvectorSg = SecurityGroupFactory.createSecurityGroup( + this, + config.securityGroupConfig?.pgVectorSecurityGroupId, + SecurityGroupEnum.PG_VECTOR_SG, + undefined, + vpc.vpc, + 'RAG PGVector database', + ); + + if (!config.securityGroupConfig?.pgVectorSecurityGroupId) { + SecurityGroupFactory.addIngress(pgvectorSg, SecurityGroupEnum.PG_VECTOR_SG, vpc, config, ragConfig.rdsConfig.dbPort); + } } const username = ragConfig.rdsConfig.username; const dbCreds = Credentials.fromGeneratedSecret(username); - const pgvector_db = new DatabaseInstance(this, 'PGVectorDB', { + const pgvector_db = new DatabaseInstance(this, createCdkId([ragConfig.repositoryId, 'PGVectorDB']), { engine: DatabaseInstanceEngine.POSTGRES, vpc: vpc.vpc, subnetGroup: vpc.subnetGroup, @@ -293,8 +316,8 @@ export class LisaRagStack extends Stack { removalPolicy: RemovalPolicy.DESTROY, }); rdsPasswordSecret = pgvector_db.secret!; - rdsConnectionInfoPs = new StringParameter(this, createCdkId([connectionParamName, 'StringParameter']), { - parameterName: `${config.deploymentPrefix}/${connectionParamName}`, + rdsConnectionInfoPs = new StringParameter(this, createCdkId([connectionParamName, ragConfig.repositoryId, 'StringParameter']), { + parameterName: `${config.deploymentPrefix}/${connectionParamName}/${ragConfig.repositoryId}`, stringValue: JSON.stringify({ username: username, passwordSecretId: rdsPasswordSecret.secretName, @@ -307,7 +330,6 @@ export class LisaRagStack extends Stack { } rdsPasswordSecret.grantRead(lambdaRole); rdsConnectionInfoPs.grantRead(lambdaRole); - baseEnvironment['RDS_CONNECTION_INFO_PS_NAME'] = rdsConnectionInfoPs.parameterName; } // Create ingest pipeline state machines for each pipeline config @@ -332,7 +354,9 @@ export class LisaRagStack extends Stack { rdsConfig: ragConfig.rdsConfig, repositoryId: ragConfig.repositoryId, type: ragConfig.type, - layers: [commonLambdaLayer, ragLambdaLayer.layer, sdkLayer] + layers: [commonLambdaLayer, ragLambdaLayer.layer, sdkLayer], + registeredRepositoriesParamName, + ragDocumentTable: docTable }); console.log(`[DEBUG] Successfully created pipeline ${index}`); } catch (error) { @@ -345,7 +369,7 @@ export class LisaRagStack extends Stack { // Create Parameter Store entry with RAG repositories const ragRepositoriesParam = new StringParameter(this, createCdkId([config.deploymentName, 'RagReposSP']), { - parameterName: `${config.deploymentPrefix}/registeredRepositories`, + parameterName: registeredRepositoriesParamName, stringValue: JSON.stringify(registeredRepositories), description: 'Serialized JSON of registered RAG repositories', }); @@ -368,5 +392,33 @@ export class LisaRagStack extends Stack { ragRepositoriesParam.grantRead(lambdaRole); modelsPs.grantRead(lambdaRole); endpointUrl.grantRead(lambdaRole); + docTable.grantReadWriteData(lambdaRole); + } + + /** + * This method links the OpenSearch Service role to the service-linked role if it exists. + * If the role doesn't exist, it will be created. + */ + async linkServiceRole () { + // Only link open search role if being used + if (!this.openSearchRegion) { + return; + } + + const iam = new IAMClient({ + region: this.openSearchRegion + }); + const response = await iam.send( + new ListRolesCommand({ + PathPrefix: '/aws-service-role/opensearchservice.amazonaws.com/', + }), + ); + + // Only if the role for OpenSearch Service doesn't exist, it will be created. + if (response.Roles && response.Roles?.length === 0) { + new CfnServiceLinkedRole(this, 'OpensearchServiceLinkedRole', { + awsServiceName: 'opensearchservice.amazonaws.com', + }); + } } } diff --git a/lib/rag/layer/requirements.txt b/lib/rag/layer/requirements.txt index 88694e98..4ebb1bb9 100644 --- a/lib/rag/layer/requirements.txt +++ b/lib/rag/layer/requirements.txt @@ -1,8 +1,8 @@ boto3>=1.34.131 botocore>=1.34.131 -langchain==0.2.16 -langchain-community==0.2.17 -langchain-openai==0.1.25 +langchain==0.3.9 +langchain-community==0.3.9 +langchain-openai==0.2.11 opensearch-py==2.6.0 pgvector==0.2.5 psycopg2-binary==2.9.9 diff --git a/lib/rag/state_machine/ingest-pipeline.ts b/lib/rag/state_machine/ingest-pipeline.ts index c3b76717..2d94fef4 100644 --- a/lib/rag/state_machine/ingest-pipeline.ts +++ b/lib/rag/state_machine/ingest-pipeline.ts @@ -28,7 +28,7 @@ import { import { Construct } from 'constructs'; import { Duration } from 'aws-cdk-lib'; import { BaseProps } from '../../schema'; -import { Code, Function, ILayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { Code, Function, ILayerVersion } from 'aws-cdk-lib/aws-lambda'; import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { LAMBDA_MEMORY, LAMBDA_TIMEOUT, OUTPUT_PATH } from './constants'; import { Vpc } from '../../networking/vpc'; @@ -38,6 +38,8 @@ import { SfnStateMachine } from 'aws-cdk-lib/aws-events-targets'; import { RagRepositoryType } from '../../schema'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as cdk from 'aws-cdk-lib'; +import { getDefaultRuntime } from '../../api-base/utils'; +import { Table } from 'aws-cdk-lib/aws-dynamodb'; type PipelineConfig = { chunkOverlap: number; @@ -64,6 +66,8 @@ type IngestPipelineStateMachineProps = BaseProps & { repositoryId: string; type: RagRepositoryType; layers?: ILayerVersion[]; + registeredRepositoriesParamName: string; + ragDocumentTable: Table; }; /** @@ -75,7 +79,7 @@ export class IngestPipelineStateMachine extends Construct { constructor (scope: Construct, id: string, props: IngestPipelineStateMachineProps) { super(scope, id); - const {config, vpc, pipelineConfig, rdsConfig, repositoryId, type, layers} = props; + const {config, vpc, pipelineConfig, rdsConfig, repositoryId, type, layers, registeredRepositoriesParamName, ragDocumentTable} = props; // Create KMS key for environment variable encryption const kmsKey = new kms.Key(this, 'EnvironmentEncryptionKey', { @@ -96,7 +100,10 @@ export class IngestPipelineStateMachine extends Construct { RDS_CONNECTION_INFO_PS_NAME: `${config.deploymentPrefix}/LisaServeRagPGVectorConnectionInfo`, OPENSEARCH_ENDPOINT_PS_NAME: `${config.deploymentPrefix}/lisaServeRagRepositoryEndpoint`, LISA_API_URL_PS_NAME: `${config.deploymentPrefix}/lisaServeRestApiUri`, + RAG_DOCUMENT_TABLE: ragDocumentTable.tableName, LOG_LEVEL: config.logLevel, + REGISTERED_REPOSITORIES_PS_NAME: registeredRepositoriesParamName, + REGISTERED_REPOSITORIES_PS_PREFIX: `${config.deploymentPrefix}/LisaServeRagConnectionInfo/`, RESTAPI_SSL_CERT_ARN: config.restApiConfig.sslCertIamArn || '', ...(rdsConfig && { RDS_USERNAME: rdsConfig.username, @@ -116,9 +123,23 @@ export class IngestPipelineStateMachine extends Construct { `arn:${cdk.Aws.PARTITION}:s3:::${pipelineConfig.s3Bucket}/*` ] }); + // Allow DynamoDB Read/Write to RAG Document Table + const dynamoPolicyStatement = new PolicyStatement({ + effect: Effect.ALLOW, + actions: [ + 'dynamodb:BatchGetItem', + 'dynamodb:GetItem', + 'dynamodb:Query', + 'dynamodb:Scan', + 'dynamodb:BatchWriteItem', + 'dynamodb:PutItem', + 'dynamodb:UpdateItem', + ], + resources: [ragDocumentTable.tableArn, `${ragDocumentTable.tableArn}/index/*`] + }); // Create array of policy statements - const policyStatements = [s3PolicyStatement]; + const policyStatements = [s3PolicyStatement, dynamoPolicyStatement]; // Create IAM certificate policy if certificate ARN is provided let certPolicyStatement; @@ -133,7 +154,7 @@ export class IngestPipelineStateMachine extends Construct { // Function to list objects modified in last 24 hours const listModifiedObjectsFunction = new Function(this, 'listModifiedObjectsFunc', { - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'repository.state_machine.list_modified_objects.handle_list_modified_objects', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -162,7 +183,7 @@ export class IngestPipelineStateMachine extends Construct { // Create the ingest documents function with S3 permissions const pipelineIngestDocumentsFunction = new Function(this, 'pipelineIngestDocumentsMapFunc', { - runtime: Runtime.PYTHON_3_10, + runtime: getDefaultRuntime(), handler: 'repository.pipeline_ingest_documents.handle_pipeline_ingest_documents', code: Code.fromAsset('./lambda'), timeout: LAMBDA_TIMEOUT, @@ -180,7 +201,9 @@ export class IngestPipelineStateMachine extends Construct { `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/LisaServeRagPGVectorConnectionInfo`, `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/lisaServeRagRepositoryEndpoint`, `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/lisaServeRestApiUri`, - `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/managementKeySecretName` + `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/managementKeySecretName`, + `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/registeredRepositories`, + `arn:${cdk.Aws.PARTITION}:ssm:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:parameter${config.deploymentPrefix}/LisaServeRagConnectionInfo/*` ] }), new PolicyStatement({ diff --git a/lib/schema.ts b/lib/schema.ts index e7b3fea0..4c96c588 100644 --- a/lib/schema.ts +++ b/lib/schema.ts @@ -23,6 +23,7 @@ import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import { AmiHardwareType } from 'aws-cdk-lib/aws-ecs'; import { z } from 'zod'; +import { EbsDeviceVolumeType } from 'aws-cdk-lib/aws-ec2'; const HERE: string = path.resolve(__dirname); const VERSION_PATH: string = path.resolve(HERE, '..', 'VERSION'); @@ -240,6 +241,62 @@ export class Ec2Metadata { maxThroughput: 100, vCpus: 192, }, + 'g6.xlarge': { + memory: 16 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 10, + vCpus: 4, + }, + 'g6.2xlarge': { + memory: 32 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 10, + vCpus: 8, + }, + 'g6.4xlarge': { + memory: 64 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 16, + }, + 'g6.8xlarge': { + memory: 128 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 32, + }, + 'g6.16xlarge': { + memory: 256 * 1000, + gpuCount: 1, + nvmePath: '/dev/nvme1n1', + maxThroughput: 25, + vCpus: 64, + }, + 'g6.12xlarge': { + memory: 192 * 1000, + gpuCount: 4, + nvmePath: '/dev/nvme1n1', + maxThroughput: 40, + vCpus: 48, + }, + 'g6.24xlarge': { + memory: 384 * 1000, + gpuCount: 4, + nvmePath: '/dev/nvme1n1', + maxThroughput: 50, + vCpus: 96, + }, + 'g6.48xlarge': { + memory: 768 * 1000, + gpuCount: 8, + nvmePath: '/dev/nvme1n1', + maxThroughput: 100, + vCpus: 192, + }, 'p4d.24xlarge': { memory: 1152 * 1000, gpuCount: 8, @@ -372,7 +429,7 @@ const EcsBaseConfigSchema = z.object({ containerConfig: ContainerConfigSchema, containerMemoryBuffer: z.number().default(1024 * 2) .describe('This is the amount of memory to buffer (or subtract off) from the total instance memory, ' + - 'if we don\'t include this, the container can have a hard time finding available RAM resources to start and the tasks will fail deployment'), + 'if we don\'t include this, the container can have a hard time finding available RAM resources to start and the tasks will fail deployment'), environment: z.record(z.string()).describe('Environment variables set on the task container'), identifier: z.string(), instanceType: z.enum(VALID_INSTANCE_KEYS).describe('EC2 instance type for running the model.'), @@ -436,7 +493,7 @@ const RdsInstanceConfig = z.object({ dbName: z.string().default('postgres').describe('Database name for existing database instance.'), dbPort: z.number().default(5432).describe('Port to open on the database instance.'), }).describe('Configuration schema for RDS Instances needed for LiteLLM scaling or PGVector RAG operations.\n \n ' + - 'The optional fields can be omitted to create a new database instance, otherwise fill in all fields to use an existing database instance.'); + 'The optional fields can be omitted to create a new database instance, otherwise fill in all fields to use an existing database instance.'); const FastApiContainerConfigSchema = z.object({ internetFacing: z.boolean().default(true).describe('Whether the REST API ALB will be configured as internet facing.'), @@ -454,8 +511,8 @@ const FastApiContainerConfigSchema = z.object({ }, { message: - 'We do not allow using an existing DB for LiteLLM because of its requirement in internal model management ' + - 'APIs. Please do not define the dbHost or passwordSecretId fields for the FastAPI container DB config.', + 'We do not allow using an existing DB for LiteLLM because of its requirement in internal model management ' + + 'APIs. Please do not define the dbHost or passwordSecretId fields for the FastAPI container DB config.', }, ), }).describe('Configuration schema for REST API.'); @@ -474,6 +531,7 @@ const OpenSearchNewClusterConfig = z.object({ masterNodes: z.number().min(0), masterNodeInstanceType: z.string(), volumeSize: z.number().min(10), + volumeType: z.nativeEnum(EbsDeviceVolumeType).default(EbsDeviceVolumeType.GP3), multiAzWithStandby: z.boolean().default(false), }); @@ -484,6 +542,7 @@ const OpenSearchExistingClusterConfig = z.object({ const RagRepositoryConfigSchema = z .object({ repositoryId: z.string(), + repositoryName: z.string().optional().describe('Name to display in the UI'), type: z.nativeEnum(RagRepositoryType), opensearchConfig: z.union([OpenSearchExistingClusterConfig, OpenSearchNewClusterConfig]).optional(), rdsConfig: RdsInstanceConfig.optional(), @@ -496,10 +555,11 @@ const RagRepositoryConfigSchema = z trigger: z.union([z.literal('daily'), z.literal('event')]), collectionName: z.string() })).optional().describe('Rag ingestion pipeline for automated inclusion into a vector store from S3'), + allowedGroups: z.array(z.string()).optional().default([]) }) .refine((input) => { return !((input.type === RagRepositoryType.OPENSEARCH && input.opensearchConfig === undefined) || - (input.type === RagRepositoryType.PGVECTOR && input.rdsConfig === undefined)); + (input.type === RagRepositoryType.PGVECTOR && input.rdsConfig === undefined)); }) .describe('Configuration schema for RAG repository. Defines settings for OpenSearch.'); @@ -535,7 +595,7 @@ const LiteLLMConfig = z.object({ db_key: z.string().refine( (key) => key.startsWith('sk-'), // key needed for model management actions 'Key string must be defined for model management operations, and it must start with "sk-".' + - 'This can be any string, and a random UUID is recommended. Example: sk-f132c7cc-059c-481b-b5ca-a42e191672aa', + 'This can be any string, and a random UUID is recommended. Example: sk-f132c7cc-059c-481b-b5ca-a42e191672aa', ), general_settings: z.any().optional(), litellm_settings: z.any().optional(), @@ -544,6 +604,28 @@ const LiteLLMConfig = z.object({ }) .describe('Core LiteLLM configuration - see https://litellm.vercel.app/docs/proxy/configs#all-settings for more details about each field.'); +const RoleConfig = z.object({ + DockerImageBuilderDeploymentRole: z.string().max(64), + DockerImageBuilderEC2Role: z.string().max(64), + DockerImageBuilderRole: z.string().max(64), + DocsRole: z.string().max(64).optional(), + DocsDeployerRole: z.string().max(64).optional(), + ECSModelDeployerRole: z.string().max(64), + ECSModelTaskRole: z.string().max(64), + ECSRestApiRole: z.string().max(64), + ECSRestApiExRole: z.string().max(64), + LambdaExecutionRole: z.string().max(64), + LambdaConfigurationApiExecutionRole: z.string().max(64), + ModelApiRole: z.string().max(64), + ModelsSfnLambdaRole: z.string().max(64), + ModelSfnRole: z.string().max(64), + RagLambdaExecutionRole: z.string().max(64).optional(), + RestApiAuthorizerRole: z.string().max(64), + S3ReaderRole: z.string().max(64).optional(), + UIDeploymentRole: z.string().max(64).optional(), +}) + .describe('Role overrides used across stacks.'); + const RawConfigSchema = z .object({ appName: z.string().default('lisa').describe('Name of the application.'), @@ -562,11 +644,13 @@ const RawConfigSchema = z }) .describe('AWS account number for deployment. Must be 12 digits.'), region: z.string().describe('AWS region for deployment.'), + partition: z.string().default('aws').describe('AWS partition for deployment.'), + domain: z.string().default('amazonaws.com').describe('AWS domain for deployment'), restApiConfig: FastApiContainerConfigSchema, vpcId: z.string().optional().describe('VPC ID for the application. (e.g. vpc-0123456789abcdef)'), subnets: z.array(z.object({ subnetId: z.string().startsWith('subnet-'), - ipv4CidrBlock: z.string() + ipv4CidrBlock: z.string(), })).optional().describe('Array of subnet objects for the application. These contain a subnetId(e.g. [subnet-fedcba9876543210] and ipv4CidrBlock'), securityGroupConfig: SecurityGroupConfigSchema.optional(), deploymentStage: z.string().default('prod').describe('Deployment stage for the application.'), @@ -594,6 +678,7 @@ const RawConfigSchema = z .default('DEBUG') .describe('Log level for application.'), authConfig: AuthConfigSchema.optional().describe('Authorization configuration.'), + roles: RoleConfig.optional(), pypiConfig: PypiConfigSchema.default({ indexUrl: '', trustedHost: '', @@ -663,14 +748,14 @@ const RawConfigSchema = z (config) => { return ( !(config.deployChat || config.deployRag || config.deployUi) || - config.authConfig + config.authConfig ); }, { message: - 'An auth config must be provided when deploying the chat, RAG, or UI stacks or when deploying an internet ' + - 'facing ALB. Check that `deployChat`, `deployRag`, `deployUi`, and `restApiConfig.internetFacing` are all ' + - 'false or that an `authConfig` is provided.', + 'An auth config must be provided when deploying the chat, RAG, or UI stacks or when deploying an internet ' + + 'facing ALB. Check that `deployChat`, `deployRag`, `deployUi`, and `restApiConfig.internetFacing` are all ' + + 'false or that an `authConfig` is provided.', }, ) .describe('Raw application configuration schema.'); diff --git a/lib/serve/index.ts b/lib/serve/index.ts index c889d5d2..d61c3f48 100644 --- a/lib/serve/index.ts +++ b/lib/serve/index.ts @@ -153,7 +153,7 @@ export class LisaServeApplicationStack extends Stack { 'LiteLLM dynamic model management database', ); if (!config.securityGroupConfig?.liteLlmDbSecurityGroupId) { - SecurityGroupFactory.addIngress(litellmDbSg, SecurityGroupEnum.LITE_LLM_SG, vpc, config); + SecurityGroupFactory.addIngress(litellmDbSg, SecurityGroupEnum.LITE_LLM_SG, vpc, config, config.restApiConfig.rdsConfig.dbPort); } const username = config.restApiConfig.rdsConfig.username; diff --git a/lib/serve/rest-api/src/requirements.txt b/lib/serve/rest-api/src/requirements.txt index 60339093..1f0959c1 100644 --- a/lib/serve/rest-api/src/requirements.txt +++ b/lib/serve/rest-api/src/requirements.txt @@ -7,7 +7,7 @@ cryptography==42.0.8 fastapi==0.111.1 fastapi_utils==0.7.0 gunicorn==22.0.0 -litellm[proxy]==1.50.4 +litellm[proxy]==1.53.7 loguru==0.7.2 pydantic==2.8.2 PyJWT==2.9.0 diff --git a/lib/serve/rest-api/src/utils/generate_litellm_config.py b/lib/serve/rest-api/src/utils/generate_litellm_config.py index 5bac7f48..95defdca 100644 --- a/lib/serve/rest-api/src/utils/generate_litellm_config.py +++ b/lib/serve/rest-api/src/utils/generate_litellm_config.py @@ -80,6 +80,8 @@ def generate_config(filepath: str) -> None: } ) + print(f"Generated config_contents file: \n{json.dumps(config_contents, indent=2)}") + # Write updated config back to original path with open(filepath, "w") as fp: yaml.safe_dump(config_contents, fp) diff --git a/lib/stages.ts b/lib/stages.ts index 78796e01..1847a8b7 100644 --- a/lib/stages.ts +++ b/lib/stages.ts @@ -259,6 +259,7 @@ export class LisaServeApplicationStage extends Stage { securityGroups: [networkingStack.vpc.securityGroups.lambdaSg], vpc: networkingStack.vpc, }); + ragStack.linkServiceRole(); // Ignore async response ragStack.addDependency(coreStack); ragStack.addDependency(iamStack); ragStack.addDependency(apiBaseStack); diff --git a/lib/user-interface/index.ts b/lib/user-interface/index.ts index 0c4ce8a6..2d7bad83 100644 --- a/lib/user-interface/index.ts +++ b/lib/user-interface/index.ts @@ -1,26 +1,26 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ -import { ExecSyncOptionsWithBufferEncoding, execSync } from 'node:child_process'; +import { execSync, ExecSyncOptionsWithBufferEncoding } from 'node:child_process'; import * as fs from 'node:fs'; import * as path from 'node:path'; import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib'; import { AwsIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway'; -import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; +import { IRole, ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import { BlockPublicAccess, Bucket, BucketEncryption } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; @@ -29,6 +29,7 @@ import { Construct } from 'constructs'; import { createCdkId } from '../core/utils'; import { BaseProps } from '../schema'; +import { Roles } from '../core/iam/roles'; /** * Properties for UserInterface Construct. @@ -48,10 +49,10 @@ type UserInterfaceProps = CustomUserInterfaceProps & StackProps; */ export class UserInterfaceStack extends Stack { /** - * @param {Construct} scope - The parent or owner of the construct. - * @param {string} id - The unique identifier for the construct within its scope. - * @param {UserInterfaceProps} props - The properties of the construct. - */ + * @param {Construct} scope - The parent or owner of the construct. + * @param {string} id - The unique identifier for the construct within its scope. + * @param {UserInterfaceProps} props - The properties of the construct. + */ constructor (scope: Construct, id: string, props: UserInterfaceProps) { super(scope, id, props); @@ -73,12 +74,9 @@ export class UserInterfaceStack extends Stack { // REST APIGW config // S3 role - const s3ReaderRole = new Role(this, `${Stack.of(this).stackName}-s3-reader-role`, { - assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), - roleName: `${Stack.of(this).stackName}-s3-reader-role`, - managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess')], - description: 'Allows API gateway to proxy static website assets', - }); + const s3ReaderRole: IRole = config.roles?.S3ReaderRole ? + Role.fromRoleName(this, Roles.S3_READER_ROLE, config.roles.S3ReaderRole) : + this.createS3ReadOnlyRole(); // Configure static site resources const proxyMethodResponse = [ @@ -222,10 +220,29 @@ export class UserInterfaceStack extends Stack { } else { webappAssets = Source.asset(config.webAppAssetsPath); } + new BucketDeployment(this, 'AwsExportsDepolyment', { sources: [webappAssets, appEnvSource], retainOnDelete: false, destinationBucket: websiteBucket, + ...(config.roles?.UIDeploymentRole && + { + role: Role.fromRoleName(this, createCdkId(['LisaRestApiUri', Roles.UI_DEPLOYMENT_ROLE]), config.roles.UIDeploymentRole), + }), + }); + } + + /** + * Create S3 read only role + * @returns {IRole} S3 read only role + */ + createS3ReadOnlyRole (): IRole { + const roleName = `${Stack.of(this).stackName}-s3-reader-role`; + return new Role(this, roleName, { + roleName, + assumedBy: new ServicePrincipal('apigateway.amazonaws.com'), + managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess')], + description: 'Allows API gateway to proxy static website assets', }); } } diff --git a/lib/user-interface/react/package-lock.json b/lib/user-interface/react/package-lock.json index ee9bf33e..cd0c4fd6 100644 --- a/lib/user-interface/react/package-lock.json +++ b/lib/user-interface/react/package-lock.json @@ -1,12 +1,12 @@ { "name": "lisa-web", - "version": "3.3.2", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lisa-web", - "version": "3.3.2", + "version": "3.4.0", "dependencies": { "@cloudscape-design/collection-hooks": "^1.0.23", "@cloudscape-design/component-toolkit": "^1.0.0-beta.65", @@ -31,6 +31,8 @@ "react-textarea-autosize": "^8.5.2", "redux-persist": "^6.0.0", "regenerator-runtime": "^0.14.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.0", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "unraw": "^3.0.0" @@ -1955,6 +1957,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2274,6 +2286,19 @@ "node": ">=6" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -4060,6 +4085,16 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4079,6 +4114,16 @@ "node": ">=12" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -4093,6 +4138,76 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-from-markdown": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", @@ -4116,55 +4231,55 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-hast": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", - "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-definitions": "^5.0.0", - "micromark-util-sanitize-uri": "^1.1.0", - "trim-lines": "^3.0.0", - "unist-util-generated": "^2.0.0", - "unist-util-position": "^4.0.0", - "unist-util-visit": "^4.0.0" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-to-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", - "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", "dependencies": { - "@types/mdast": "^3.0.0" + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "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/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" + "node_modules/mdast-util-gfm-autolink-literal/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/micromark": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", - "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4175,30 +4290,16 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-core-commonmark": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", - "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4209,29 +4310,12 @@ "url": "https://opencollective.com/unified" } ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } + "license": "MIT" }, - "node_modules/micromark-factory-destination": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", - "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "node_modules/mdast-util-gfm-autolink-literal/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", "funding": [ { "type": "GitHub Sponsors", @@ -4242,16 +4326,81 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "license": "MIT", "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-factory-label": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", - "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "node_modules/mdast-util-gfm-footnote/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-footnote/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", "funding": [ { "type": "GitHub Sponsors", @@ -4262,17 +4411,31 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", "funding": [ { "type": "GitHub Sponsors", @@ -4283,15 +4446,30 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", - "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "funding": [ { "type": "GitHub Sponsors", @@ -4302,17 +4480,17 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-factory-whitespace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", - "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { "type": "GitHub Sponsors", @@ -4323,17 +4501,18 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "funding": [ { "type": "GitHub Sponsors", @@ -4344,15 +4523,16 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-chunked": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", - "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "funding": [ { "type": "GitHub Sponsors", @@ -4363,14 +4543,18 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^1.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-classify-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", - "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { "type": "GitHub Sponsors", @@ -4381,16 +4565,18 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-combine-extensions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", - "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4401,15 +4587,16 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", - "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "funding": [ { "type": "GitHub Sponsors", @@ -4420,14 +4607,75 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^1.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-decode-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", - "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "funding": [ { "type": "GitHub Sponsors", @@ -4438,17 +4686,18 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", - "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { "type": "GitHub Sponsors", @@ -4458,12 +4707,13 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, - "node_modules/micromark-util-html-tag-name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", - "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { "type": "GitHub Sponsors", @@ -4473,12 +4723,13 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, - "node_modules/micromark-util-normalize-identifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", - "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4489,14 +4740,15 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-symbol": "^1.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-resolve-all": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", - "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "funding": [ { "type": "GitHub Sponsors", @@ -4507,14 +4759,15 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-types": "^1.0.0" + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", - "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { "type": "GitHub Sponsors", @@ -4525,16 +4778,17 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/micromark-util-subtokenize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", - "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", "funding": [ { "type": "GitHub Sponsors", @@ -4545,17 +4799,18 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4565,12 +4820,13 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, - "node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "node_modules/mdast-util-gfm-footnote/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", "funding": [ { "type": "GitHub Sponsors", @@ -4580,581 +4836,4305 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/mdast-util-gfm-footnote/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@types/unist": "^3.0.0" }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "@types/unist": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/mdast-util-gfm-strikethrough/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" }, - "node_modules/mnth": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mnth/-/mnth-2.0.0.tgz", - "integrity": "sha512-3ZH4UWBGpAwCKdfjynLQpUDVZWMe6vRHwarIpMdGLUp89CVR9hjzgyWERtMyqx+fPEqQ/PsAxFwvwPxLFxW40A==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.8.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==" - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", - "bin": { - "mustache": "bin/mustache" + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "github", - "url": "https://paypal.me/jimmywarting" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], - "engines": { - "node": ">=10.5.0" + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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==", - "engines": { - "node": ">=0.10.0" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/oidc-client-ts": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.2.5.tgz", - "integrity": "sha512-omAHoLdFcylnwZeHJahOnJBwd0r78JzhmVAmsQjLGrexAnQKiHW9Ilr9FlRD5qjMikmabvaucI4k49AbQLXhmQ==", - "peer": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "crypto-js": "^4.1.1", - "jwt-decode": "^3.1.2" - }, - "engines": { - "node": ">=12.13.0" + "micromark-util-types": "^2.0.0" } }, - "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==", + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "wrappy": "1" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-strikethrough/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" + "@types/unist": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/openai": { - "version": "4.68.4", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.68.4.tgz", - "integrity": "sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==", + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.23.8" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.61", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.61.tgz", - "integrity": "sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==", + "node_modules/mdast-util-gfm-table/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "@types/unist": "*" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + "node_modules/mdast-util-gfm-table/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, + "node_modules/mdast-util-gfm-table/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "engines": { - "node": ">=4" - } - }, - "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, + "node_modules/mdast-util-gfm-table/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" + "@types/mdast": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/mdast-util-gfm-table/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "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==", - "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" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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==", - "engines": { - "node": ">= 6" + "node_modules/mdast-util-gfm-table/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-table/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm-task-list-item/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-gfm/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-gfm/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", + "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-phrasing/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-phrasing/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-autolink-literal/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-footnote/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-strikethrough/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-table/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm-task-list-item/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-extension-gfm/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mnth": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mnth/-/mnth-2.0.0.tgz", + "integrity": "sha512-3ZH4UWBGpAwCKdfjynLQpUDVZWMe6vRHwarIpMdGLUp89CVR9hjzgyWERtMyqx+fPEqQ/PsAxFwvwPxLFxW40A==", + "dependencies": { + "@babel/runtime": "^7.8.0" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "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==" + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": 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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oidc-client-ts": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.2.5.tgz", + "integrity": "sha512-omAHoLdFcylnwZeHJahOnJBwd0r78JzhmVAmsQjLGrexAnQKiHW9Ilr9FlRD5qjMikmabvaucI4k49AbQLXhmQ==", + "peer": true, + "dependencies": { + "crypto-js": "^4.1.1", + "jwt-decode": "^3.1.2" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openai": { + "version": "4.68.4", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.68.4.tgz", + "integrity": "sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.61", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.61.tgz", + "integrity": "sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "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==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-import": { @@ -5162,467 +9142,1145 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", + "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-json-view-lite": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-0.9.8.tgz", + "integrity": "sha512-4D0UgQ5SjanNi2KzgEsEKOb9kj0L9HQz/3IshwIZN5reZsSU62t6sp8c+vskV42lXjGQCi7qGfoXIpNRsON7LA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-keyed-flatten-children": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-1.3.0.tgz", + "integrity": "sha512-qB7A6n+NHU0x88qTZGAJw6dsqwI941jcRPBB640c/CyWqjPQQ+YUmXOuzPziuHb7iqplM3xksWAbGYwkQT0tXA==", + "dependencies": { + "react-is": "^16.8.6" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, + "node_modules/react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/react-markdown/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==" + }, + "node_modules/react-oidc-context": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-2.3.0.tgz", + "integrity": "sha512-xT8HOliaQ3H2Q+1+nppAyOX9TlhC+ZXWkR5UDHPEfIZU+yJou1JGypUWPn1SMeFhhzovT+K7IP71/9ahVCL0+g==", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "oidc-client-ts": "^2.2.1", + "react": ">=16.8.0" + } + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/react-router": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", + "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", + "dependencies": { + "@remix-run/router": "1.7.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", + "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", + "dependencies": { + "@remix-run/router": "1.7.2", + "react-router": "6.14.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", + "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-mock-store": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz", + "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==", + "dev": true, + "dependencies": { + "lodash.isplainobject": "^4.0.6" + } + }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "peerDependencies": { + "redux": ">4.0.0" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { - "node": ">=14.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "postcss": "^8.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "node_modules/remark-breaks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz", + "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", + "license": "MIT", "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" + "@types/mdast": "^4.0.0", + "mdast-util-newline-to-break": "^2.0.0", + "unified": "^11.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" + "url": "https://opencollective.com/unified" } }, - "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "node_modules/remark-breaks/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" - }, - "engines": { - "node": ">= 14" + "@types/unist": "*" + } + }, + "node_modules/remark-breaks/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/remark-breaks/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "url": "https://opencollective.com/unified" } }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "node_modules/remark-breaks/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" }, - "peerDependencies": { - "postcss": "^8.2.14" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "node_modules/remark-breaks/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node_modules/remark-gfm/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/prettier": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", - "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" + "node_modules/remark-gfm/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/remark-gfm/node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, + "node_modules/remark-gfm/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", "dependencies": { - "fast-diff": "^1.1.2" + "@types/mdast": "^4.0.0" }, - "engines": { - "node": ">=6.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "node_modules/remark-gfm/node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-gfm/node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-gfm/node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/property-information": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", - "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/remark-gfm/node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "node_modules/remark-gfm/node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "engines": { - "node": ">=6" + "node_modules/remark-gfm/node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/remark-gfm/node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/remark-gfm/node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ] - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + ], + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "node_modules/remark-gfm/node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-json-view-lite": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-0.9.8.tgz", - "integrity": "sha512-4D0UgQ5SjanNi2KzgEsEKOb9kj0L9HQz/3IshwIZN5reZsSU62t6sp8c+vskV42lXjGQCi7qGfoXIpNRsON7LA==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "node_modules/remark-gfm/node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/react-keyed-flatten-children": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-1.3.0.tgz", - "integrity": "sha512-qB7A6n+NHU0x88qTZGAJw6dsqwI941jcRPBB640c/CyWqjPQQ+YUmXOuzPziuHb7iqplM3xksWAbGYwkQT0tXA==", + "node_modules/remark-gfm/node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "react-is": "^16.8.6" - }, - "peerDependencies": { - "react": ">=15.0.0" + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/react-markdown": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", - "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "node_modules/remark-gfm/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "@types/prop-types": "^15.0.0", - "@types/unist": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^2.0.0", - "prop-types": "^15.0.0", - "property-information": "^6.0.0", - "react-is": "^18.0.0", - "remark-parse": "^10.0.0", - "remark-rehype": "^10.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/react-markdown/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==" - }, - "node_modules/react-oidc-context": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-2.3.0.tgz", - "integrity": "sha512-xT8HOliaQ3H2Q+1+nppAyOX9TlhC+ZXWkR5UDHPEfIZU+yJou1JGypUWPn1SMeFhhzovT+K7IP71/9ahVCL0+g==", - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "oidc-client-ts": "^2.2.1", - "react": ">=16.8.0" + "node_modules/remark-gfm/node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true + "node_modules/remark-gfm/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, - "redux": { - "optional": true + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - } + ], + "license": "MIT" }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "node_modules/remark-gfm/node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/react-router": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", - "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", + "node_modules/remark-gfm/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@remix-run/router": "1.7.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/react-router-dom": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", - "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", + "node_modules/remark-gfm/node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@remix-run/router": "1.7.2", - "react-router": "6.14.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "micromark-util-types": "^2.0.0" } }, - "node_modules/react-textarea-autosize": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", - "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", + "node_modules/remark-gfm/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.20.13", - "use-composed-ref": "^1.3.0", - "use-latest": "^1.2.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "node_modules/remark-gfm/node_modules/micromark-util-subtokenize": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.3.tgz", + "integrity": "sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } + "node_modules/remark-gfm/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/read-cache/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/remark-gfm/node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/remark-gfm/node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" }, - "engines": { - "node": ">=8.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "node_modules/remark-gfm/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.9.2" + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/redux-mock-store": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/redux-mock-store/-/redux-mock-store-1.5.4.tgz", - "integrity": "sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==", - "dev": true, + "node_modules/remark-gfm/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", "dependencies": { - "lodash.isplainobject": "^4.0.6" - } - }, - "node_modules/redux-persist": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", - "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", - "peerDependencies": { - "redux": ">4.0.0" + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "peerDependencies": { - "redux": "^4" + "node_modules/remark-gfm/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, + "node_modules/remark-gfm/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/remark-parse": { @@ -5654,6 +10312,96 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-stringify/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/remark-stringify/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", @@ -6916,6 +11664,16 @@ "peerDependencies": { "zod": "^3.22.4" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/lib/user-interface/react/package.json b/lib/user-interface/react/package.json index 39323706..91a19196 100644 --- a/lib/user-interface/react/package.json +++ b/lib/user-interface/react/package.json @@ -1,7 +1,7 @@ { "name": "lisa-web", "private": true, - "version": "3.3.2", + "version": "3.4.0", "type": "module", "scripts": { "dev": "vite", @@ -34,6 +34,8 @@ "react-textarea-autosize": "^8.5.2", "redux-persist": "^6.0.0", "regenerator-runtime": "^0.14.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.0", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "unraw": "^3.0.0" diff --git a/lib/user-interface/react/src/components/chatbot/Chat.tsx b/lib/user-interface/react/src/components/chatbot/Chat.tsx index 8cd0d10d..2937ebe6 100644 --- a/lib/user-interface/react/src/components/chatbot/Chat.tsx +++ b/lib/user-interface/react/src/components/chatbot/Chat.tsx @@ -533,7 +533,7 @@ export default function Chat ({ sessionId }) { setFileContext={setFileContext} />
- + {session.history.map((message, idx) => ( ))} @@ -548,9 +548,10 @@ export default function Chat ({ sessionId }) {
-
+
-
+
+ + {message.metadata && showMetadata && ( + + + + )} + + + )} {message?.type === 'human' && ( - - {message.content} - + + + + + {message.content} + + + +
+ +
+
)}
); diff --git a/lib/user-interface/react/src/components/chatbot/RagOptions.tsx b/lib/user-interface/react/src/components/chatbot/RagOptions.tsx index eb1d5eb4..73f71157 100644 --- a/lib/user-interface/react/src/components/chatbot/RagOptions.tsx +++ b/lib/user-interface/react/src/components/chatbot/RagOptions.tsx @@ -57,7 +57,7 @@ export default function RagControls ({ auth, isRunning, setUseRag, setRagConfig repositories.map((repo) => { setRepositoryMap((map) => new Map(map.set(repo.repositoryId, repo.type))); return { - label: `${repo.repositoryId} (${repo.type})`, + label: `${repo.repositoryName || repo.repositoryId} (${repo.type})`, value: repo.repositoryId, }; }), diff --git a/lib/user-interface/react/src/components/model-management/create-model/CreateModelModal.tsx b/lib/user-interface/react/src/components/model-management/create-model/CreateModelModal.tsx index 656e6ea3..75cc4f67 100644 --- a/lib/user-interface/react/src/components/model-management/create-model/CreateModelModal.tsx +++ b/lib/user-interface/react/src/components/model-management/create-model/CreateModelModal.tsx @@ -170,7 +170,7 @@ export function CreateModelModal (props: CreateModelModalProps) : ReactElement { } } - const requiredFields = [['modelId', 'modelName'], [], [], [], []]; + const requiredFields = [['modelId', 'modelName'], ['containerConfig.image.baseImage'], [], [], []]; useEffect(() => { const parsedValue = _.mergeWith({}, initialForm, props.selectedItems[0], (a: IModelRequest, b: IModelRequest) => b === null ? a : undefined); @@ -318,8 +318,7 @@ export function CreateModelModal (props: CreateModelModalProps) : ReactElement { case 'next': case 'skip': { - touchFields(requiredFields[state.activeStepIndex]); - if (isValid) { + if (touchFields(requiredFields[state.activeStepIndex]) && isValid) { setState({ ...state, activeStepIndex: event.detail.requestedStepIndex, diff --git a/lib/user-interface/react/src/components/types.tsx b/lib/user-interface/react/src/components/types.tsx index 0f75540c..a7a696e7 100644 --- a/lib/user-interface/react/src/components/types.tsx +++ b/lib/user-interface/react/src/components/types.tsx @@ -103,6 +103,7 @@ export type RepositoryTypes = 'OpenSearch'; */ export type Repository = { repositoryId: string; + repositoryName?: string; type: RepositoryTypes; }; diff --git a/lib/user-interface/react/src/index.css b/lib/user-interface/react/src/index.css index 8c9cb2c8..c11e19ff 100644 --- a/lib/user-interface/react/src/index.css +++ b/lib/user-interface/react/src/index.css @@ -6,3 +6,8 @@ div > pre > code { word-wrap: break-word; white-space: pre-line; } + +ol { + list-style: decimal; + padding-left: 2rem; +} diff --git a/lib/user-interface/react/src/shared/model/model-management.model.ts b/lib/user-interface/react/src/shared/model/model-management.model.ts index 0721681c..92e7fd2e 100644 --- a/lib/user-interface/react/src/shared/model/model-management.model.ts +++ b/lib/user-interface/react/src/shared/model/model-management.model.ts @@ -230,5 +230,16 @@ export const ModelRequestSchema = z.object({ }); } } + + const baseImageValidator = z.string().min(1, {message: 'Required for LISA hosted models.'}); + const baseImageResult = baseImageValidator.safeParse(value.containerConfig.image.baseImage); + if (baseImageResult.success === false) { + for (const error of baseImageResult.error.errors) { + context.addIssue({ + ...error, + path: ['containerConfig', 'image', 'baseImage'] + }); + } + } } }); diff --git a/lib/user-interface/react/src/shared/reducers/configuration.reducer.ts b/lib/user-interface/react/src/shared/reducers/configuration.reducer.ts index 14d17561..6c2cad4b 100644 --- a/lib/user-interface/react/src/shared/reducers/configuration.reducer.ts +++ b/lib/user-interface/react/src/shared/reducers/configuration.reducer.ts @@ -21,6 +21,9 @@ import { IConfiguration } from '../model/configuration.model'; export const configurationApi = createApi({ reducerPath: 'configuration', baseQuery: lisaBaseQuery(), + tagTypes: ['configuration'], + refetchOnFocus: true, + refetchOnReconnect: true, endpoints: (builder) => ({ getConfiguration: builder.query({ query: (configScope) => ({ diff --git a/lib/user-interface/react/src/shared/reducers/model-management.reducer.ts b/lib/user-interface/react/src/shared/reducers/model-management.reducer.ts index 994eb4c4..55de56c4 100644 --- a/lib/user-interface/react/src/shared/reducers/model-management.reducer.ts +++ b/lib/user-interface/react/src/shared/reducers/model-management.reducer.ts @@ -21,6 +21,9 @@ import { IModel, IModelListResponse, IModelRequest, IModelUpdateRequest } from ' export const modelManagementApi = createApi({ reducerPath: 'models', baseQuery: lisaBaseQuery(), + tagTypes: ['models'], + refetchOnFocus: true, + refetchOnReconnect: true, endpoints: (builder) => ({ getAllModels: builder.query({ query: () => ({ diff --git a/lib/user-interface/react/src/shared/validation/index.ts b/lib/user-interface/react/src/shared/validation/index.ts index 3968221f..a48b3174 100644 --- a/lib/user-interface/react/src/shared/validation/index.ts +++ b/lib/user-interface/react/src/shared/validation/index.ts @@ -112,7 +112,7 @@ export type SetFieldsFunction = ( ) => void; -export type TouchFieldsFunction = (fields: string[], method?: ValidationTouchActionMethod) => void; +export type TouchFieldsFunction = (fields: string[], method?: ValidationTouchActionMethod) => boolean; /** @@ -268,7 +268,7 @@ export const useValidationReducer = > return { state, errors, - isValid: parseResult.success, + isValid: Object.keys(errors).length === 0, setState: (newState: Partial, method: ValidationStateActionMethod = ModifyMethod.Default) => { setState({ type: ValidationReducerActionTypes.STATE, @@ -289,12 +289,19 @@ export const useValidationReducer = > touchFields: ( fields: string[], method: ValidationTouchActionMethod = ModifyMethod.Default - ) => { + ): boolean => { setState({ type: ValidationReducerActionTypes.TOUCH, method, fields, } as ValidationTouchAction); + const parseResult = formSchema.safeParse({...state.form, ...{touched: fields}}); + if (!parseResult.success) { + errors = issuesToErrors(parseResult.error.issues, fields.reduce((acc, key) => { + acc[key] = true; return acc; + }, {})); + } + return Object.keys(errors).length === 0; }, }; }; diff --git a/lib/user-interface/react/tsconfig.json b/lib/user-interface/react/tsconfig.json index 87bcd380..56fdb076 100644 --- a/lib/user-interface/react/tsconfig.json +++ b/lib/user-interface/react/tsconfig.json @@ -13,7 +13,8 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "outDir": "dist/" }, "linterOptions": { "exclude": ["src", "node_modules", "build", "dist", "coverage", "scripts", "test", "tests"] diff --git a/lisa-sdk/lisapy/langchain.py b/lisa-sdk/lisapy/langchain.py index 0e12404a..3674137c 100644 --- a/lisa-sdk/lisapy/langchain.py +++ b/lisa-sdk/lisapy/langchain.py @@ -21,9 +21,8 @@ from langchain.llms.base import LLM from langchain.schema.output import GenerationChunk from langchain_core.embeddings import Embeddings -from langchain_core.pydantic_v1 import BaseModel, Extra from langchain_openai import OpenAIEmbeddings -from pydantic import PrivateAttr +from pydantic import BaseModel, Extra, PrivateAttr from .main import FoundationModel, Lisa @@ -44,12 +43,12 @@ class LisaTextgen(LLM): client: Lisa """An instance of the Lisa client.""" - foundation_model: FoundationModel = PrivateAttr(default_factory=None) + _foundation_model: FoundationModel = PrivateAttr(default_factory=None) def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - self.foundation_model = self.client.describe_model(self.provider, self.model_name) + self._foundation_model = self.client.describe_model(self.provider, self.model_name) @property def _llm_type(self) -> str: @@ -63,7 +62,7 @@ def _identifying_params(self) -> Mapping[str, Any]: "provider": self.provider, "model_name": self.model_name, "client": self.client, - "foundation_model": self.foundation_model, + "foundation_model": self._foundation_model, } def _call( @@ -73,13 +72,13 @@ def _call( run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> str: - if self.foundation_model.streaming: + if self._foundation_model.streaming: completion = "" for chunk in self._stream(prompt, stop, run_manager, **kwargs): completion += chunk.text return completion - text, _ = self.client.generate(prompt, self.foundation_model) + text, _ = self.client.generate(prompt, self._foundation_model) return text # type: ignore @@ -114,7 +113,7 @@ class LisaOpenAIEmbeddings(BaseModel, Embeddings): verify: Union[bool, str] """Cert path or option for verifying SSL""" - embedding_model: OpenAIEmbeddings = PrivateAttr(default_factory=None) + _embedding_model: OpenAIEmbeddings = PrivateAttr(default_factory=None) """OpenAI-compliant client for making requests against embedding model.""" class Config: @@ -125,7 +124,7 @@ class Config: def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.embedding_model = OpenAIEmbeddings( + self._embedding_model = OpenAIEmbeddings( openai_api_base=self.lisa_openai_api_base, openai_api_key=self.api_token, model=self.model, @@ -138,19 +137,19 @@ def __init__(self, **kwargs: Any) -> None: def embed_documents(self, texts: List[str]) -> List[List[float]]: """Use OpenAI API to embed a list of documents.""" - return cast(List[List[float]], self.embedding_model.embed_documents(texts=texts)) + return cast(List[List[float]], self._embedding_model.embed_documents(texts=texts)) def embed_query(self, text: str) -> List[float]: """Use OpenAI API to embed a text.""" - return cast(List[float], self.embedding_model.embed_query(text=text)) + return cast(List[float], self._embedding_model.embed_query(text=text)) async def aembed_documents(self, texts: List[str]) -> List[List[float]]: """Use OpenAI API to embed a list of documents.""" - return cast(List[List[float]], await self.embedding_model.aembed_documents(texts=texts)) + return cast(List[List[float]], await self._embedding_model.aembed_documents(texts=texts)) async def aembed_query(self, text: str) -> List[float]: """Use OpenAI API to embed a text.""" - return cast(List[float], await self.embedding_model.aembed_query(text=text)) + return cast(List[float], await self._embedding_model.aembed_query(text=text)) class LisaEmbeddings(BaseModel, Embeddings): @@ -169,7 +168,7 @@ class LisaEmbeddings(BaseModel, Embeddings): client: Lisa """An instance of the Lisa client.""" - foundation_model: FoundationModel = PrivateAttr(default_factory=None) + _foundation_model: FoundationModel = PrivateAttr(default_factory=None) class Config: """Configuration for this pydantic object.""" @@ -179,7 +178,7 @@ class Config: def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.foundation_model = self.client.describe_model(self.provider, self.model_name) + self._foundation_model = self.client.describe_model(self.provider, self.model_name) def embed_documents(self, texts: List[str]) -> List[List[float]]: """Compute doc embeddings using a LISA model. @@ -194,7 +193,7 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]: List[List[float]] List of embeddings, one for each text. """ - return self.client.embed(texts, self.foundation_model) + return self.client.embed(texts, self._foundation_model) def embed_query(self, text: str) -> List[float]: """Compute query embeddings using a LISA model. @@ -209,7 +208,7 @@ def embed_query(self, text: str) -> List[float]: List[float] Embedding for the text. """ - return self.client.embed(text, self.foundation_model)[0] + return self.client.embed(text, self._foundation_model)[0] async def aembed_query(self, text: str) -> List[float]: """Asynchronous compute query embeddings using a LISA model. @@ -224,7 +223,7 @@ async def aembed_query(self, text: str) -> List[float]: List[float] Embedding for the text. """ - return (await self.client.aembed(text, self.foundation_model))[0] + return (await self.client.aembed(text, self._foundation_model))[0] async def aembed_documents(self, texts: List[str]) -> List[List[float]]: """Asynchronous compute doc embeddings using a LISA model. @@ -239,4 +238,4 @@ async def aembed_documents(self, texts: List[str]) -> List[List[float]]: List[List[float]] List of embeddings, one for each text. """ - return await self.client.aembed(texts, self.foundation_model) + return await self.client.aembed(texts, self._foundation_model) diff --git a/lisa-sdk/pyproject.toml b/lisa-sdk/pyproject.toml index 01cda84b..79bb9828 100644 --- a/lisa-sdk/pyproject.toml +++ b/lisa-sdk/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "lisapy" -version = "3.3.2" +version = "3.4.0" description = "A simple SDK to help you interact with LISA. LISA is an LLM hosting solution for AWS dedicated clouds or ADCs." authors = ["Steve Goley "] readme = "README.md" diff --git a/package-lock.json b/package-lock.json index b747aa63..e6361420 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lisa", - "version": "3.3.2", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lisa", - "version": "3.3.2", + "version": "3.4.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -16,6 +16,7 @@ "constructs": "^10.0.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "react-avatar": "^5.0.3", "source-map-support": "^0.5.21", "util": "^0.12.5", "zod": "^3.22.3" @@ -1194,6 +1195,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "peer": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", @@ -5457,6 +5471,15 @@ "node": ">=10" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -5669,6 +5692,18 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-js-pure": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.39.0.tgz", + "integrity": "sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -5796,6 +5831,15 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -7631,6 +7675,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -7806,6 +7856,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-retina": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-retina/-/is-retina-1.0.3.tgz", + "integrity": "sha512-/tCmbIETZwCd8uHWO+GvbRa7jxwHFHdfetHfiwoP0aN9UDf3prUJMtKn7iBFYipYhqY1bSTjur8hC/Dakt8eyw==", + "license": "MIT" + }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", @@ -9739,8 +9795,7 @@ "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 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "4.1.0", @@ -10227,6 +10282,19 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -10275,6 +10343,17 @@ "tmpl": "1.0.5" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -10398,9 +10477,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -10454,6 +10533,16 @@ "node": ">=8" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -10899,6 +10988,25 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "peer": true + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -10953,6 +11061,35 @@ } ] }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-avatar": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-avatar/-/react-avatar-5.0.3.tgz", + "integrity": "sha512-DNc+qkWH9QehSEZqHBhqpXWsPY+rU9W7kD68QFHfu8Atfsvx/3ML0DzAePgTUd96nCXQQ3KZMcC3LKYT8FiBIg==", + "license": "MIT", + "dependencies": { + "is-retina": "^1.0.3", + "md5": "^2.0.0" + }, + "peerDependencies": { + "@babel/runtime": ">=7", + "core-js-pure": ">=3", + "prop-types": "^15.0.0 || ^16.0.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -10971,6 +11108,13 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT", + "peer": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", diff --git a/package.json b/package.json index 3f4334c7..c0d4fba3 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,18 @@ { "name": "lisa", - "version": "3.3.2", + "version": "3.4.0", "bin": { "lisa": "bin/lisa.js" }, "scripts": { - "build": "tsc && cd ./cdk_runner_lambda && npm run build", + "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk", "prepare": "husky install", "migrate-properties": "node ./scripts/migrate-properties.mjs", - "postinstall": "(cd lib/user-interface/react && npm install) && (cd lib/docs && npm install)", - "postbuild": "(cd lib/user-interface/react && npm build) && (cd lib/docs && npm build)", + "postinstall": "(cd lib/user-interface/react && npm install) && (cd lib/docs && npm install) && (cd ecs_model_deployer && npm install)", + "postbuild": "(cd lib/user-interface/react && npm run build) && (cd lib/docs && npm run build) && (cd ecs_model_deployer && npm run build)", "generateSchemaDocs": "npx zod2md -c ./lib/zod2md.config.ts" }, "devDependencies": { @@ -42,14 +42,15 @@ }, "dependencies": { "aws-cdk-lib": "2.125.0", + "aws-sdk": "^2.0.0", "cdk-nag": "^2.27.198", "constructs": "^10.0.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "react-avatar": "^5.0.3", "source-map-support": "^0.5.21", "util": "^0.12.5", - "zod": "^3.22.3", - "aws-sdk": "^2.0.0" + "zod": "^3.22.3" }, "lint-staged": { "*.ts": [ diff --git a/scripts/gen-certs.sh b/scripts/gen-certs.sh old mode 100644 new mode 100755 index 8db2173a..80a2f37d --- a/scripts/gen-certs.sh +++ b/scripts/gen-certs.sh @@ -12,10 +12,23 @@ if [[ -z $REGION ]]; then exit 1 fi -domain="*.$REGION.elb.amazonaws.com" +if [[ -z $DOMAIN ]]; then + DOMAIN="amazonaws.com" +fi + +domain="*.$REGION.elb.$DOMAIN" # Check if the certificate and key files already exist if [ ! -f "$outPathCert" ] || [ ! -f "$outPathKey" ]; then + echo "Generating certificate and key for $domain..." + openssl_version=$(openssl version | awk '{print $2}') + maj=$(echo "$openssl_version" | cut -d. -f1) + min=$(echo "$openssl_version" | cut -d. -f2) + if [ $maj -eq 1 ] && [ $min -lt 10 ] || [ $maj -lt 1 ]; then + echo "Warning: Your version of OpenSSL ${openssl_version} is not supported. Please upgrade to version 1.10+" + exit 1 + fi + openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \ -nodes -keyout ${outPathKey} -out ${outPathCert} -subj "/CN=${domain}" \ -addext "subjectAltName=DNS:${domain}" &> /dev/null diff --git a/test/cdk/mocks/MockApp.ts b/test/cdk/mocks/MockApp.ts index e7fab84a..6999a8a5 100644 --- a/test/cdk/mocks/MockApp.ts +++ b/test/cdk/mocks/MockApp.ts @@ -26,6 +26,8 @@ import { UserInterfaceStack } from '../../../lib/user-interface/index'; import ConfigParser from './ConfigParser'; import { Config } from '../../../lib/schema'; import { LisaDocsStack } from '../../../lib/docs'; +import { LisaModelsApiStack } from '../../../lib/models'; +import { LisaRagStack } from '../../../lib/rag'; export default class MockApp { @@ -77,6 +79,18 @@ export default class MockApp { stackName: 'LisaServe', vpc: networkingStack.vpc, }); + + const modelsStack = new LisaModelsApiStack(app, 'LisaModels', { + ...baseStackProps, + stackName: 'LisaModels', + authorizer: apiBaseStack.authorizer, + lisaServeEndpointUrlPs: serveStack.endpointUrl, + restApiId: apiBaseStack.restApiId, + rootResourceId: apiBaseStack.rootResourceId, + securityGroups: [networkingStack.vpc.securityGroups.ecsModelAlbSg], + vpc: networkingStack.vpc, + }); + const uiStack = new UserInterfaceStack(app, 'LisaUI', { ...baseStackProps, architecture: ARCHITECTURE, @@ -85,6 +99,18 @@ export default class MockApp { rootResourceId: apiBaseStack.rootResourceId, }); + const ragStack = new LisaRagStack(app, 'LisaRAG', { + ...baseStackProps, + stackName: 'LisaRAG', + authorizer: apiBaseStack.authorizer, + endpointUrl: serveStack.endpointUrl, + modelsPs: serveStack.modelsPs, + restApiId: apiBaseStack.restApiId, + rootResourceId: apiBaseStack.rootResourceId, + securityGroups: [networkingStack.vpc.securityGroups.lambdaSg], + vpc: networkingStack.vpc, + }); + const docStack = new LisaDocsStack(app, 'LisaDocs',{ ...baseStackProps, stackName: 'LisaDocs' @@ -98,7 +124,9 @@ export default class MockApp { chatStack, coreStack, serveStack, + modelsStack, uiStack, + ragStack, docStack ]; diff --git a/test/cdk/mocks/config.yaml b/test/cdk/mocks/config.yaml index 60e432ae..0c0150d7 100644 --- a/test/cdk/mocks/config.yaml +++ b/test/cdk/mocks/config.yaml @@ -41,9 +41,27 @@ dev: sslCertIamArn: arn:aws:iam::012345678901:server-certificate/lisa-self-signed-dev ragRepositories: - repositoryId: pgvector-rag + repositoryName: "PGVector" type: pgvector rdsConfig: username: postgres + - repositoryId: pgvector-rag2 + repositoryName: "PGVector 2" + type: pgvector + rdsConfig: + username: postgres + allowedGroups: + - group1 + - repositoryId: opensearch-rag + type: opensearch + repositoryName: "Opensearch 2" + opensearchConfig: + dataNodes: 2 + dataNodeInstanceType: r7g.large.search + masterNodes: 0 + masterNodeInstanceType: r7g.large.search + volumeSize: 20 + multiAzWithStandby: false # - repositoryId: default # type: opensearch # opensearchConfig: diff --git a/test/cdk/mocks/roles.yaml b/test/cdk/mocks/roles.yaml new file mode 100644 index 00000000..90bca538 --- /dev/null +++ b/test/cdk/mocks/roles.yaml @@ -0,0 +1,35 @@ +env: dev + +dev: + roles: + #RagRole / LisaServeIam Roles + RagLambdaExecutionRole: TestRagRole + ECSRestApiRole: TestEcsRestApiRole + ECSRestApiExRole: TestEcsRestApiRole + + #ImageBuilderRoles + DockerImageBuilderEC2Role: TestImageBuilderRole #ImageBuilder + DockerImageBuilderRole: TestImageBuilderRole #ImageBuilder + DockerImageBuilderDeploymentRole: TestImageBuilderRole #ImageBuilder + + # ModelDeployerRole + ECSModelDeployerRole: TestECSModelDeployerRole + ECSModelTaskRole: TestECSModelDeployerRole + + #ModelAPIRole + ModelsSfnLambdaRole: TestModelApiRole + ModelSfnRole: TestModelApiRole + ModelApiRole: TestModelApiRole + + #ChatUIRoles + LambdaExecutionRole: TestLambdaExecutionRole #SessionAPI + LambdaConfigurationApiExecutionRole: TestConfigurationApiExecutionRole + S3ReaderRole: TestReaderRole + UIDeploymentRole: TestDeployerRole + + #RestAPIRoles + RestApiAuthorizerRole: TestAuthorizerRole + + #Docs + DocsRole: TestDocsRole + DocsDeployerRole: TestDocsRole diff --git a/test/cdk/stacks/chat.test.ts b/test/cdk/stacks/chat.test.ts index da466d75..e7cb1a4c 100644 --- a/test/cdk/stacks/chat.test.ts +++ b/test/cdk/stacks/chat.test.ts @@ -1,32 +1,28 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { LisaChatApplicationStack } from '../../../lib/chat'; import { LisaApiBaseStack } from '../../../lib/core/api_base'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -38,27 +34,7 @@ describe.each(regions)('Chat Nag Pack Tests | Region Test: %s', (awsRegion) => { beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/core-api-base.test.ts b/test/cdk/stacks/core-api-base.test.ts index c3e543a6..97d3aa43 100644 --- a/test/cdk/stacks/core-api-base.test.ts +++ b/test/cdk/stacks/core-api-base.test.ts @@ -1,31 +1,28 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { LisaApiBaseStack } from '../../../lib/core/api_base'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -37,27 +34,7 @@ describe.each(regions)('API Core Nag Pack Tests | Region Test: %s', (awsRegion) beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/core-api-deploy.test.ts b/test/cdk/stacks/core-api-deploy.test.ts index 7b0fe20e..0087dc0f 100644 --- a/test/cdk/stacks/core-api-deploy.test.ts +++ b/test/cdk/stacks/core-api-deploy.test.ts @@ -1,32 +1,28 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { LisaApiBaseStack } from '../../../lib/core/api_base'; import { LisaApiDeploymentStack } from '../../../lib/core/api_deployment'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -38,27 +34,7 @@ describe.each(regions)('API Core Deployment Nag Pack Tests | Region Test: %s', ( beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', @@ -84,13 +60,11 @@ describe.each(regions)('API Core Deployment Nag Pack Tests | Region Test: %s', ( tempStack.authorizer._attachToApi(tempStack.restApi); - const apiDeploymentStack = new LisaApiDeploymentStack(app, 'LisaApiDeployment', { + stack = new LisaApiDeploymentStack(app, 'LisaApiDeployment', { ...baseStackProps, restApiId: tempStack.restApiId, }); - stack = apiDeploymentStack; - // WHEN Aspects.of(stack).add(new AwsSolutionsChecks({ verbose: true })); Aspects.of(stack).add(new NIST80053R5Checks({ verbose: true })); diff --git a/test/cdk/stacks/core.test.ts b/test/cdk/stacks/core.test.ts index 5be9d4ce..2820e186 100644 --- a/test/cdk/stacks/core.test.ts +++ b/test/cdk/stacks/core.test.ts @@ -1,31 +1,28 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { CoreStack } from '../../../lib/core'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -40,27 +37,7 @@ xdescribe.each(regions)('Core Nag Pack Tests | Region Test: %s', (awsRegion) => beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/docs.test.ts b/test/cdk/stacks/docs.test.ts index e5e2a924..2ac27593 100644 --- a/test/cdk/stacks/docs.test.ts +++ b/test/cdk/stacks/docs.test.ts @@ -1,29 +1,25 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; -import { LisaDocsStack } from '../../../lib/docs/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaDocsStack } from '../../../lib/docs'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -35,27 +31,7 @@ describe.each(regions)('Docs Nag Pack Tests | Region Test: %s', (awsRegion) => { beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../../../test/cdk/mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/iam-stack.test.ts b/test/cdk/stacks/iam-stack.test.ts index a4aac9d6..efcd23b1 100644 --- a/test/cdk/stacks/iam-stack.test.ts +++ b/test/cdk/stacks/iam-stack.test.ts @@ -1,29 +1,25 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { LisaServeIAMStack } from '../../../lib/iam_stack'; -import { Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -35,28 +31,7 @@ describe.each(regions)('IAM Stack CDK Nag Tests | Region Test: %s', (awsRegion: beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/networking.test.ts b/test/cdk/stacks/networking.test.ts index bf291aea..24d024bd 100644 --- a/test/cdk/stacks/networking.test.ts +++ b/test/cdk/stacks/networking.test.ts @@ -1,30 +1,26 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { createCdkId } from '../../../lib/core/utils'; import { LisaNetworkingStack } from '../../../lib/networking'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { BaseProps, Config } from '../../../lib/schema'; +import ConfigParser from '../mocks/ConfigParser'; type TestConfig = [string, number, number, number, number]; type TestValues = [number, number, number, number]; @@ -49,27 +45,7 @@ describe.each(regions)( beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/roleOverrides.test.ts b/test/cdk/stacks/roleOverrides.test.ts new file mode 100644 index 00000000..b2358c07 --- /dev/null +++ b/test/cdk/stacks/roleOverrides.test.ts @@ -0,0 +1,74 @@ +/** + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import { Template } from 'aws-cdk-lib/assertions'; +import MockApp from '../mocks/MockApp'; // Import your actual stack +import ConfigParser from '../mocks/ConfigParser'; +import { Roles } from '../../../lib/core/iam/roles'; + +const stackRolesOverrides: Record = { + 'LisaApiBase': 1, + 'LisaServe': 3, + 'LisaUI': 1, + 'LisaDocs': 2, + 'LisaRAG': 2, +}; + +const stackRoles: Record = { + 'LisaApiBase': 2, + 'LisaServe': 4, + 'LisaUI': 3, + 'LisaNetworking': 0, + 'LisaChat': 2, + 'LisaCore': 0, + 'LisaApiDeployment': 0, + 'LisaIAM': 2, + 'LisaDocs': 4, + 'LisaModels': 9, + 'LisaRAG': 2, +}; + +describe('Verify role overrides', () => { + const config = ConfigParser.parseConfig(['config.yaml', 'roles.yaml']); + expect(Object.keys(config.roles || {}).length).toBe(Object.keys(Roles).length); + + const { stacks } = MockApp.create(config); + describe('Number of IAM Roles created with overrides', () => { + for (const stack of stacks) { + const expectedRoles = stackRolesOverrides[stack.stackName] || 0; + + it(`${stack} should contain ${expectedRoles} roles`, () => { + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::IAM::Role', expectedRoles); + }); + } + }); +}); + +describe('Verify created roles', () => { + const { stacks } = MockApp.create(); + describe('Number of IAM Roles created', () => { + + for (const stack of stacks) { + const expectedRoles = stackRoles[stack.stackName] || 0; + + it(`${stack} should contain ${expectedRoles} roles`, () => { + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::IAM::Role', expectedRoles); + }); + } + }); +}); diff --git a/test/cdk/stacks/securityGroupOverrides.test.ts b/test/cdk/stacks/securityGroupOverrides.test.ts index 26423838..1807e8ce 100644 --- a/test/cdk/stacks/securityGroupOverrides.test.ts +++ b/test/cdk/stacks/securityGroupOverrides.test.ts @@ -24,7 +24,8 @@ const stackGroupOverrides: Record = { const stackGroups: Record = { LisaServe: 2, - LisaNetworking: 3 + LisaNetworking: 3, + LisaRAG: 2, }; const RESOURCE = 'AWS::EC2::SecurityGroup'; diff --git a/test/cdk/stacks/serve.test.ts b/test/cdk/stacks/serve.test.ts index 9bfbced0..71264b95 100644 --- a/test/cdk/stacks/serve.test.ts +++ b/test/cdk/stacks/serve.test.ts @@ -1,31 +1,27 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; import { LisaServeApplicationStack } from '../../../lib/serve'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -37,27 +33,7 @@ describe.each(regions)('Serve Nag Pack Tests | Region Test: %s', (awsRegion) => beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/test/cdk/stacks/ui.test.ts b/test/cdk/stacks/ui.test.ts index 63066fa4..a3c4a338 100644 --- a/test/cdk/stacks/ui.test.ts +++ b/test/cdk/stacks/ui.test.ts @@ -1,33 +1,30 @@ /** - Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"). - You may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"). + You may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -import * as fs from 'fs'; -import * as path from 'path'; + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ import { App, Aspects, Stack, StackProps } from 'aws-cdk-lib'; import { Annotations, Match } from 'aws-cdk-lib/assertions'; import { AwsSolutionsChecks, NIST80053R5Checks } from 'cdk-nag'; -import * as yaml from 'js-yaml'; import { ARCHITECTURE } from '../../../lib/core'; import { LisaApiBaseStack } from '../../../lib/core/api_base'; import { createCdkId } from '../../../lib/core/utils'; -import { LisaNetworkingStack } from '../../../lib/networking/index'; -import { BaseProps, Config, ConfigFile, ConfigSchema } from '../../../lib/schema'; +import { LisaNetworkingStack } from '../../../lib/networking'; +import { BaseProps, Config } from '../../../lib/schema'; import { UserInterfaceStack } from '../../../lib/user-interface'; +import ConfigParser from '../mocks/ConfigParser'; const regions = ['us-east-1', 'us-gov-west-1', 'us-gov-east-1', 'us-isob-east-1', 'us-iso-east-1', 'us-iso-west-1']; @@ -39,27 +36,7 @@ describe.each(regions)('UI Nag Pack Tests | Region Test: %s', (awsRegion) => { beforeAll(() => { app = new App(); - - // Read configuration file - const configFilePath = path.join(__dirname, '../../../test/cdk/mocks/config.yaml'); - const configFile = yaml.load(fs.readFileSync(configFilePath, 'utf8')) as ConfigFile; - const configEnv = configFile.env || 'dev'; - const configData = configFile[configEnv]; - if (!configData) { - throw new Error(`Configuration for environment "${configEnv}" not found.`); - } - // Validate and parse configuration - try { - config = ConfigSchema.parse(configData); - } catch (error) { - if (error instanceof Error) { - console.error('Error parsing the configuration:', error.message); - } else { - console.error('An unexpected error occurred:', error); - } - process.exit(1); - } - + config = ConfigParser.parseConfig(); baseStackProps = { env: { account: '012345678901', diff --git a/tsconfig.json b/tsconfig.json index 747a2f76..855ed2d9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,5 +22,5 @@ "typeRoots": ["./node_modules/@types"], "outDir": "dist/" }, - "exclude": ["node_modules", "cdk.out", ".git", "dist"] + "exclude": ["node_modules", "cdk.out", ".git", "dist", "lib/user-interface/react"] }