From 11e082f186676fa3e749e9064eeedefdbde234a2 Mon Sep 17 00:00:00 2001 From: Pelin Kuran <52086912+pelinKuran@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:16:32 +0200 Subject: [PATCH 01/22] Automated metadata generation using genAI MVP (#1598) ### Feature - Feature ### Detail - Automated metadata generation using gen AI. MVP phase ### Related #1599 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: dlpzx --- .../modules/s3_datasets/api/dataset/enums.py | 9 + .../s3_datasets/api/dataset/input_types.py | 17 + .../s3_datasets/api/dataset/mutations.py | 20 +- .../s3_datasets/api/dataset/queries.py | 17 + .../s3_datasets/api/dataset/resolvers.py | 70 ++- .../modules/s3_datasets/api/dataset/types.py | 44 ++ .../api/table_column/input_types.py | 8 + .../s3_datasets/api/table_column/mutations.py | 12 +- .../s3_datasets/api/table_column/resolvers.py | 6 + .../aws/bedrock_metadata_client.py | 145 +++++ .../s3_datasets/aws/s3_dataset_client.py | 13 + .../db/dataset_column_repositories.py | 29 +- .../modules/s3_datasets/db/dataset_models.py | 3 + .../s3_datasets/db/dataset_repositories.py | 43 +- .../services/dataset_column_service.py | 8 + .../s3_datasets/services/dataset_enums.py | 18 + .../services/dataset_location_service.py | 26 + .../s3_datasets/services/dataset_service.py | 28 + .../services/dataset_table_service.py | 24 + config.json | 6 +- .../components/GenerateMetadataComponent.js | 424 +++++++++++++++ .../components/MetadataMainModal.js | 77 +++ .../components/ReviewMetadataComponent.js | 494 ++++++++++++++++++ .../S3_Datasets/components/SampleDataPopup.js | 43 ++ .../components/SampleDataTableComponent.js | 52 ++ .../components/SubitemDescriptionsGrid.js | 78 +++ .../modules/S3_Datasets/components/index.js | 1 + .../batchUpdateTableColumnDescriptions.js | 14 + .../services/generateMetadataBedrock.js | 46 ++ .../src/modules/S3_Datasets/services/index.js | 2 + .../services/listDatasetTablesFolders.js | 27 + .../S3_Datasets/services/listSampleData.js | 15 + .../modules/S3_Datasets/views/DatasetView.js | 36 +- frontend/src/utils/helpers/moduleUtils.js | 8 +- 34 files changed, 1850 insertions(+), 13 deletions(-) create mode 100644 backend/dataall/modules/s3_datasets/api/dataset/enums.py create mode 100644 backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py create mode 100644 backend/dataall/modules/s3_datasets/services/dataset_enums.py create mode 100644 frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js create mode 100644 frontend/src/modules/S3_Datasets/components/MetadataMainModal.js create mode 100644 frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js create mode 100644 frontend/src/modules/S3_Datasets/components/SampleDataPopup.js create mode 100644 frontend/src/modules/S3_Datasets/components/SampleDataTableComponent.js create mode 100644 frontend/src/modules/S3_Datasets/components/SubitemDescriptionsGrid.js create mode 100644 frontend/src/modules/S3_Datasets/services/batchUpdateTableColumnDescriptions.js create mode 100644 frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js create mode 100644 frontend/src/modules/S3_Datasets/services/listDatasetTablesFolders.js create mode 100644 frontend/src/modules/S3_Datasets/services/listSampleData.js diff --git a/backend/dataall/modules/s3_datasets/api/dataset/enums.py b/backend/dataall/modules/s3_datasets/api/dataset/enums.py new file mode 100644 index 000000000..16aa95907 --- /dev/null +++ b/backend/dataall/modules/s3_datasets/api/dataset/enums.py @@ -0,0 +1,9 @@ +from dataall.base.api.constants import GraphQLEnumMapper + + +class MetadataGenerationTargets(GraphQLEnumMapper): + """Describes the s3_datasets metadata generation targets""" + + Table = 'Table' + Folder = 'Folder' + S3_Dataset = 'S3_Dataset' diff --git a/backend/dataall/modules/s3_datasets/api/dataset/input_types.py b/backend/dataall/modules/s3_datasets/api/dataset/input_types.py index ced7ddf6a..ada6315b5 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/input_types.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/input_types.py @@ -46,6 +46,15 @@ gql.Argument(name='expiryMaxDuration', type=gql.Integer), ], ) +DatasetMetadataInput = gql.InputType( + name='DatasetMetadataInput', + arguments=[ + gql.Argument('label', gql.String), + gql.Argument('description', gql.String), + gql.Argument('tags', gql.ArrayType(gql.String)), + gql.Argument('topics', gql.ArrayType(gql.Ref('Topic'))), + ], +) DatasetPresignedUrlInput = gql.InputType( name='DatasetPresignedUrlInput', @@ -58,6 +67,14 @@ CrawlerInput = gql.InputType(name='CrawlerInput', arguments=[gql.Argument(name='prefix', type=gql.String)]) +SampleDataInput = gql.InputType( + name='SampleDataInput', + arguments=[ + gql.Field(name='fields', type=gql.ArrayType(gql.String)), + gql.Field(name='rows', type=gql.ArrayType(gql.String)), + ], +) + ImportDatasetInput = gql.InputType( name='ImportDatasetInput', arguments=[ diff --git a/backend/dataall/modules/s3_datasets/api/dataset/mutations.py b/backend/dataall/modules/s3_datasets/api/dataset/mutations.py index d82f98194..5df48bf4f 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/mutations.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/mutations.py @@ -1,9 +1,5 @@ from dataall.base.api import gql -from dataall.modules.s3_datasets.api.dataset.input_types import ( - ModifyDatasetInput, - NewDatasetInput, - ImportDatasetInput, -) +from dataall.modules.s3_datasets.api.dataset.input_types import ModifyDatasetInput, NewDatasetInput, ImportDatasetInput from dataall.modules.s3_datasets.api.dataset.resolvers import ( create_dataset, update_dataset, @@ -11,7 +7,9 @@ delete_dataset, import_dataset, start_crawler, + generate_metadata, ) +from dataall.modules.s3_datasets.api.dataset.enums import MetadataGenerationTargets createDataset = gql.MutationField( name='createDataset', @@ -68,3 +66,15 @@ resolver=start_crawler, type=gql.Ref('GlueCrawler'), ) +generateMetadata = gql.MutationField( + name='generateMetadata', + args=[ + gql.Argument(name='resourceUri', type=gql.NonNullableType(gql.String)), + gql.Argument(name='targetType', type=gql.NonNullableType(MetadataGenerationTargets.toGraphQLEnum())), + gql.Argument(name='version', type=gql.Integer), + gql.Argument(name='metadataTypes', type=gql.NonNullableType(gql.ArrayType(gql.String))), + gql.Argument(name='sampleData', type=gql.Ref('SampleDataInput')), + ], + type=gql.Ref('GeneratedMetadata'), + resolver=generate_metadata, +) diff --git a/backend/dataall/modules/s3_datasets/api/dataset/queries.py b/backend/dataall/modules/s3_datasets/api/dataset/queries.py index 5043b868d..fdd9ed5aa 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/queries.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/queries.py @@ -4,6 +4,8 @@ get_dataset_assume_role_url, get_file_upload_presigned_url, list_datasets_owned_by_env_group, + list_dataset_tables_folders, + read_sample_data, ) getDataset = gql.QueryField( @@ -45,3 +47,18 @@ resolver=list_datasets_owned_by_env_group, test_scope='Dataset', ) +listDatasetTablesFolders = gql.QueryField( + name='listDatasetTablesFolders', + args=[ + gql.Argument(name='datasetUri', type=gql.NonNullableType(gql.String)), + gql.Argument(name='filter', type=gql.Ref('DatasetFilter')), + ], + type=gql.Ref('DatasetItemsSearchResult'), + resolver=list_dataset_tables_folders, +) +listSampleData = gql.QueryField( + name='listSampleData', + args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], + type=gql.Ref('QueryPreviewResult'), # basically returns nothing...? + resolver=read_sample_data, +) # return the data -> user invokes generateMetadata again + sample data ; similar api exists diff --git a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py index 90f6fd3d9..d97eb8429 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py @@ -1,5 +1,5 @@ import logging - +import re from dataall.base.api.context import Context from dataall.base.feature_toggle_checker import is_feature_enabled from dataall.base.utils.expiration_util import Expiration @@ -11,6 +11,9 @@ from dataall.modules.s3_datasets.db.dataset_models import S3Dataset from dataall.modules.datasets_base.services.datasets_enums import DatasetRole, ConfidentialityClassification from dataall.modules.s3_datasets.services.dataset_service import DatasetService +from dataall.modules.s3_datasets.services.dataset_table_service import DatasetTableService +from dataall.modules.s3_datasets.services.dataset_location_service import DatasetLocationService +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets, MetadataGenerationTypes log = logging.getLogger(__name__) @@ -156,6 +159,59 @@ def list_datasets_owned_by_env_group( return DatasetService.list_datasets_owned_by_env_group(environmentUri, groupUri, filter) +# @ResourceThresholdRepository.invocation_handler('generate_metadata_ai') +# To make this treshold work treshold limits should be added on resource_treshold_repository into the resource paths dictionary. +# as an example; 'nlq' : 'modules.worksheets.features.max_count_per_day' here max_count_per_day shall be defined for metadata generation +# or it could be used as it is by using different key or even the same key after merge. +@is_feature_enabled('modules.s3_datasets.features.generate_metadata_ai.active') +def generate_metadata( + context: Context, + source: S3Dataset, + resourceUri: str, + targetType: str, + version: int, + metadataTypes: list, + sampleData: dict = {}, +): + RequestValidator.validate_uri(param_name='resourceUri', param_value=resourceUri) + if metadataTypes not in [item.value for item in MetadataGenerationTypes]: + raise InvalidInput( + 'metadataType', + metadataTypes, + f'a list of allowed values {[item.value for item in MetadataGenerationTypes]}', + ) + # TODO validate sampleData and make it generic for S3 + if targetType == MetadataGenerationTargets.S3_Dataset.value: + return DatasetService.generate_metadata_for_dataset( + resourceUri=resourceUri, version=version, metadataTypes=metadataTypes + ) + elif targetType == MetadataGenerationTargets.Table.value: + return DatasetTableService.generate_metadata_for_table( + resourceUri=resourceUri, version=version, metadataTypes=metadataTypes, sampleData=sampleData + ) + elif targetType == MetadataGenerationTargets.Folder.value: + return DatasetLocationService.generate_metadata_for_folder( + resourceUri=resourceUri, version=version, metadataTypes=metadataTypes + ) + else: + raise Exception('Unsupported target type for metadata generation') + + +def read_sample_data(context: Context, source: S3Dataset, tableUri: str): + RequestValidator.validate_uri(param_name='tableUri', param_value=tableUri) + return DatasetTableService.preview(uri=tableUri) + + +def update_dataset_metadata(context: Context, source: S3Dataset, resourceUri: str): + return DatasetService.update_dataset(uri=resourceUri, data=input) + + +def list_dataset_tables_folders(context: Context, source: S3Dataset, datasetUri: str, filter: dict = None): + if not filter: + filter = {} + return DatasetService.list_dataset_tables_folders(dataset_uri=datasetUri, filter=filter) + + class RequestValidator: @staticmethod def validate_creation_request(data): @@ -200,6 +256,18 @@ def validate_share_expiration_request(data): 'is of invalid type', ) + @staticmethod + def validate_uri(param_name: str, param_value: str): + if not param_value: + raise RequiredParameter(param_name) + pattern = r'^[a-z0-9]{8}$' + if not re.match(pattern, param_value): + raise InvalidInput( + param_name=param_name, + param_value=param_value, + constraint='8 characters long and contain only lowercase letters and numbers', + ) + @staticmethod def validate_import_request(data): RequestValidator.validate_creation_request(data) diff --git a/backend/dataall/modules/s3_datasets/api/dataset/types.py b/backend/dataall/modules/s3_datasets/api/dataset/types.py index a8e11a14e..9a5c4f3f7 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/types.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/types.py @@ -140,3 +140,47 @@ gql.Field(name='status', type=gql.String), ], ) +SubitemDescription = gql.ObjectType( + name='SubitemDescription', + fields=[ + gql.Field(name='label', type=gql.String), + gql.Field(name='description', type=gql.String), + gql.Field(name='subitem_id', type=gql.String), + ], +) +GeneratedMetadata = gql.ObjectType( + name='GeneratedMetadata', + fields=[ + gql.Field(name='type', type=gql.String), # Table, Column, Folder, Dataset + gql.Field(name='label', type=gql.String), + gql.Field(name='topics', type=gql.ArrayType(gql.String)), + gql.Field(name='tags', type=gql.ArrayType(gql.String)), + gql.Field(name='description', type=gql.String), + gql.Field(name='name', type=gql.String), + gql.Field(name='subitem_descriptions', type=gql.ArrayType(gql.Ref('SubitemDescription'))), + ], +) + +DatasetItem = gql.ObjectType( + name='DatasetItem', + fields=[ + gql.Field(name='name', type=gql.String), + gql.Field(name='targetType', type=gql.String), + gql.Field(name='targetUri', type=gql.String), + ], +) + +DatasetItemsSearchResult = gql.ObjectType( + name='DatasetItemsSearchResult', + fields=[ + gql.Field(name='count', type=gql.Integer), + gql.Field(name='nodes', type=gql.ArrayType(DatasetItem)), + gql.Field(name='pageSize', type=gql.Integer), + gql.Field(name='nextPage', type=gql.Integer), + gql.Field(name='pages', type=gql.Integer), + gql.Field(name='page', type=gql.Integer), + gql.Field(name='previousPage', type=gql.Integer), + gql.Field(name='hasNext', type=gql.Boolean), + gql.Field(name='hasPrevious', type=gql.Boolean), + ], +) diff --git a/backend/dataall/modules/s3_datasets/api/table_column/input_types.py b/backend/dataall/modules/s3_datasets/api/table_column/input_types.py index ca32c83f9..2d8f90c77 100644 --- a/backend/dataall/modules/s3_datasets/api/table_column/input_types.py +++ b/backend/dataall/modules/s3_datasets/api/table_column/input_types.py @@ -18,3 +18,11 @@ gql.Argument('topics', gql.Integer), ], ) +SubitemDescription = gql.InputType( + name='SubitemDescriptionInput', + arguments=[ + gql.Argument(name='label', type=gql.String), + gql.Argument(name='description', type=gql.String), + gql.Argument(name='subitem_id', type=gql.String), + ], +) diff --git a/backend/dataall/modules/s3_datasets/api/table_column/mutations.py b/backend/dataall/modules/s3_datasets/api/table_column/mutations.py index d9ae99b6d..3ee266ff6 100644 --- a/backend/dataall/modules/s3_datasets/api/table_column/mutations.py +++ b/backend/dataall/modules/s3_datasets/api/table_column/mutations.py @@ -1,5 +1,9 @@ from dataall.base.api import gql -from dataall.modules.s3_datasets.api.table_column.resolvers import sync_table_columns, update_table_column +from dataall.modules.s3_datasets.api.table_column.resolvers import ( + sync_table_columns, + update_table_column, + batch_update_table_columns_description, +) syncDatasetTableColumns = gql.MutationField( name='syncDatasetTableColumns', @@ -18,3 +22,9 @@ type=gql.Ref('DatasetTableColumn'), resolver=update_table_column, ) +batchUpdateDatasetTableColumn = gql.MutationField( + name='batchUpdateDatasetTableColumn', + args=[gql.Argument(name='columns', type=gql.ArrayType(gql.Ref('SubitemDescriptionInput')))], + type=gql.String, + resolver=batch_update_table_columns_description, +) diff --git a/backend/dataall/modules/s3_datasets/api/table_column/resolvers.py b/backend/dataall/modules/s3_datasets/api/table_column/resolvers.py index 07cb82d5a..3acbe2408 100644 --- a/backend/dataall/modules/s3_datasets/api/table_column/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/table_column/resolvers.py @@ -41,3 +41,9 @@ def update_table_column(context: Context, source, columnUri: str = None, input: description = input.get('description', 'No description provided') return DatasetColumnService.update_table_column_description(column_uri=columnUri, description=description) + + +def batch_update_table_columns_description(context: Context, source, columns): + if columns is None: + return None + return DatasetColumnService.batch_update_table_columns_description(columns=columns) diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py new file mode 100644 index 000000000..c5940e5b4 --- /dev/null +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py @@ -0,0 +1,145 @@ +import logging +import json +from dataall.base.config import config +from dataall.base.aws.sts import SessionHelper + + +log = logging.getLogger(__name__) + +# TODO: refactoring with prompt templates + +class BedrockClient: + def __init__(self, account_id: str, region: str): + session = SessionHelper.remote_session(accountid=account_id, region=region) + self._client = session.client('bedrock-runtime', region_name=region) + + def _generate_prompt(self, **kwargs): + prompt_type = kwargs.get('prompt_type', 'Table') + common_data = { + 'label': kwargs.get('label', ''), + 'description': kwargs.get('description', ''), + 'tags': kwargs.get('tags', ''), + 'columns': kwargs.get('columns', []), + 'subitem_descriptions': kwargs.get('subitem_descriptions', []), + 'file_names': kwargs.get('file_names', []), + 'folder_name': kwargs.get('folder_name', ''), + 'folder_description': kwargs.get('folder_description', ''), + 'folder_tags': kwargs.get('folder_tags', ''), + 'tables': kwargs.get('tables', []), + 'table_description': kwargs.get('table_descriptions', ''), + 'metadata_types': kwargs.get('metadata_type', []), + 'folders': kwargs.get('folders', []), + 'sample_data': kwargs.get('sample_data', []), + } + if prompt_type == 'Table': + return f""" + Generate or improve metadata for a common_data['label'] table using the following provided data: + - Table name: {common_data['label'] if common_data['label'] else 'No description provided'} + - Column names: {common_data['columns'] if common_data['columns'] else 'No description provided'} + - Table description: {common_data['description'] if common_data['description'] else 'No description provided'} + - Tags: {common_data['tags'] if common_data['tags'] else 'No description provided'} + - Subitem Descriptions: {common_data['subitem_descriptions'] if common_data['subitem_descriptions'] else 'No description provided'} + - (Only Input) Sample data: {common_data['sample_data'] if common_data['sample_data'] else 'No sample data'} + **Important**: + - If the data indicates "No description provided," do not use that particular input for generating metadata, these data is optional you should still generate in that case. + - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. + - Sample data is only input for you to understand the table better, do not generate sample data. + Your response must strictly contain all the requested metadata types, do not include any of the metadata types if it is not specified by the user. Don't use ' ' in your response, use " ". + Subitem Descriptions corresponds to column descriptions. If the user specifically didn't ask for subitem descriptions, do not include it in the response. + subitem_descriptions is another dictionary within the existing dictionary, rest of them are strings, never change order of columns when you generate description for them. + For example, if the requested metadata types are "Tags" and "Subitem Descriptions", the response should be: + tags: + subitem_descriptions: + : + : + ,..., + : + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. + Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + + """ + + elif prompt_type == 'S3_Dataset': + return f""" + Generate or improve metadata for a dataset using the following provided data: + - Dataset name: {common_data['label'] if common_data['label'] else 'No description provided'} + - Table names in the dataset: {common_data['tables'] if common_data['tables'] else 'No description provided'} + - Folder names in the dataset: {common_data['folders'] if common_data['folders'] else 'No description provided'} + - Current tags for dataset: {common_data['tags'] if common_data['tags'] else 'No description provided'} + - Current dataset description: {common_data['description'] if common_data['description'] else 'No description provided'} + **Important**: + - If the data indicates "No description provided," do not use that particular input for generating metadata. + - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. + - Return the result as a Python dictionary. + Your response should strictly contain the requested metadata types. Don't use ' ' in your response, use " ". + For example, if the requested metadata types are "tags" and "description", the response should be: + "tags": + "description": + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with listing table names and folder names for description and for label keep the current name + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. + + """ + elif prompt_type == 'Folder': + return f""" + Generate a detailed metadata description for a database table using following provided data: + folder name: {common_data['label']}, + file names: {common_data['file_names'] if common_data['file_names'] else 'No description provided'} + folder_description: {common_data['description'] if common_data['description'] else 'No description provided'} + folder_tags: {common_data['tags'] if common_data['tags'] else 'No description provided'} + **Important**: + - If the data indicates "No description provided," do not use that particular input for generating metadata. + - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. + - Return the result as a Python dictionary. + Your response should strictly contain the requested metadata types. + For example, if the requested metadata types are "tags" and "description", the response should be: + "tags": + "description": + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + Return a python dictionary, all the keys must be lowercase. Don't use ' ' in your response, use " ". Include file types as pdf, and write file names in description. + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. + """ + + def _invoke_model(self, prompt): + messages = [{'role': 'user', 'content': [{'type': 'text', 'text': prompt}]}] + body = json.dumps( + { + 'anthropic_version': 'bedrock-2023-05-31', + 'max_tokens': 4096, + 'messages': messages, + 'temperature': 0.5, + 'top_p': 0.5, + 'stop_sequences': ['\n\nHuman:'], + 'top_k': 250, + } + ) + #TODO: adjust input depending on model + model_id = config.get_property('modules.s3_datasets.features.generate_metadata_ai.model_id') + response = self._client.invoke_model(body=body, modelId=model_id) + response_body = json.loads(response.get('body').read()) + return response_body.get('content', []) + + def _parse_response(self, response_content, targetName, subitem_ids): + output_str = response_content[0]['text'] + + output_dict = json.loads(output_str) + if not output_dict.get('name'): + output_dict['name'] = targetName + + if output_dict.get('subitem_descriptions'): + subitem_ids = subitem_ids.pop() + subitem_ids = subitem_ids.split(',') + subitem_ids = subitem_ids[: len(output_dict['subitem_descriptions'])] + subitem_descriptions = [] + for index, (key, value) in enumerate(output_dict['subitem_descriptions'].items()): + subitem_descriptions.append({'label': key, 'description': value, 'subitem_id': subitem_ids[index]}) + output_dict['subitem_descriptions'] = subitem_descriptions + return output_dict + + def generate_metadata(self, **kwargs): + # TODO: refactor to use explicit params instead of kwargs + prompt = self._generate_prompt(**kwargs) + response_content = self._invoke_model(prompt) + # TODO: add templated output so that we can avoid parsing the response too much + return self._parse_response(response_content, kwargs.get('label', ' '), kwargs.get('subitem_ids', ' ')) diff --git a/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py b/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py index 94db4d056..778edb508 100644 --- a/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py +++ b/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py @@ -73,3 +73,16 @@ def get_bucket_encryption(self) -> (str, str, str): f'Data.all Environment Pivot Role does not have s3:GetEncryptionConfiguration Permission for {dataset.S3BucketName} bucket: {e}' ) raise Exception(f'Cannot fetch the bucket encryption configuration for {dataset.S3BucketName}: {e}') + + def list_bucket_files(self, bucket_name, prefix): + dataset = self._dataset + try: + response = self._client.list_objects_v2( + Bucket=bucket_name, + Prefix=prefix, + ExpectedBucketOwner=dataset.AwsAccountId, + MaxKeys=1000, + ) + return response.get('Contents', []) + except ClientError as e: + raise Exception(f'Cannot list the bucket files for {dataset.S3BucketName}: {e}') diff --git a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py index c2038084b..17e7eb733 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py @@ -1,5 +1,5 @@ from operator import or_ - +from sqlalchemy import func, and_ from dataall.base.db import paginate from dataall.base.db.exceptions import ObjectNotFound from dataall.modules.s3_datasets.db.dataset_models import DatasetTableColumn @@ -42,3 +42,30 @@ def paginate_active_columns_for_table(session, table_uri: str, filter: dict): ).order_by(DatasetTableColumn.columnType.asc()) return paginate(query=q, page=filter.get('page', 1), page_size=filter.get('pageSize', 10)).to_dict() + + @staticmethod + def get_table_info_metadata_generation(session, table_uri: str): + result = ( + session.query( + DatasetTableColumn.GlueTableName, + DatasetTableColumn.AWSAccountId, + func.array_agg(DatasetTableColumn.description).label('description'), + func.array_agg(DatasetTableColumn.label).label('label'), + func.array_agg(DatasetTableColumn.columnUri).label('columnUri'), + ) + .filter(and_(DatasetTableColumn.tableUri == table_uri)) + .group_by(DatasetTableColumn.GlueTableName, DatasetTableColumn.AWSAccountId) + .first() + ) + return result + + @staticmethod + def query_active_columns_for_table(session, table_uri: str): + return ( + session.query(DatasetTableColumn) + .filter( + DatasetTableColumn.tableUri == table_uri, + DatasetTableColumn.deleted.is_(None), + ) + .order_by(DatasetTableColumn.columnType.asc()) + ) diff --git a/backend/dataall/modules/s3_datasets/db/dataset_models.py b/backend/dataall/modules/s3_datasets/db/dataset_models.py index 3e9291485..819d9065d 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_models.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_models.py @@ -46,7 +46,10 @@ class DatasetStorageLocation(Resource, Base): S3BucketName = Column(String, nullable=False) S3Prefix = Column(String, nullable=False) S3AccessPoint = Column(String, nullable=True) + label = Column(String, nullable=False) region = Column(String, default='eu-west-1') + description = Column(String, nullable=True) + tags = Column(ARRAY(String)) locationCreated = Column(Boolean, default=False) userRoleForStorageLocation = query_expression() projectPermission = query_expression() diff --git a/backend/dataall/modules/s3_datasets/db/dataset_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_repositories.py index 075575a2a..feedf7fb5 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_repositories.py @@ -1,6 +1,7 @@ import logging -from sqlalchemy import and_, or_ +import sqlalchemy +from sqlalchemy import and_, or_, literal from sqlalchemy.orm import Query from dataall.core.activity.db.activity_models import Activity from dataall.core.environment.db.environment_models import Environment @@ -9,7 +10,7 @@ from dataall.base.db.exceptions import ObjectNotFound from dataall.modules.datasets_base.services.datasets_enums import ConfidentialityClassification, Language from dataall.core.environment.services.environment_resource_manager import EnvironmentResource -from dataall.modules.s3_datasets.db.dataset_models import DatasetTable, S3Dataset +from dataall.modules.s3_datasets.db.dataset_models import DatasetTable, S3Dataset, DatasetStorageLocation from dataall.base.utils.naming_convention import ( NamingConventionService, NamingConventionPattern, @@ -278,3 +279,41 @@ def _set_import_data(dataset, data): dataset.importedAdminRole = True if data.get('adminRoleName') else False if data.get('imported'): dataset.KmsAlias = data.get('KmsKeyAlias') if data.get('KmsKeyAlias') else 'SSE-S3' + + @staticmethod + def query_dataset_tables_folders(session, dataset_uri): + q1 = ( + session.query( + S3Dataset.datasetUri, + DatasetTable.tableUri.label('targetUri'), + DatasetTable.name.label('name'), + literal('Table', type_=sqlalchemy.types.String).label('targetType'), + ) + .join( + DatasetTable, + DatasetTable.datasetUri == S3Dataset.datasetUri, + ) + .filter(S3Dataset.datasetUri == dataset_uri) + ) + q2 = ( + session.query( + S3Dataset.datasetUri, + DatasetStorageLocation.locationUri.label('targetUri'), + DatasetStorageLocation.name.label('name'), + literal('Folder', type_=sqlalchemy.types.String).label('targetType'), + ) + .join( + DatasetStorageLocation, + DatasetStorageLocation.datasetUri == S3Dataset.datasetUri, + ) + .filter(S3Dataset.datasetUri == dataset_uri) + ) + return q1.union(q2) + + @staticmethod + def paginated_dataset_tables_folders(session, dataset_uri, data): + return paginate( + query=DatasetRepository.query_dataset_tables_folders(session, dataset_uri), + page=data.get('page', 1), + page_size=data.get('pageSize', 10), + ).to_dict() diff --git a/backend/dataall/modules/s3_datasets/services/dataset_column_service.py b/backend/dataall/modules/s3_datasets/services/dataset_column_service.py index 987b855a4..40987c4c7 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_column_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_column_service.py @@ -70,3 +70,11 @@ def update_table_column_description(column_uri: str, description) -> DatasetTabl Worker.queue(engine=get_context().db_engine, task_ids=[task.taskUri]) return column + + @staticmethod + def batch_update_table_columns_description(columns): + for column_ in columns: + DatasetColumnService.update_table_column_description( + column_uri=column_['subitem_id'], description=column_['description'] + ) + return 'Success' diff --git a/backend/dataall/modules/s3_datasets/services/dataset_enums.py b/backend/dataall/modules/s3_datasets/services/dataset_enums.py new file mode 100644 index 000000000..a62d0fd5b --- /dev/null +++ b/backend/dataall/modules/s3_datasets/services/dataset_enums.py @@ -0,0 +1,18 @@ +from enum import Enum + + +class MetadataGenerationTargets(Enum): + """Describes the s3_datasets metadata generation types""" + + Table = 'Table' + Folder = 'Folder' + S3_Dataset = 'S3_Dataset' + + +class MetadataGenerationTypes(Enum): + """Describes the s3_datasets metadata generation types""" + + Description = 'Description' + Label = 'Label' + Tag = 'Tag' + Topic = 'Topic' diff --git a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py index a4ac2b33f..23827c9c1 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py @@ -1,3 +1,5 @@ +import logging + from dataall.modules.s3_datasets.indexers.dataset_indexer import DatasetIndexer from dataall.base.context import get_context from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService @@ -18,6 +20,11 @@ from dataall.modules.s3_datasets.services.dataset_permissions import DATASET_FOLDER_READ, GET_DATASET_FOLDER from dataall.modules.s3_datasets.db.dataset_repositories import DatasetRepository from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation, S3Dataset +from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient +from dataall.modules.s3_datasets.aws.s3_dataset_client import S3DatasetClient +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets + +log = logging.getLogger(__name__) class DatasetLocationService: @@ -137,3 +144,22 @@ def _delete_dataset_folder_read_permission(session, dataset: S3Dataset, location } for group in permission_group: ResourcePolicyService.delete_resource_policy(session=session, group=group, resource_uri=location_uri) + + @staticmethod + def generate_metadata_for_folder(resourceUri, version, metadataTypes): + context = get_context() + # TODO decide what to do with version + with context.db_engine.scoped_session() as session: + folder = DatasetLocationRepository.get_location_by_uri(session, resourceUri) + dataset = DatasetRepository.get_dataset_by_uri(session, folder.datasetUri) + files = S3DatasetClient(dataset).list_bucket_files(folder.S3BucketName, folder.S3Prefix) + file_names = [f['Key'] for f in files] + log.info('file names', file_names) + return BedrockClient(folder.AWSAccountId, 'us-east-1').generate_metadata( + prompt_type=MetadataGenerationTargets.Folder.value, + label=folder.label, + file_names=file_names, + description=folder.description, + tags=folder.tags, + metadata_type=metadataTypes, + ) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index 14cfdc2fd..e80b8e4ba 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -47,6 +47,8 @@ from dataall.modules.datasets_base.db.dataset_models import DatasetBase from dataall.modules.s3_datasets.services.dataset_permissions import DATASET_TABLE_ALL from dataall.modules.datasets_base.services.dataset_service_interface import DatasetServiceInterface +from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets log = logging.getLogger(__name__) @@ -557,3 +559,29 @@ def delete_dataset_term_links(session, dataset_uri): for table_uri in tables: GlossaryRepository.delete_glossary_terms_links(session, table_uri, 'DatasetTable') GlossaryRepository.delete_glossary_terms_links(session, dataset_uri, 'Dataset') + + @staticmethod + def list_dataset_tables_folders(dataset_uri, filter): + context = get_context() + with context.db_engine.scoped_session() as session: + return DatasetRepository.paginated_dataset_tables_folders(session, dataset_uri, filter) + + @staticmethod + def generate_metadata_for_dataset(resourceUri, version, metadataTypes): + # TODO decide what to do with version + context = get_context() + with context.db_engine.scoped_session() as session: + dataset = DatasetBaseRepository.get_dataset_by_uri(session, resourceUri) + table_labels = [t.label for t in DatasetRepository.get_dataset_tables(session, resourceUri)] + table_descriptions = [t.description for t in DatasetRepository.get_dataset_tables(session, resourceUri)] + folders = [f.label for f in DatasetLocationRepository.get_dataset_folders(session, resourceUri)] + return BedrockClient(dataset.AwsAccountId, 'us-east-1').generate_metadata( + prompt_type=MetadataGenerationTargets.S3_Dataset.value, + label=dataset.label, + tables=table_labels, + description=dataset.description, + table_description=table_descriptions, + tags=dataset.tags, + metadata_type=metadataTypes, + folders=folders, + ) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index 5c3c3228f..d4a7f7878 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -29,6 +29,10 @@ from dataall.modules.s3_datasets.services.dataset_service import DatasetService from dataall.base.utils import json_utils from dataall.base.db import exceptions +from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient +from dataall.modules.s3_datasets.db.dataset_column_repositories import DatasetColumnRepository +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets + log = logging.getLogger(__name__) @@ -183,3 +187,23 @@ def _delete_dataset_table_read_permission(session, table_uri): ResourcePolicyService.delete_resource_policy( session=session, group=None, resource_uri=table_uri, resource_type=DatasetTable.__name__ ) + + # TODO ADD PERMISSIONS! + @staticmethod + def generate_metadata_for_table(resourceUri, version, metadataTypes, sampleData): + # TODO decide what to do with version + context = get_context() + with context.db_engine.scoped_session() as session: + table = DatasetTableRepository.get_dataset_table_by_uri(session, resourceUri) + table_column = DatasetColumnRepository.get_table_info_metadata_generation(session, resourceUri) + return BedrockClient(table_column.AWSAccountId, 'us-east-1').generate_metadata( + prompt_type=MetadataGenerationTargets.Table.value, + label=table.label, + columns={','.join(table_column.label)}, + subitem_descriptions={','.join(table_column.description)}, + subitem_ids={','.join(table_column.columnUri)}, + description=table.description, + tags=table.tags, + metadata_type=metadataTypes, + sample_data=sampleData, + ) diff --git a/config.json b/config.json index 26d0891b4..9c4b80f8f 100644 --- a/config.json +++ b/config.json @@ -58,7 +58,11 @@ "preview_data": true, "glue_crawler": true, "metrics_data": true, - "show_stack_logs": "enabled" + "show_stack_logs": "enabled", + "generate_metadata_ai": { + "active": true, + "model_id": "anthropic.claude-3-sonnet-20240229-v1:0" + } } }, "shares_base": { diff --git a/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js new file mode 100644 index 000000000..f50933bfb --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js @@ -0,0 +1,424 @@ +// import { LoadingButton } from '@mui/lab'; +import { + // Autocomplete, + Avatar, + Box, + Button, + // CardContent, + // CardHeader, + Checkbox, + Chip, + Divider, + FormControl, + FormGroup, + FormControlLabel, + FormLabel, + Grid, + InputLabel, + MenuItem, + Select, + Switch, + // TextField, + Typography +} from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +// import { Formik } from 'formik'; +import { useSnackbar } from 'notistack'; +import PropTypes from 'prop-types'; +// import { useCallback, useEffect, useState } from 'react'; +import { useState } from 'react'; +import AutoModeIcon from '@mui/icons-material/AutoMode'; +// import * as Yup from 'yup'; +// import { ChipInput, Defaults } from 'design'; +import { Defaults, Scrollbar } from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; +import { listDatasetTablesFolders, generateMetadataBedrock } from '../services'; +import { useCallback } from 'react'; + +/* eslint-disable no-console */ +export const GenerateMetadataComponent = (props) => { + const { + dataset, + targetType, + setTargetType, + targets, + setTargets, + targetOptions, + setTargetOptions, + selectedMetadataTypes, + setSelectedMetadataTypes, + currentView, + setCurrentView, + loadingMetadata, + setLoadingMetadata, + version, + setVersion, + ...other + } = props; + const { enqueueSnackbar } = useSnackbar(); + const dispatch = useDispatch(); + + const client = useClient(); + const [loadingTableFolder, setLoadingTableFolder] = useState(false); + const [tableFolderFilter, setTableFolderFilter] = useState(Defaults.filter); + const handleChange = useCallback( + async (event) => { + setTargetType(event.target.value); + if (event.target.value === 'Dataset') { + setTargets([ + { + targetUri: dataset.datasetUri, + targetType: 'S3_Dataset' + } + ]); + } else { + setTargets([]); + setLoadingTableFolder(true); + const response = await client.query( + listDatasetTablesFolders({ + datasetUri: dataset.datasetUri, + filter: tableFolderFilter + }) + ); + if (!response.errors) { + setTargetOptions(response.data.listDatasetTablesFolders); + } else { + dispatch({ + type: SET_ERROR, + error: response.errors[0].message + dataset.datasetUri + }); + } + setLoadingTableFolder(false); + } + }, + [client, dispatch] + ); + + const handleMetadataChange = (event) => { + setSelectedMetadataTypes({ + ...selectedMetadataTypes, + [event.target.name]: event.target.checked + }); + }; + + const handlePageChange = async (page) => { + page += 1; //expecting 1-indexing + if (page <= targetOptions.pages && page !== targetOptions.page) { + await setTableFolderFilter({ ...tableFolderFilter, page: page }); + } + }; + + const generateMetadata = async () => { + setCurrentView('REVIEW_METADATA'); + for (let target of targets) { + let response = await client.mutate( + generateMetadataBedrock({ + resourceUri: target.targetUri, + targetType: target.targetType, + metadataTypes: Object.entries(selectedMetadataTypes) + .filter(([key, value]) => value === true) + .map(([key]) => key), + version: version, + sampleData: {} + }) + ); + if (!response.errors) { + target.description = response.data.generateMetadata.description; + target.label = response.data.generateMetadata.label; + target.name = response.data.generateMetadata.name; + target.tags = response.data.generateMetadata.tags; + target.topics = response.data.generateMetadata.topics; + target.subitem_descriptions = ( + response.data.generateMetadata.subitem_descriptions || [] + ).map((item) => ({ + description: item.description, + label: item.label, + subitem_id: item.subitem_id + })); + const hasNotEnoughData = [ + target.description, + target.label, + target.name, + target.tags, + target.topics, + target.subitem_descriptions + ].some((value) => value === 'NotEnoughData'); + + if (hasNotEnoughData) { + enqueueSnackbar( + `Not enough data to generate metadata for ${target.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'warning' + } + ); + } else { + enqueueSnackbar( + `Metadata generation is successful for ${target.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + } + ); + } + setVersion(version + 1); + } + } + }; + return ( + <> + + + 1} + label="Select target type" + color="primary" + variant="outlined" + /> + + + {targetType && ( + 2} + label="Select target resources" + color="primary" + variant="outlined" + /> + )} + + + {targetType && !!targets.length && ( + 3} + label="Select type of metadata" + color="primary" + variant="outlined" + /> + )} + + + + + + + Target Type * + + + {targetType === 'Dataset' && ( + + Data.all will use the table and folder metadata to generate + Dataset label, description, tags and/or topics using Amazon + Bedrock. + + )} + {targetType === 'TablesAndFolders' && ( + + Data.all will use table column names and table descriptions and + folder S3 prefix names to generate Tables and Folders label, + description, tags and/or topics using Amazon Bedrock. + + )} + + + {targetType === 'Dataset' && ( + } + label={dataset.name} + /> + )} + {targetType === 'TablesAndFolders' && ( + + + node.targetUri} + rows={targetOptions.nodes} + columns={[ + { field: 'targetUri', hide: true }, + { + field: 'name', + headerName: 'Name', + flex: 1.5, + editable: false + }, + { + field: 'targetType', + headerName: 'Type', + flex: 1, + editable: false + } + ]} + rowCount={targetOptions.count} + page={targetOptions.page - 1} + pageSize={targetOptions.pageSize} + paginationMode="server" + onPageChange={handlePageChange} + loading={loadingTableFolder} + onPageSizeChange={(pageSize) => { + setTableFolderFilter({ + ...tableFolderFilter, + pageSize: pageSize + }); + }} + getRowHeight={() => 'auto'} + disableSelectionOnClick + onSelectionModelChange={(newSelectionModel) => { + const selectedTargets = newSelectionModel.map((id) => + targetOptions.nodes.find( + (option) => option.targetUri === id + ) + ); + setTargets(selectedTargets); + if (newSelectionModel.length === 0) { + setSelectedMetadataTypes({}); + } + }} + sx={{ + wordWrap: 'break-word', + '& .MuiDataGrid-row': { + borderBottom: '1px solid rgba(145, 158, 171, 0.24)' + }, + '& .MuiDataGrid-columnHeaders': { + borderBottom: 0.5 + } + }} + /> + + + )} + + + {targetType && !!targets.length && ( + + Metadata + + } + label="Label" + /> + + } + label="Description" + /> + + } + label="Tags" + /> + + } + label="Subitem Descriptions" + /> + + } + label="Topics" + /> + + )} + + + {!loadingMetadata && ( + + )} + + ); +}; + +GenerateMetadataComponent.propTypes = { + dataset: PropTypes.object.isRequired, + targetType: PropTypes.string.isRequired, + setTargetType: PropTypes.func.isRequired, + targets: PropTypes.array.isRequired, + setTargets: PropTypes.func.isRequired, + targetOptions: PropTypes.array.isRequired, + setTargetOptions: PropTypes.func.isRequired, + selectedMetadataTypes: PropTypes.object.isRequired, + setSelectedMetadataTypes: PropTypes.func.isRequired, + currentView: PropTypes.string.isRequired, + setCurrentView: PropTypes.func.isRequired, + version: PropTypes.number.isRequired, + setVersion: PropTypes.func.isRequired +}; diff --git a/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js new file mode 100644 index 000000000..efde24871 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js @@ -0,0 +1,77 @@ +import { Dialog } from '@mui/material'; +import PropTypes from 'prop-types'; +import { useEffect, useState } from 'react'; +import { Defaults } from 'design'; +import { GenerateMetadataComponent } from './GenerateMetadataComponent'; +import { ReviewMetadataComponent } from './ReviewMetadataComponent'; + +export const MetadataMainModal = (props) => { + const { dataset, onApply, onClose, open, ...other } = props; + const [currentView, setCurrentView] = useState('GENERATE_FORM'); + const [targetType, setTargetType] = useState(''); + const [targets, setTargets] = useState([]); + const [targetOptions, setTargetOptions] = useState([]); + const [version, setVersion] = useState(0); + const [selectedMetadataTypes, setSelectedMetadataTypes] = useState({ + label: false, + description: false, + tags: false, + topics: false, + subitem_descriptions: false + }); + + useEffect(() => { + if (!open) { + setCurrentView('GENERATE_FORM'); + setTargetType(''); + setTargets([]); + setTargetOptions(Defaults.pagedResponse); + setSelectedMetadataTypes({}); + setVersion(0); + } + }, [open]); + + if (!dataset) { + return null; + } + + return ( + + {currentView === 'GENERATE_FORM' && ( + + )} + {currentView === 'REVIEW_METADATA' && ( + + )} + + ); +}; + +MetadataMainModal.propTypes = { + dataset: PropTypes.object.isRequired, + onApply: PropTypes.func, + onClose: PropTypes.func, + open: PropTypes.bool.isRequired +}; diff --git a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js new file mode 100644 index 000000000..46488d4b2 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js @@ -0,0 +1,494 @@ +import { + Button, + Box, + Chip, + Typography, + CircularProgress +} from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +import { useSnackbar } from 'notistack'; +import PropTypes from 'prop-types'; +import AutoModeIcon from '@mui/icons-material/AutoMode'; +import { Scrollbar } from 'design'; +import { SET_ERROR, useDispatch } from 'globalErrors'; +import { useClient } from 'services'; +import { updateDataset, generateMetadataBedrock } from '../services'; +import { updateDatasetTable } from 'modules/Tables/services'; +import { BatchUpdateDatasetTableColumn } from '../services/batchUpdateTableColumnDescriptions'; +import { listSampleData } from '../services/listSampleData'; +import { updateDatasetStorageLocation } from 'modules/Folders/services'; +import SampleDataPopup from './SampleDataPopup'; +import React, { useState } from 'react'; +import SubitemDescriptionsGrid from './SubitemDescriptionsGrid'; + +export const ReviewMetadataComponent = (props) => { + const { + dataset, + targets, + setTargets, + selectedMetadataTypes, + version + } = props; + const { enqueueSnackbar } = useSnackbar(); + const dispatch = useDispatch(); + const client = useClient(); + const [popupOpen, setPopupOpen] = useState(false); + const [sampleData, setSampleData] = useState(null); + const [targetUri, setTargetUri] = useState(null); + const [showPopup, setShowPopup] = React.useState(false); + const [subitemDescriptions, setSubitemDescriptions] = React.useState([]); + + const showSubItemsPopup = (subitemDescriptions) => { + setSubitemDescriptions(subitemDescriptions); + setShowPopup(true); + }; + + const closeSubItemsPopup = () => { + setShowPopup(false); + }; + const openSampleDataPopup = (data) => { + setSampleData(data); + setPopupOpen(true); + }; + + const closeSampleDataPopup = () => { + setPopupOpen(false); + setSampleData(null); + }; + async function handleSaveSubitemDescriptions() { + try { + const columns_ = subitemDescriptions.map((item) => ({ + description: item.description, + label: item.label, + subitem_id: item.subitem_id + })); + const response = await client.mutate( + BatchUpdateDatasetTableColumn(columns_) + ); + if (!response.errors) { + enqueueSnackbar('Successfully updated subitem descriptions', { + variant: 'success' + }); + closeSubItemsPopup(); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + dispatch({ type: SET_ERROR, error: err.message }); + } + } + + async function handleRegenerate(table) { + try { + const response = await client.query( + listSampleData({ + tableUri: table.targetUri + }) + ); + openSampleDataPopup(response.data.listSampleData); + setTargetUri(table.targetUri); + if (!response.errors) { + enqueueSnackbar('Successfully read sample data', { + variant: 'success' + }); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + dispatch({ type: SET_ERROR, error: err.message }); + } + } + const handleAcceptAndRegenerate = async () => { + // Perform any necessary actions for accepting and regenerating the data + try { + const targetIndex = targets.findIndex((t) => t.targetUri === targetUri); + if (targetIndex !== -1) { + const { __typename, ...sampleDataWithoutTypename } = sampleData; + const response = await client.mutate( + generateMetadataBedrock({ + resourceUri: targets[targetIndex].targetUri, + targetType: targets[targetIndex].targetType, + metadataTypes: Object.entries(selectedMetadataTypes) + .filter(([key, value]) => value === true) + .map(([key]) => key), + version: version, + sampleData: sampleDataWithoutTypename + }) + ); + + if (!response.errors) { + const updatedTarget = { + ...targets[targetIndex], + description: response.data.generateMetadata.description, + label: response.data.generateMetadata.label, + name: response.data.generateMetadata.name, + tags: response.data.generateMetadata.tags, + topics: response.data.generateMetadata.topics, + subitem_descriptions: + response.data.generateMetadata.subitem_descriptions + }; + + const updatedTargets = [...targets]; + updatedTargets[targetIndex] = updatedTarget; + + setTargets(updatedTargets); + + enqueueSnackbar( + `Metadata generation is successful for ${updatedTarget.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + } + ); + } + } else { + console.error(`Target with targetUri not found`); + enqueueSnackbar(`Metadata generation is unsuccessful`, { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'error' + }); + } + + closeSampleDataPopup(); + } catch (err) { + dispatch({ type: SET_ERROR, error: err.message }); + } + }; + + async function saveMetadata(targets) { + try { + const updatedTargets = targets.map(async (target) => { + const updatedMetadata = {}; + + // Loop through selectedMetadataTypes and add the corresponding key-value pairs to updatedMetadata + Object.entries(selectedMetadataTypes).forEach( + ([metadataType, checked]) => { + if (checked) { + updatedMetadata[metadataType] = target[metadataType]; + } + } + ); + if (target.targetType === 'S3_Dataset') { + updatedMetadata.KmsAlias = dataset.KmsAlias; + const response = await client.mutate( + updateDataset({ + datasetUri: target.targetUri, + input: updatedMetadata + }) + ); + + if (!response.errors) { + return { ...target, success: true }; + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + return { ...target, success: false }; + } + } else if (target.targetType === 'Table') { + const response = await client.mutate( + updateDatasetTable({ + tableUri: target.targetUri, + input: updatedMetadata + }) + ); + + if (!response.errors) { + return { ...target, success: true }; + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + return { ...target, success: false }; + } + } else if (target.targetType === 'Folder') { + const response = await client.mutate( + updateDatasetStorageLocation({ + locationUri: target.targetUri, + input: updatedMetadata + }) + ); + + if (!response.errors) { + return { ...target, success: true }; // Return the updated target with success flag + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + return { ...target, success: false }; // Return the target with success flag set to false + } + } + }); + + const updatedTargetsResolved = await Promise.all(updatedTargets); + + const successfulTargets = updatedTargetsResolved.filter( + (target) => target.success + ); + const failedTargets = updatedTargetsResolved.filter( + (target) => !target.success + ); + + if (successfulTargets.length > 0) { + enqueueSnackbar( + `${successfulTargets.length} target(s) updated successfully`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + } + ); + } + + if (failedTargets.length > 0) { + enqueueSnackbar(`${failedTargets.length} target(s) failed to update`, { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'error' + }); + } + } catch (err) { + console.error(err); + dispatch({ type: SET_ERROR, error: err.message }); + } + } + return ( + <> + {Array.isArray(targets) && targets.length > 0 ? ( + + + + node.targetUri} + rowHeight={80} + columns={[ + { field: 'targetUri', hide: true }, + { + field: 'name', + headerName: 'Name', + flex: 1.5, + editable: false + }, + { + field: 'targetType', + headerName: 'Target Type', + flex: 1.5, + editable: false + }, + { + field: 'label', + headerName: 'Label', + flex: 2, + editable: true, + renderCell: (params) => + params.value === undefined ? ( + + ) : params.value === 'NotEnoughData' ? ( + + ) : ( +
+ {params.value} +
+ ) + }, + { + field: 'description', + headerName: 'Description', + flex: 3, + editable: true, + renderCell: (params) => + params.value === undefined ? ( + + ) : params.value === 'NotEnoughData' ? ( + + ) : ( +
+ {params.value} +
+ ) + }, + { + field: 'tags', + headerName: 'Tags', + flex: 2, + editable: true, + valueSetter: (params) => { + const { id, row, newValue } = params; + const tags = + typeof newValue === 'string' + ? newValue.split(',') + : newValue; + return { ...row, targetUri: id, tags }; + }, + renderCell: (params) => + params.value === undefined ? ( + + ) : params.value[0] === 'NotEnoughData' ? ( + + ) : ( +
+ {Array.isArray(params.value) + ? params.value.join(', ') + : params.value} +
+ ) + }, + { + field: 'topics', + headerName: 'Topics', + flex: 2, + editable: true, + renderCell: (params) => + params.value === undefined ? ( + + ) : params.value[0] === 'NotEnoughData' ? ( + + ) : ( +
+ {params.value} +
+ ) + }, + { + field: 'subitem_descriptions', + headerName: 'Subitem Descriptions', + flex: 3, + editable: false, + renderCell: (params) => + params.value === undefined ? ( + + ) : params.value[0] === 'NotEnoughData' ? ( + + ) : ( + + ) + }, + { + field: 'regenerate', + headerName: 'Regenerate', + flex: 3, + type: 'boolean', + renderCell: (params) => + params.row.targetType === 'Table' ? ( + + ) : ( + '-' + ) + } + ]} + columnVisibilityModel={{ + targetUri: false, + label: selectedMetadataTypes['label'] + ? selectedMetadataTypes['label'] + : false, + description: selectedMetadataTypes['description'] + ? selectedMetadataTypes['description'] + : false, + tags: selectedMetadataTypes['tags'] + ? selectedMetadataTypes['tags'] + : false, + topics: selectedMetadataTypes['topics'] + ? selectedMetadataTypes['topics'] + : false, + subitem_descriptions: selectedMetadataTypes[ + 'subitem_descriptions' + ] + ? selectedMetadataTypes['subitem_descriptions'] + : false + }} + pageSize={10} + rowsPerPageOptions={[5, 10, 20]} + pagination + disableSelectionOnClick + onCellEditCommit={(params) => { + const { value, id, field } = params; + const updatedTargets = targets.map((target) => { + const newTarget = { ...target }; + if (newTarget.targetUri === id) { + newTarget[field] = value; + } + return newTarget; + }); + setTargets(updatedTargets); + }} + onProcessRowUpdateError={(error) => { + console.error('Error updating row:', error); + }} + sx={{ + wordWrap: 'break-word', + '& .MuiDataGrid-row': { + borderBottom: '1px solid rgba(145, 158, 171, 0.24)' + }, + '& .MuiDataGrid-columnHeaders': { + borderBottom: 0.5 + } + }} + /> +
+
+
+ ) : ( + No metadata available + )} + {showPopup && ( + + )} + + + + ); +}; + +ReviewMetadataComponent.propTypes = { + dataset: PropTypes.object.isRequired, + targetType: PropTypes.string.isRequired, + targets: PropTypes.array.isRequired, + setTargets: PropTypes.func.isRequired, + selectedMetadataTypes: PropTypes.object.isRequired, + version: PropTypes.number.isRequired, + setVersion: PropTypes.func.isRequired +}; diff --git a/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js b/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js new file mode 100644 index 000000000..a271db9c7 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { Modal, Box, Button, Typography } from '@mui/material'; +import SampleDataTableComponent from './SampleDataTableComponent'; + +const SampleDataPopup = ({ + open, + sampleData, + handleClose, + handleRegenerate +}) => { + return ( + + + + + By clicking the button below, you agree to share this sample data with + a third-party language model. + + + {' '} + + + + ); +}; + +export default SampleDataPopup; diff --git a/frontend/src/modules/S3_Datasets/components/SampleDataTableComponent.js b/frontend/src/modules/S3_Datasets/components/SampleDataTableComponent.js new file mode 100644 index 000000000..a15d3b32b --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/SampleDataTableComponent.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { styled } from '@mui/styles'; +import { Card } from '@mui/material'; + +const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ + '& .MuiDataGrid-columnsContainer': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(29,29,29,0.33)' + : 'rgba(255,255,255,0.38)' + } +})); +const buildHeader = (fields) => + fields.map((field) => ({ + field: JSON.parse(field).name, + headerName: JSON.parse(field).name, + editable: false + })); +const buildRows = (rows, fields) => { + const header = fields.map((field) => JSON.parse(field).name); + const newRows = rows.map((row) => JSON.parse(row)); + const builtRows = newRows.map((row) => + header.map((h, index) => ({ [h]: row[index] })) + ); + const objects = []; + builtRows.forEach((row) => { + const obj = {}; + row.forEach((r) => { + Object.entries(r).forEach(([key, value]) => { + obj[key] = value; + }); + obj.id = Math.random(); + }); + objects.push(obj); + }); + return objects; +}; +const SampleDataTableComponent = ({ data }) => { + return ( + + + + ); +}; + +export default SampleDataTableComponent; diff --git a/frontend/src/modules/S3_Datasets/components/SubitemDescriptionsGrid.js b/frontend/src/modules/S3_Datasets/components/SubitemDescriptionsGrid.js new file mode 100644 index 000000000..4d5fe3613 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/components/SubitemDescriptionsGrid.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { Grid, Typography, Paper, Button } from '@mui/material'; + +const SubitemDescriptionsGrid = ({ subitemDescriptions, onClose, onSave }) => { + return ( +
+ + + + + + + Label + + + + + Description + + + + + {subitemDescriptions.map((item) => ( + + + + + {item.label} + + + + + {item.description} + + + + + ))} + + + + + + + + +
+ ); +}; + +export default SubitemDescriptionsGrid; diff --git a/frontend/src/modules/S3_Datasets/components/index.js b/frontend/src/modules/S3_Datasets/components/index.js index 7e899832c..2fda5268c 100644 --- a/frontend/src/modules/S3_Datasets/components/index.js +++ b/frontend/src/modules/S3_Datasets/components/index.js @@ -6,3 +6,4 @@ export * from './DatasetOverview'; export * from './DatasetStartCrawlerModal'; export * from './DatasetTables'; export * from './DatasetUpload'; +export * from './MetadataMainModal'; diff --git a/frontend/src/modules/S3_Datasets/services/batchUpdateTableColumnDescriptions.js b/frontend/src/modules/S3_Datasets/services/batchUpdateTableColumnDescriptions.js new file mode 100644 index 000000000..e09d883c9 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/services/batchUpdateTableColumnDescriptions.js @@ -0,0 +1,14 @@ +import { gql } from 'apollo-boost'; + +export const BatchUpdateDatasetTableColumn = (columns) => ({ + variables: { + columns + }, + mutation: gql` + mutation BatchUpdateDatasetTableColumn( + $columns: [SubitemDescriptionInput] + ) { + batchUpdateDatasetTableColumn(columns: $columns) + } + ` +}); diff --git a/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js b/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js new file mode 100644 index 000000000..15176ce08 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js @@ -0,0 +1,46 @@ +import { gql } from 'apollo-boost'; + +export const generateMetadataBedrock = ({ + resourceUri, + targetType, + metadataTypes, + version, + sampleData +}) => ({ + variables: { + resourceUri, + targetType, + metadataTypes, + version, + sampleData + }, + mutation: gql` + mutation generateMetadata( + $resourceUri: String! + $targetType: MetadataGenerationTargets + $metadataTypes: [String] + $version: Int + $sampleData: SampleDataInput + ) { + generateMetadata( + resourceUri: $resourceUri + targetType: $targetType + metadataTypes: $metadataTypes + version: $version + sampleData: $sampleData + ) { + type + label + description + tags + topics + name + subitem_descriptions { + description + label + subitem_id + } + } + } + ` +}); diff --git a/frontend/src/modules/S3_Datasets/services/index.js b/frontend/src/modules/S3_Datasets/services/index.js index 53a75912e..a32c84061 100644 --- a/frontend/src/modules/S3_Datasets/services/index.js +++ b/frontend/src/modules/S3_Datasets/services/index.js @@ -1,9 +1,11 @@ export * from './createDataset'; export * from './deleteDataset'; export * from './generateDatasetAccessToken'; +export * from './generateMetadataBedrock'; export * from './getDatasetPresignedUrl'; export * from './importDataset'; export * from './listDatasetStorageLocations'; +export * from './listDatasetTablesFolders'; export * from './startGlueCrawler'; export * from './syncTables'; export * from './updateDataset'; diff --git a/frontend/src/modules/S3_Datasets/services/listDatasetTablesFolders.js b/frontend/src/modules/S3_Datasets/services/listDatasetTablesFolders.js new file mode 100644 index 000000000..4acc23017 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/services/listDatasetTablesFolders.js @@ -0,0 +1,27 @@ +import { gql } from 'apollo-boost'; + +export const listDatasetTablesFolders = ({ datasetUri, filter }) => ({ + variables: { + datasetUri, + filter + }, + query: gql` + query listDatasetTablesFolders( + $datasetUri: String! + $filter: DatasetFilter + ) { + listDatasetTablesFolders(datasetUri: $datasetUri, filter: $filter) { + count + page + pages + hasNext + hasPrevious + nodes { + name + targetType + targetUri + } + } + } + ` +}); diff --git a/frontend/src/modules/S3_Datasets/services/listSampleData.js b/frontend/src/modules/S3_Datasets/services/listSampleData.js new file mode 100644 index 000000000..b9863fa2a --- /dev/null +++ b/frontend/src/modules/S3_Datasets/services/listSampleData.js @@ -0,0 +1,15 @@ +import { gql } from 'apollo-boost'; + +export const listSampleData = ({ tableUri }) => ({ + variables: { + tableUri + }, + query: gql` + query listSampleData($tableUri: String!) { + listSampleData(tableUri: $tableUri) { + fields + rows + } + } + ` +}); diff --git a/frontend/src/modules/S3_Datasets/views/DatasetView.js b/frontend/src/modules/S3_Datasets/views/DatasetView.js index 6f1800a64..c9ea04eda 100644 --- a/frontend/src/modules/S3_Datasets/views/DatasetView.js +++ b/frontend/src/modules/S3_Datasets/views/DatasetView.js @@ -7,6 +7,7 @@ import { Upload, ViewArrayOutlined } from '@mui/icons-material'; +import AutoModeIcon from '@mui/icons-material/AutoMode'; import { Box, Breadcrumbs, @@ -42,7 +43,8 @@ import { DatasetAWSActions, DatasetData, DatasetOverview, - DatasetUpload + DatasetUpload, + MetadataMainModal } from '../components'; import { isFeatureEnabled, isModuleEnabled, ModuleNames } from 'utils'; import { RequestAccessModal } from 'modules/Catalog/components'; @@ -127,6 +129,7 @@ const DatasetView = () => { const [isRequestAccessOpen, setIsRequestAccessOpen] = useState(false); const [isOpeningModal, setIsOpeningModal] = useState(false); + const [isMetadataModalOpen, setIsMetadataModalOpen] = useState(false); const handleRequestAccessModalOpen = () => { setIsOpeningModal(true); setIsRequestAccessOpen(true); @@ -135,6 +138,15 @@ const DatasetView = () => { const handleRequestAccessModalClose = () => { setIsRequestAccessOpen(false); }; + + const handleMetadataModalOpen = () => { + setIsMetadataModalOpen(true); + }; + + const handleMetadataModalClose = () => { + setIsMetadataModalOpen(false); + }; + const reloadVotes = async () => { const response = await client.query(countUpVotes(params.uri, 'dataset')); if (!response.errors && response.data.countUpVotes !== null) { @@ -266,6 +278,28 @@ const DatasetView = () => { + {isFeatureEnabled('s3_datasets', 'generate_metadata_ai') && ( + + )} + + {isFeatureEnabled('s3_datasets', 'generate_metadata_ai') && ( + + )} + {isAdmin && ( Date: Mon, 7 Oct 2024 13:35:58 +0200 Subject: [PATCH 02/22] Refactor metadata generation to use prompt templates and output parser --- .../aws/bedrock_metadata_client.py | 165 ++++-------------- .../services/dataset_location_service.py | 2 +- .../services/dataset_metadata_service.py | 147 ++++++++++++++++ .../s3_datasets/services/dataset_service.py | 26 +-- .../services/dataset_table_service.py | 2 +- .../components/GenerateMetadataComponent.js | 39 ++--- .../components/MetadataMainModal.js | 2 +- .../components/ReviewMetadataComponent.js | 33 ++-- .../src/modules/S3_Datasets/services/index.js | 2 + 9 files changed, 224 insertions(+), 194 deletions(-) create mode 100644 backend/dataall/modules/s3_datasets/services/dataset_metadata_service.py diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py index c5940e5b4..7f5fa52aa 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py @@ -1,145 +1,40 @@ import logging -import json +import os from dataall.base.config import config from dataall.base.aws.sts import SessionHelper - +from langchain_aws import BedrockLLM +from langchain.prompts import PromptTemplate +from langchain_core.pydantic_v1 import BaseModel, Field +from langchain_core.output_parsers import JsonOutputParser log = logging.getLogger(__name__) -# TODO: refactoring with prompt templates +# TODO: refactoring with prompt templates +# TODO session for infra account - should we use a dedicated role? class BedrockClient: - def __init__(self, account_id: str, region: str): - session = SessionHelper.remote_session(accountid=account_id, region=region) - self._client = session.client('bedrock-runtime', region_name=region) - - def _generate_prompt(self, **kwargs): - prompt_type = kwargs.get('prompt_type', 'Table') - common_data = { - 'label': kwargs.get('label', ''), - 'description': kwargs.get('description', ''), - 'tags': kwargs.get('tags', ''), - 'columns': kwargs.get('columns', []), - 'subitem_descriptions': kwargs.get('subitem_descriptions', []), - 'file_names': kwargs.get('file_names', []), - 'folder_name': kwargs.get('folder_name', ''), - 'folder_description': kwargs.get('folder_description', ''), - 'folder_tags': kwargs.get('folder_tags', ''), - 'tables': kwargs.get('tables', []), - 'table_description': kwargs.get('table_descriptions', ''), - 'metadata_types': kwargs.get('metadata_type', []), - 'folders': kwargs.get('folders', []), - 'sample_data': kwargs.get('sample_data', []), + def __init__(self): + session = SessionHelper.get_session() + self._client = session.client('bedrock-runtime', region_name=os.getenv('AWS_REGION', 'eu-west-1')) + self._model_id = config.get_property('modules.s3_datasets.features.generate_metadata_ai.model_id') + ## TODO experiment with optimal params + # { + # 'anthropic_version': 'bedrock-2023-05-31', + # 'max_tokens': 4096, + # 'messages': messages, + # 'temperature': 0.5, + # 'top_p': 0.5, + # 'stop_sequences': ['\n\nHuman:'], + # 'top_k': 250, + # } + model_kwargs = { + 'anthropic_version': 'bedrock-2023-05-31', + 'max_tokens': 4096, + 'temperature': 0, } - if prompt_type == 'Table': - return f""" - Generate or improve metadata for a common_data['label'] table using the following provided data: - - Table name: {common_data['label'] if common_data['label'] else 'No description provided'} - - Column names: {common_data['columns'] if common_data['columns'] else 'No description provided'} - - Table description: {common_data['description'] if common_data['description'] else 'No description provided'} - - Tags: {common_data['tags'] if common_data['tags'] else 'No description provided'} - - Subitem Descriptions: {common_data['subitem_descriptions'] if common_data['subitem_descriptions'] else 'No description provided'} - - (Only Input) Sample data: {common_data['sample_data'] if common_data['sample_data'] else 'No sample data'} - **Important**: - - If the data indicates "No description provided," do not use that particular input for generating metadata, these data is optional you should still generate in that case. - - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. - - Sample data is only input for you to understand the table better, do not generate sample data. - Your response must strictly contain all the requested metadata types, do not include any of the metadata types if it is not specified by the user. Don't use ' ' in your response, use " ". - Subitem Descriptions corresponds to column descriptions. If the user specifically didn't ask for subitem descriptions, do not include it in the response. - subitem_descriptions is another dictionary within the existing dictionary, rest of them are strings, never change order of columns when you generate description for them. - For example, if the requested metadata types are "Tags" and "Subitem Descriptions", the response should be: - tags: - subitem_descriptions: - : - : - ,..., - : - Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. - Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. - For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. - - """ - - elif prompt_type == 'S3_Dataset': - return f""" - Generate or improve metadata for a dataset using the following provided data: - - Dataset name: {common_data['label'] if common_data['label'] else 'No description provided'} - - Table names in the dataset: {common_data['tables'] if common_data['tables'] else 'No description provided'} - - Folder names in the dataset: {common_data['folders'] if common_data['folders'] else 'No description provided'} - - Current tags for dataset: {common_data['tags'] if common_data['tags'] else 'No description provided'} - - Current dataset description: {common_data['description'] if common_data['description'] else 'No description provided'} - **Important**: - - If the data indicates "No description provided," do not use that particular input for generating metadata. - - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. - - Return the result as a Python dictionary. - Your response should strictly contain the requested metadata types. Don't use ' ' in your response, use " ". - For example, if the requested metadata types are "tags" and "description", the response should be: - "tags": - "description": - Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with listing table names and folder names for description and for label keep the current name - For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. - Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. - - """ - elif prompt_type == 'Folder': - return f""" - Generate a detailed metadata description for a database table using following provided data: - folder name: {common_data['label']}, - file names: {common_data['file_names'] if common_data['file_names'] else 'No description provided'} - folder_description: {common_data['description'] if common_data['description'] else 'No description provided'} - folder_tags: {common_data['tags'] if common_data['tags'] else 'No description provided'} - **Important**: - - If the data indicates "No description provided," do not use that particular input for generating metadata. - - Only focus on generating the following metadata types as specified by the user: {common_data['metadata_types']}. Do not include any other metadata types. - - Return the result as a Python dictionary. - Your response should strictly contain the requested metadata types. - For example, if the requested metadata types are "tags" and "description", the response should be: - "tags": - "description": - For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. - Return a python dictionary, all the keys must be lowercase. Don't use ' ' in your response, use " ". Include file types as pdf, and write file names in description. - Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. - """ - - def _invoke_model(self, prompt): - messages = [{'role': 'user', 'content': [{'type': 'text', 'text': prompt}]}] - body = json.dumps( - { - 'anthropic_version': 'bedrock-2023-05-31', - 'max_tokens': 4096, - 'messages': messages, - 'temperature': 0.5, - 'top_p': 0.5, - 'stop_sequences': ['\n\nHuman:'], - 'top_k': 250, - } - ) - #TODO: adjust input depending on model - model_id = config.get_property('modules.s3_datasets.features.generate_metadata_ai.model_id') - response = self._client.invoke_model(body=body, modelId=model_id) - response_body = json.loads(response.get('body').read()) - return response_body.get('content', []) - - def _parse_response(self, response_content, targetName, subitem_ids): - output_str = response_content[0]['text'] - - output_dict = json.loads(output_str) - if not output_dict.get('name'): - output_dict['name'] = targetName - - if output_dict.get('subitem_descriptions'): - subitem_ids = subitem_ids.pop() - subitem_ids = subitem_ids.split(',') - subitem_ids = subitem_ids[: len(output_dict['subitem_descriptions'])] - subitem_descriptions = [] - for index, (key, value) in enumerate(output_dict['subitem_descriptions'].items()): - subitem_descriptions.append({'label': key, 'description': value, 'subitem_id': subitem_ids[index]}) - output_dict['subitem_descriptions'] = subitem_descriptions - return output_dict + self._llm = BedrockLLM(model_id=self._model_id, client=self._client, model_kwargs=model_kwargs) - def generate_metadata(self, **kwargs): - # TODO: refactor to use explicit params instead of kwargs - prompt = self._generate_prompt(**kwargs) - response_content = self._invoke_model(prompt) - # TODO: add templated output so that we can avoid parsing the response too much - return self._parse_response(response_content, kwargs.get('label', ' '), kwargs.get('subitem_ids', ' ')) + def invoke_model(self, prompt_template: PromptTemplate, prompt: str, output_object: BaseModel): + parser = JsonOutputParser(pydantic_object=output_object) + chain = prompt_template | self._llm | parser + return chain.invoke(prompt) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py index 23827c9c1..45c85b0ca 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py @@ -155,7 +155,7 @@ def generate_metadata_for_folder(resourceUri, version, metadataTypes): files = S3DatasetClient(dataset).list_bucket_files(folder.S3BucketName, folder.S3Prefix) file_names = [f['Key'] for f in files] log.info('file names', file_names) - return BedrockClient(folder.AWSAccountId, 'us-east-1').generate_metadata( + return BedrockClient().generate_metadata( prompt_type=MetadataGenerationTargets.Folder.value, label=folder.label, file_names=file_names, diff --git a/backend/dataall/modules/s3_datasets/services/dataset_metadata_service.py b/backend/dataall/modules/s3_datasets/services/dataset_metadata_service.py new file mode 100644 index 000000000..b38042735 --- /dev/null +++ b/backend/dataall/modules/s3_datasets/services/dataset_metadata_service.py @@ -0,0 +1,147 @@ +from typing import List, Optional +from langchain.prompts import PromptTemplate +from langchain_core.pydantic_v1 import BaseModel + + +class MetadataOutput(BaseModel): + tags: Optional[List[str]] = None + description: Optional[str] = None + label: Optional[str] = None + topics: Optional[List[str]] = None + + +class DatasetMetadataGenerationService: + @staticmethod + def get_dataset_prompt_template() -> PromptTemplate: + prompt_template = """ + Generate or improve metadata for a dataset using the following provided data: + - Dataset name: {label} + - Current dataset description: {description} + - Current tags for dataset: {tags} + - Table names in the dataset: {table_names} + - Folder names in the dataset: {folder_names} + **Important**: + - If the data indicates "No description provided" or is None or [] do not use that particular input for generating metadata. + - Only focus on generating the following metadata types as specified by the user: {metadata_types}. Do not include any other metadata types. + - Return the result as a Python dictionary. + Your response should strictly contain the requested metadata types. Don't use ' ' in your response, use " ". + For example, if the requested metadata types are "tags" and "description", the response should be: + "tags": + "description": + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with listing table names and folder names for description and for label keep the current name + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. + + """ + return PromptTemplate.from_template(prompt_template) + + @staticmethod + def format_dataset_prompt_template( + template: PromptTemplate, + metadata_types: List[str], + label: str, + description: str, + tags: List[str], + table_labels: List[str], + table_descriptions: List[str], + folder_labels: List[str], + ) -> str: + # TODO: add table description + # TODO add input validation + return template.format( + label=label, + description=description, + tags=tags, + table_names=table_labels, + folder_names=folder_labels, + metadata_types=metadata_types, + ) + + @staticmethod + def get_table_prompt_template() -> PromptTemplate: + prompt_template = """ + Generate or improve metadata for a common_data['label'] table using the following provided data: + - Table name: {label} + - Current table description: {description} + - Current tags for table: {tags} + - Column names: {columns} + - Column Descriptions: {column_descriptions} + - (Only Input) Sample data: {sample_data} + **Important**: + - If the data indicates "No description provided," do not use that particular input for generating metadata, these data is optional you should still generate in that case. + - Only focus on generating the following metadata types as specified by the user: {metadata_types}. Do not include any other metadata types. + - Sample data is only input for you to understand the table better, do not generate sample data. + Your response must strictly contain all the requested metadata types, do not include any of the metadata types if it is not specified by the user. Don't use ' ' in your response, use " ". + Subitem Descriptions corresponds to column descriptions. If the user specifically didn't ask for subitem descriptions, do not include it in the response. + subitem_descriptions is another dictionary within the existing dictionary, rest of them are strings, never change order of columns when you generate description for them. + For example, if the requested metadata types are "Tags" and "Subitem Descriptions", the response should be: + tags: + subitem_descriptions: + : + : + ,..., + : + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. + Return the result as a Python dictionary where the keys are the requested metadata types, all the keys must be lowercase and the values are the corresponding generated metadata. + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + + """ + return PromptTemplate.from_template(prompt_template) + + @staticmethod + def format_table_prompt_template( + template: PromptTemplate, + metadata_types: List[str], + label: str, + description: str, + tags: List[str], + column_names: List[str], + column_descriptions: List[str], + sample_data=None, + ) -> str: + # TODO add input validation + return template.format( + metadata_types=metadata_types, + label=label, + description=description, + tags=tags, + column_names=column_names, + column_descriptions=column_descriptions, + sample_data=sample_data, + ) + + @staticmethod + def get_folder_prompt_template() -> PromptTemplate: + prompt_template = """ + Generate a detailed metadata description for a database table using following provided data: + - folder name: {label}, + - folder_description: {description} + - folder_tags: {tags} + - file names: {file_names} + **Important**: + - If the data indicates "No description provided," do not use that particular input for generating metadata. + - Only focus on generating the following metadata types as specified by the user: {metadata_types}. Do not include any other metadata types. + - Return the result as a Python dictionary. + Your response should strictly contain the requested metadata types. + For example, if the requested metadata types are "tags" and "description", the response should be: + "tags": + "description": + For tags and topics, ensure the output is a string list. Label is singular so you should return only one label as string. + Return a python dictionary, all the keys must be lowercase. Don't use ' ' in your response, use " ". Include file types as pdf, and write file names in description. + Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with "NotEnoughData" for all values of dictionary keys. + """ + return PromptTemplate.from_template(prompt_template) + + @staticmethod + def format_folder_prompt_template( + template: PromptTemplate, + metadata_types: List[str], + label: str, + description: str, + tags: List[str], + file_names: List[str], + ) -> str: + # TODO add input validation + return template.format( + label=label, description=description, tags=tags, metadata_types=metadata_types, file_names=file_names + ) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index e80b8e4ba..52f68a2f7 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -48,7 +48,10 @@ from dataall.modules.s3_datasets.services.dataset_permissions import DATASET_TABLE_ALL from dataall.modules.datasets_base.services.dataset_service_interface import DatasetServiceInterface from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient -from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets +from dataall.modules.s3_datasets.services.dataset_metadata_service import ( + MetadataOutput, + DatasetMetadataGenerationService, +) log = logging.getLogger(__name__) @@ -572,16 +575,19 @@ def generate_metadata_for_dataset(resourceUri, version, metadataTypes): context = get_context() with context.db_engine.scoped_session() as session: dataset = DatasetBaseRepository.get_dataset_by_uri(session, resourceUri) - table_labels = [t.label for t in DatasetRepository.get_dataset_tables(session, resourceUri)] - table_descriptions = [t.description for t in DatasetRepository.get_dataset_tables(session, resourceUri)] - folders = [f.label for f in DatasetLocationRepository.get_dataset_folders(session, resourceUri)] - return BedrockClient(dataset.AwsAccountId, 'us-east-1').generate_metadata( - prompt_type=MetadataGenerationTargets.S3_Dataset.value, + tables = DatasetRepository.get_dataset_tables(session, dataset.datasetUri) + table_labels = [t.label for t in tables] + table_descriptions = [t.description for t in tables] + folder_labels = [f.label for f in DatasetLocationRepository.get_dataset_folders(session, resourceUri)] + template = DatasetMetadataGenerationService.get_dataset_prompt_template() + prompt = DatasetMetadataGenerationService.format_dataset_prompt_template( + template=template, + metadata_types=metadataTypes, label=dataset.label, - tables=table_labels, description=dataset.description, - table_description=table_descriptions, tags=dataset.tags, - metadata_type=metadataTypes, - folders=folders, + table_labels=table_labels, + table_descriptions=table_descriptions, + folder_labels=folder_labels, ) + return BedrockClient().invoke_model(prompt_template=template, prompt=prompt, output_object=MetadataOutput) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index d4a7f7878..251d21e01 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -196,7 +196,7 @@ def generate_metadata_for_table(resourceUri, version, metadataTypes, sampleData) with context.db_engine.scoped_session() as session: table = DatasetTableRepository.get_dataset_table_by_uri(session, resourceUri) table_column = DatasetColumnRepository.get_table_info_metadata_generation(session, resourceUri) - return BedrockClient(table_column.AWSAccountId, 'us-east-1').generate_metadata( + return BedrockClient().generate_metadata( prompt_type=MetadataGenerationTargets.Table.value, label=table.label, columns={','.join(table_column.label)}, diff --git a/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js index f50933bfb..0e02c4432 100644 --- a/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js @@ -1,11 +1,8 @@ -// import { LoadingButton } from '@mui/lab'; +import { useState, useCallback } from 'react'; import { - // Autocomplete, Avatar, Box, Button, - // CardContent, - // CardHeader, Checkbox, Chip, Divider, @@ -18,25 +15,17 @@ import { MenuItem, Select, Switch, - // TextField, Typography } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; -// import { Formik } from 'formik'; import { useSnackbar } from 'notistack'; import PropTypes from 'prop-types'; -// import { useCallback, useEffect, useState } from 'react'; -import { useState } from 'react'; import AutoModeIcon from '@mui/icons-material/AutoMode'; -// import * as Yup from 'yup'; -// import { ChipInput, Defaults } from 'design'; import { Defaults, Scrollbar } from 'design'; import { SET_ERROR, useDispatch } from 'globalErrors'; import { useClient } from 'services'; import { listDatasetTablesFolders, generateMetadataBedrock } from '../services'; -import { useCallback } from 'react'; -/* eslint-disable no-console */ export const GenerateMetadataComponent = (props) => { const { dataset, @@ -361,17 +350,19 @@ export const GenerateMetadataComponent = (props) => { } label="Tags" /> - - } - label="Subitem Descriptions" - /> + {targetType !== 'Dataset' && ( + + } + label="Subitem Descriptions" + /> + )} { variant="contained" disabled={Object.values(selectedMetadataTypes).every( (value) => value === false - )} // Note: I tested my multiple API call by setting this to {false} directly. + )} > Generate diff --git a/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js index efde24871..4e4e21f4a 100644 --- a/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js +++ b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js @@ -11,7 +11,7 @@ export const MetadataMainModal = (props) => { const [targetType, setTargetType] = useState(''); const [targets, setTargets] = useState([]); const [targetOptions, setTargetOptions] = useState([]); - const [version, setVersion] = useState(0); + const [version, setVersion] = useState(0); //TODO: use version and add previous response in backend const [selectedMetadataTypes, setSelectedMetadataTypes] = useState({ label: false, description: false, diff --git a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js index 46488d4b2..48d45e4aa 100644 --- a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js @@ -1,10 +1,5 @@ -import { - Button, - Box, - Chip, - Typography, - CircularProgress -} from '@mui/material'; +import React, { useState } from 'react'; +import { Button, Box, Chip, Typography, CircularProgress } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import { useSnackbar } from 'notistack'; import PropTypes from 'prop-types'; @@ -12,23 +7,20 @@ import AutoModeIcon from '@mui/icons-material/AutoMode'; import { Scrollbar } from 'design'; import { SET_ERROR, useDispatch } from 'globalErrors'; import { useClient } from 'services'; -import { updateDataset, generateMetadataBedrock } from '../services'; import { updateDatasetTable } from 'modules/Tables/services'; -import { BatchUpdateDatasetTableColumn } from '../services/batchUpdateTableColumnDescriptions'; -import { listSampleData } from '../services/listSampleData'; import { updateDatasetStorageLocation } from 'modules/Folders/services'; +import { + BatchUpdateDatasetTableColumn, + listSampleData, + updateDataset, + generateMetadataBedrock +} from '../services'; import SampleDataPopup from './SampleDataPopup'; -import React, { useState } from 'react'; import SubitemDescriptionsGrid from './SubitemDescriptionsGrid'; export const ReviewMetadataComponent = (props) => { - const { - dataset, - targets, - setTargets, - selectedMetadataTypes, - version - } = props; + const { dataset, targets, setTargets, selectedMetadataTypes, version } = + props; const { enqueueSnackbar } = useSnackbar(); const dispatch = useDispatch(); const client = useClient(); @@ -99,7 +91,6 @@ export const ReviewMetadataComponent = (props) => { } } const handleAcceptAndRegenerate = async () => { - // Perform any necessary actions for accepting and regenerating the data try { const targetIndex = targets.findIndex((t) => t.targetUri === targetUri); if (targetIndex !== -1) { @@ -166,7 +157,6 @@ export const ReviewMetadataComponent = (props) => { const updatedTargets = targets.map(async (target) => { const updatedMetadata = {}; - // Loop through selectedMetadataTypes and add the corresponding key-value pairs to updatedMetadata Object.entries(selectedMetadataTypes).forEach( ([metadataType, checked]) => { if (checked) { @@ -212,7 +202,7 @@ export const ReviewMetadataComponent = (props) => { ); if (!response.errors) { - return { ...target, success: true }; // Return the updated target with success flag + return { ...target, success: true }; } else { dispatch({ type: SET_ERROR, error: response.errors[0].message }); return { ...target, success: false }; // Return the target with success flag set to false @@ -465,7 +455,6 @@ export const ReviewMetadataComponent = (props) => { @@ -477,7 +481,5 @@ ReviewMetadataComponent.propTypes = { targetType: PropTypes.string.isRequired, targets: PropTypes.array.isRequired, setTargets: PropTypes.func.isRequired, - selectedMetadataTypes: PropTypes.object.isRequired, - version: PropTypes.number.isRequired, - setVersion: PropTypes.func.isRequired + selectedMetadataTypes: PropTypes.object.isRequired }; diff --git a/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js b/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js index 15176ce08..4eb9605c6 100644 --- a/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js +++ b/frontend/src/modules/S3_Datasets/services/generateMetadataBedrock.js @@ -4,42 +4,33 @@ export const generateMetadataBedrock = ({ resourceUri, targetType, metadataTypes, - version, - sampleData + tableSampleData }) => ({ variables: { resourceUri, targetType, metadataTypes, - version, - sampleData + tableSampleData }, mutation: gql` mutation generateMetadata( $resourceUri: String! - $targetType: MetadataGenerationTargets - $metadataTypes: [String] - $version: Int - $sampleData: SampleDataInput + $targetType: MetadataGenerationTargets! + $metadataTypes: [String]! + $tableSampleData: TableSampleData ) { generateMetadata( resourceUri: $resourceUri targetType: $targetType metadataTypes: $metadataTypes - version: $version - sampleData: $sampleData + tableSampleData: $tableSampleData ) { - type + targetUri + targetType label description tags topics - name - subitem_descriptions { - description - label - subitem_id - } } } ` From 93223823d3e914a1fd3ad62fe0dd329d0231d278 Mon Sep 17 00:00:00 2001 From: dlpzx Date: Wed, 18 Jun 2025 14:42:46 +0200 Subject: [PATCH 08/22] merge-conflicts --- .../services/dataset_location_service.py | 168 ------------------ .../s3_datasets/services/dataset_service.py | 1 - 2 files changed, 169 deletions(-) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py index 8ae8647ca..e69de29bb 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py @@ -1,168 +0,0 @@ -import logging - -from dataall.modules.s3_datasets.indexers.dataset_indexer import DatasetIndexer -from dataall.base.context import get_context -from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService -from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService - -##TODO -##from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService -from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository -from dataall.base.db.exceptions import ResourceAlreadyExists -from dataall.modules.s3_datasets.services.dataset_service import DatasetService -from dataall.modules.s3_datasets.aws.s3_location_client import S3LocationClient -from dataall.modules.s3_datasets.db.dataset_location_repositories import DatasetLocationRepository -from dataall.modules.s3_datasets.indexers.location_indexer import DatasetLocationIndexer -from dataall.modules.s3_datasets.services.dataset_permissions import ( - UPDATE_DATASET_FOLDER, - MANAGE_DATASETS, - CREATE_DATASET_FOLDER, - LIST_DATASET_FOLDERS, - DELETE_DATASET_FOLDER, -) -from dataall.modules.s3_datasets.services.dataset_permissions import DATASET_FOLDER_READ, GET_DATASET_FOLDER -from dataall.modules.s3_datasets.db.dataset_repositories import DatasetRepository -from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation, S3Dataset -from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient -from dataall.modules.s3_datasets.aws.s3_dataset_client import S3DatasetClient - -log = logging.getLogger(__name__) - - -class DatasetLocationService: - @staticmethod - def _get_dataset_uri(session, uri): - location = DatasetLocationRepository.get_location_by_uri(session, uri) - return location.datasetUri - - @staticmethod - @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) - @ResourcePolicyService.has_resource_permission(CREATE_DATASET_FOLDER) - def create_storage_location(uri: str, data: dict): - with get_context().db_engine.scoped_session() as session: - exists = DatasetLocationRepository.exists(session, uri, data['prefix']) - - if exists: - raise ResourceAlreadyExists( - action='Create Folder', - message=f'Folder: {data["prefix"]} already exist on dataset {uri}', - ) - - dataset = DatasetRepository.get_dataset_by_uri(session, uri) - location = DatasetLocationRepository.create_dataset_location(session, dataset, data) - DatasetLocationService._attach_dataset_folder_read_permission(session, dataset, location.locationUri) - - if 'terms' in data.keys(): - DatasetLocationService._create_glossary_links(session, location, data['terms']) - - S3LocationClient(location, dataset).create_bucket_prefix() - - DatasetLocationIndexer.upsert(session=session, folder_uri=location.locationUri) - DatasetIndexer.upsert(session, dataset.datasetUri) - return location - - @staticmethod - @ResourcePolicyService.has_resource_permission(LIST_DATASET_FOLDERS) - def list_dataset_locations(uri: str, filter: dict = None): - with get_context().db_engine.scoped_session() as session: - return DatasetLocationRepository.list_dataset_locations(session=session, uri=uri, data=filter) - - @staticmethod - def get_storage_location(uri): - with get_context().db_engine.scoped_session() as session: - return DatasetLocationRepository.get_location_by_uri(session, uri) - - @staticmethod - @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) - @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_FOLDER, parent_resource=_get_dataset_uri) - def update_storage_location(uri: str, data: dict): - with get_context().db_engine.scoped_session() as session: - location = DatasetLocationRepository.get_location_by_uri(session, uri) - for k in data.keys(): - setattr(location, k, data.get(k)) - - if 'terms' in data.keys(): - DatasetLocationService._create_glossary_links(session, location, data['terms']) - - DatasetLocationIndexer.upsert(session, folder_uri=location.locationUri) - DatasetIndexer.upsert(session, location.datasetUri) - - return location - - @staticmethod - @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) - @ResourcePolicyService.has_resource_permission(DELETE_DATASET_FOLDER, parent_resource=_get_dataset_uri) - def remove_storage_location(uri: str = None): - with get_context().db_engine.scoped_session() as session: - location = DatasetLocationRepository.get_location_by_uri(session, uri) - DatasetService.check_before_delete(session, location.locationUri, action=DELETE_DATASET_FOLDER) - DatasetService.execute_on_delete(session, location.locationUri, action=DELETE_DATASET_FOLDER) - dataset = DatasetRepository.get_dataset_by_uri(session, location.datasetUri) - DatasetLocationService._delete_dataset_folder_read_permission(session, dataset, location.locationUri) - DatasetLocationRepository.delete(session, location) - GlossaryRepository.delete_glossary_terms_links( - session, - target_uri=location.locationUri, - target_type='DatasetStorageLocation', - ) - DatasetLocationIndexer.delete_doc(doc_id=location.locationUri) - return True - - @staticmethod - def _create_glossary_links(session, location, terms): - GlossaryRepository.set_glossary_terms_links( - session, get_context().username, location.locationUri, 'Folder', terms - ) - - @staticmethod - def _attach_dataset_folder_read_permission(session, dataset: S3Dataset, location_uri): - """ - Attach Folder permissions to dataset groups - """ - permission_group = { - dataset.SamlAdminGroupName, - dataset.stewards if dataset.stewards is not None else dataset.SamlAdminGroupName, - } - for group in permission_group: - ResourcePolicyService.attach_resource_policy( - session=session, - group=group, - permissions=DATASET_FOLDER_READ, - resource_uri=location_uri, - resource_type=DatasetStorageLocation.__name__, - ) - - @staticmethod - def _delete_dataset_folder_read_permission(session, dataset: S3Dataset, location_uri): - """ - Delete Folder permissions to dataset groups - """ - permission_group = { - dataset.SamlAdminGroupName, - dataset.stewards if dataset.stewards is not None else dataset.SamlAdminGroupName, - } - for group in permission_group: - ResourcePolicyService.delete_resource_policy(session=session, group=group, resource_uri=location_uri) - - @staticmethod - @ResourcePolicyService.has_resource_permission(GET_DATASET_FOLDER) - def get_folder_restricted_information(uri: str, folder: DatasetStorageLocation): - context = get_context() - with context.db_engine.scoped_session() as session: - return DatasetRepository.get_dataset_by_uri(session, folder.datasetUri) - - @staticmethod - @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_FOLDER) - # @ResourceThresholdService.check_invocation_count( - # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' - # ) - def generate_metadata_for_folder(uri, metadata_types): - context = get_context() - with context.db_engine.scoped_session() as session: - folder = DatasetLocationRepository.get_location_by_uri(session, uri) - dataset = DatasetRepository.get_dataset_by_uri(session, folder.datasetUri) - files = S3DatasetClient(dataset).list_bucket_files(folder.S3BucketName, folder.S3Prefix) - metadata = BedrockClient().invoke_model_folder_metadata( - metadata_types=metadata_types, folder=folder, files=[f['Key'] for f in files] - ) - return [{'targetUri': uri, 'targetType': 'Folder'} | metadata] diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index d5810535d..1088a066c 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -32,7 +32,6 @@ from dataall.modules.s3_datasets.db.dataset_table_repositories import DatasetTableRepository from dataall.modules.s3_datasets.indexers.dataset_indexer import DatasetIndexer from dataall.modules.s3_datasets.services.dataset_permissions import ( - GET_DATASET, CREDENTIALS_DATASET, CRAWL_DATASET, DELETE_DATASET, From 12c6e97b784497c14d6c6850b0edbeca94ceaacc Mon Sep 17 00:00:00 2001 From: dlpzx Date: Thu, 17 Jul 2025 14:48:20 +0200 Subject: [PATCH 09/22] GenAI metadata generation - improvements --- .../dataall/base/cdkproxy/requirements.txt | 4 +- .../s3_datasets/api/dataset/queries.py | 7 - .../s3_datasets/api/dataset/resolvers.py | 5 - .../modules/s3_datasets/api/table/queries.py | 14 +- .../s3_datasets/api/table/resolvers.py | 6 + .../s3_datasets/aws/athena_table_client.py | 2 +- .../aws/bedrock_metadata_client.py | 26 +- .../metadata_generation_dataset_template.txt | 136 +- .../metadata_generation_folder_template.txt | 105 +- .../metadata_generation_table_template.txt | 192 +- .../db/dataset_column_repositories.py | 38 +- .../services/dataset_location_service.py | 169 + .../s3_datasets/services/dataset_service.py | 3 +- .../services/dataset_table_service.py | 50 +- backend/requirements.txt | 9 +- .../components/GenerateMetadataComponent.js | 243 +- .../components/MetadataMainModal.js | 1 + .../components/ReviewMetadataComponent.js | 361 ++- .../S3_Datasets/components/SampleDataPopup.js | 25 +- .../src/modules/S3_Datasets/services/index.js | 2 +- .../S3_Datasets/services/listSampleData.js | 15 - .../services/listTableSampleData.js | 15 + frontend/yarn.lock | 2869 ++++++++++++++--- 23 files changed, 3363 insertions(+), 934 deletions(-) delete mode 100644 frontend/src/modules/S3_Datasets/services/listSampleData.js create mode 100644 frontend/src/modules/S3_Datasets/services/listTableSampleData.js diff --git a/backend/dataall/base/cdkproxy/requirements.txt b/backend/dataall/base/cdkproxy/requirements.txt index b1ca43acd..d13832ef2 100644 --- a/backend/dataall/base/cdkproxy/requirements.txt +++ b/backend/dataall/base/cdkproxy/requirements.txt @@ -1,8 +1,8 @@ aws-cdk-lib==2.177.0 -boto3==1.35.26 +boto3==1.39.7 boto3-stubs==1.35.26 cdk-nag==2.7.2 -fastapi == 0.115.5 +fastapi == 0.116.1 PyYAML==6.0 requests==2.32.2 tabulate==0.8.9 diff --git a/backend/dataall/modules/s3_datasets/api/dataset/queries.py b/backend/dataall/modules/s3_datasets/api/dataset/queries.py index fdd9ed5aa..fd8824bf0 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/queries.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/queries.py @@ -5,7 +5,6 @@ get_file_upload_presigned_url, list_datasets_owned_by_env_group, list_dataset_tables_folders, - read_sample_data, ) getDataset = gql.QueryField( @@ -56,9 +55,3 @@ type=gql.Ref('DatasetItemsSearchResult'), resolver=list_dataset_tables_folders, ) -listSampleData = gql.QueryField( - name='listSampleData', - args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], - type=gql.Ref('QueryPreviewResult'), # basically returns nothing...? - resolver=read_sample_data, -) # return the data -> user invokes generateMetadata again + sample data ; similar api exists diff --git a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py index 6702bf267..e131f27fd 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py @@ -193,11 +193,6 @@ def generate_metadata( raise Exception('Unsupported target type for metadata generation') -def read_sample_data(context: Context, source: S3Dataset, tableUri: str): - RequestValidator.validate_uri(param_name='tableUri', param_value=tableUri) - return DatasetTableService.preview(uri=tableUri) - - def update_dataset_metadata(context: Context, source: S3Dataset, resourceUri: str): return DatasetService.update_dataset(uri=resourceUri, data=input) diff --git a/backend/dataall/modules/s3_datasets/api/table/queries.py b/backend/dataall/modules/s3_datasets/api/table/queries.py index 6b86acb1f..fc207a65f 100644 --- a/backend/dataall/modules/s3_datasets/api/table/queries.py +++ b/backend/dataall/modules/s3_datasets/api/table/queries.py @@ -1,6 +1,11 @@ from dataall.base.api import gql from dataall.modules.s3_datasets.api.table.input_types import DatasetTableFilter -from dataall.modules.s3_datasets.api.table.resolvers import get_table, preview, list_table_data_filters +from dataall.modules.s3_datasets.api.table.resolvers import ( + get_table, + preview, + read_sample_data, + list_table_data_filters, +) from dataall.modules.s3_datasets.api.table.types import ( DatasetTable, DatasetTableSearchResult, @@ -38,6 +43,13 @@ type=gql.Ref('QueryPreviewResult'), ) +listTableSampleData = gql.QueryField( + name='listTableSampleData', + args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], + type=gql.Ref('QueryPreviewResult'), + resolver=read_sample_data, +) + listTableDataFilters = gql.QueryField( name='listTableDataFilters', args=[ diff --git a/backend/dataall/modules/s3_datasets/api/table/resolvers.py b/backend/dataall/modules/s3_datasets/api/table/resolvers.py index 63d0b0299..5b377a53e 100644 --- a/backend/dataall/modules/s3_datasets/api/table/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/table/resolvers.py @@ -33,6 +33,12 @@ def preview(context, source, tableUri: str = None): return DatasetTableService.preview(uri=tableUri) +def read_sample_data(context: Context, source: S3Dataset, tableUri: str): + if not tableUri: + return None + return DatasetTableService.read_table_sample(uri=tableUri) + + def get_glue_table_properties(context: Context, source: DatasetTable, **kwargs): if not source: return None diff --git a/backend/dataall/modules/s3_datasets/aws/athena_table_client.py b/backend/dataall/modules/s3_datasets/aws/athena_table_client.py index fd511d385..ca69d9f5e 100644 --- a/backend/dataall/modules/s3_datasets/aws/athena_table_client.py +++ b/backend/dataall/modules/s3_datasets/aws/athena_table_client.py @@ -37,7 +37,7 @@ def get_table(self): ) cursor = connection.cursor() - sql = 'select * from {table_identifier} limit 50'.format( + sql = 'select * from {table_identifier} order by rand() limit 50'.format( table_identifier=sql_utils.Identifier(self._table.GlueDatabaseName, self._table.GlueTableName) ) cursor.execute(sql) # nosemgrep diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py index 72f0cd21a..b01fcfad0 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_metadata_client.py @@ -1,12 +1,11 @@ import logging import os -from dataall.base.db import exceptions from dataall.base.aws.sts import SessionHelper from typing import List, Optional from langchain_core.prompts import PromptTemplate -from langchain_core.pydantic_v1 import BaseModel -from langchain_aws import ChatBedrock as BedrockChat +from pydantic import BaseModel +from langchain_aws import ChatBedrockConverse from langchain_core.output_parsers import JsonOutputParser log = logging.getLogger(__name__) @@ -22,12 +21,17 @@ ) +class ColumnMetadata(BaseModel): + label: str + description: str + + class MetadataOutput(BaseModel): tags: Optional[List[str]] = None description: Optional[str] = None label: Optional[str] = None topics: Optional[List[str]] = None - columns_metadata: Optional[List[dict]] = None + subitem_descriptions: Optional[List[ColumnMetadata]] = None class BedrockClient: @@ -39,10 +43,9 @@ def __init__(self): 'max_tokens': 4096, 'temperature': 0.5, 'top_k': 250, - 'top_p': 0.5, - 'stop_sequences': ['\n\nHuman'], + 'stop_sequences': [], } - self._model = BedrockChat(client=self._client, model_id=model_id, model_kwargs=model_kwargs) + self._model = ChatBedrockConverse(client=self._client, model_id=model_id, **model_kwargs) def invoke_model_dataset_metadata(self, metadata_types, dataset, tables, folders): try: @@ -63,18 +66,20 @@ def invoke_model_dataset_metadata(self, metadata_types, dataset, tables, folders except Exception as e: raise e - def invoke_model_table_metadata(self, metadata_types, table, columns, sample_data, generate_columns_metadata=False): + def invoke_model_table_metadata(self, metadata_types, table, columns, sample_data): try: prompt_template = PromptTemplate.from_file(METADATA_GENERATION_TABLE_TEMPLATE_PATH) parser = JsonOutputParser(pydantic_object=MetadataOutput) chain = prompt_template | self._model | parser + + # Check if subitem_descriptions is in the requested metadata types + generate_columns = 'subitem_descriptions' in metadata_types context = { 'metadata_types': metadata_types, - 'generate_columns_metadata': generate_columns_metadata, + 'generate_columns_metadata': generate_columns, 'label': table.label, 'description': table.description, 'tags': table.tags, - 'topics': table.topics, 'column_labels': [c.label for c in columns], 'column_descriptions': [c.description for c in columns], 'sample_data': sample_data, @@ -93,7 +98,6 @@ def invoke_model_folder_metadata(self, metadata_types, folder, files): 'label': folder.label, 'description': folder.description, 'tags': folder.tags, - 'topics': folder.topics, 'file_names': files, } return chain.invoke(context) diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_dataset_template.txt b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_dataset_template.txt index c99c75c98..1a863c832 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_dataset_template.txt +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_dataset_template.txt @@ -1,82 +1,76 @@ -Your task is to generate or improve the metadata fields of a Dataset. - -Use the following input parameters: - - Dataset name: {dataset_label} - - Current dataset description: {description} - - Current tags for dataset: {tags} - - Current topics for dataset: {topics} - - Table names in the dataset: {table_names} - - Folder names in the dataset: {folder_names} - - -There are 4 metadata fields that can be requested to you. - 1. label - 1 to 3 words that give a "title" to the Dataset. If provided, you can use the current Dataset name as starting point. - 2. description - less than 30 words that summarize the Tables and Folders contained in the Dataset. If provided, use the current description and tags as starting point; but mainly use the Table names and Folder names. - 3. tags - list of strings (less than 3), where each string can take any value. Tags should highlight the most important field or thematic of the Dataset. If there are current tags that represent additional information, add them to the list of tags. Do not return the label as a tag. - 4. topics - list of strings (1 or 2), where each string must be one of the following topics that represent company departments ['Finance', 'Marketing', 'Engineering', 'HR', 'Operations', 'Sales', 'Other'] Choose a topic according to the Tables and Folders of the dataset. If there are current topics that represent additional information, add them to the list of topics. - -There are some rules that you MUST follow: -- If any of the input parameters is equal to "No description provided" or is None or [] do not use that particular input -for generating the metadata fields. -- This time the user has requested ONLY the following metadata fields: {metadata_types} Your response should strictly -contain only the requested metadata fields. -- Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with -"NotEnoughData" for all values of dictionary keys. -- If the Table names and the Folder names are both none or [], return "Empty Dataset" as the description and "empty" as one of the tags. -- Return the result as a Python dictionary where the keys are the requested metadata fields, all the keys must be -lowercase and the values are the corresponding generated metadata. -- Do not return any explanations, ONLY the Python dictionary. - ---------------------------------------- ---------------------------------------- -Here are some examples: - -Example 1. - -Given the following input parameters: - label: None, - description: No description provided, - tags: [], - table_names: [], - folder_names: [], - metadata_types: ["label", "description", "tags", "topics"] - -response = {{ +You are a metadata generation assistant for AWS data assets. Your task is to generate or enhance metadata fields for a Dataset based on the provided information. + +INPUT PARAMETERS: +- Dataset name: {dataset_label} +- Current dataset description: {description} +- Current tags for dataset: {tags} +- Current topics for dataset: {topics} +- Table names in the dataset: {table_names} +- Table descriptions in the dataset: {table_descriptions} +- Folder names in the dataset: {folder_names} + +METADATA FIELDS REQUESTED: {metadata_types} +You will only generate the fields listed above. Each field has specific requirements: + +1. label - A concise title (1-3 words) for the Dataset. Use the current name as a starting point if available. +2. description - A concise summary (<30 words) of the Dataset's contents, focusing primarily on the Tables and Folders it contains. +3. tags - Up to 3 keywords highlighting the Dataset's main themes or content types. Do not duplicate the label as a tag. +4. topics - 1-2 topics from this fixed list: ['Finances', 'HumanResources', 'Products', 'Services', 'Operations', 'Research', 'Sales', 'Orders', 'Sites', 'Energy', 'Customers', 'Misc'] + +RULES: +- Ignore any input parameter that is "No description provided", None, or an empty list []. +- Return ONLY the requested metadata fields as specified in {metadata_types}. +- If insufficient data exists to generate meaningful metadata, return "NotEnoughData" for those fields. +- If both Table names and Folder names are empty or None, use "Empty Dataset" as the description and include "empty" as a tag. +- Return results as a Python dictionary with lowercase keys matching the requested metadata fields. +- Provide ONLY the Python dictionary in your response, no explanations or additional text. + +EXAMPLES: + +Example 1: Insufficient data +Input: +- Dataset name: None +- Current description: No description provided +- Current tags: [] +- Table names: [] +- Folder names: [] +- Requested fields: ["label", "description", "tags", "topics"] + +Output: +{{ "label": "NotEnoughData", "description": "Empty Dataset", - "topics": "NotEnoughData", - "tags": ["empty"] + "tags": ["empty"], + "topics": "NotEnoughData" }} -Example 2. -Given the following input parameters: - label: None, - description: No description provided, - tags: [], - table_names: ["customer_orders", "product_inventory", "sales_transactions"], - folder_names: ["orders", "inventory", "sales"], - metadata_types: ["label", "description"] - -response = {{ +Example 2: Sales data +Input: +- Dataset name: None +- Current description: No description provided +- Current tags: [] +- Table names: ["customer_orders", "product_inventory", "sales_transactions"] +- Folder names: ["orders", "inventory", "sales"] +- Requested fields: ["label", "description"] + +Output: +{{ "label": "Sales and Inventory", "description": "Dataset containing customer orders, product inventory, and sales transactions information, organized into orders, inventory, and sales folders." }} -Example 3. -Given the following input parameters: - label: None, - description: No description provided, - tags: [], - table_names: ["employee_data", "payroll", "performance_reviews"],, - folder_names: ["hr_records", "financial", "evaluations"], - metadata_types: ["label", "tags", "topics"] - -response = {{ +Example 3: HR data +Input: +- Dataset name: None +- Current description: No description provided +- Current tags: [] +- Table names: ["employee_data", "payroll", "performance_reviews"] +- Folder names: ["hr_records", "financial", "evaluations"] +- Requested fields: ["label", "tags", "topics"] + +Output: +{{ "label": "HR Management System", "tags": ["employee", "payroll", "performance"], - "topics": ["HR", "Finance"] + "topics": ["HumanResources", "Finances"] }} - - - - diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt index 37bcef15f..ea20630e8 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt @@ -1,61 +1,68 @@ -Your task is to generate or improve the metadata fields of a Folder (S3 Prefix). +You are a metadata generation assistant for AWS data assets. Your task is to generate or enhance metadata fields for a Folder (S3 Prefix) based on the provided information. -Use the following input parameters: - - Folder name: {label}, - - Current Folder description: {description} - - Current tags for Folder: {tags} - - Current topics for Folder: {topics} - - File names (files stored inside the folder): {file_names} +INPUT PARAMETERS: +- Folder name: {label} +- Current Folder description: {description} +- Current tags for Folder: {tags} +- File names (files stored inside the folder): {file_names} +METADATA FIELDS REQUESTED: {metadata_types} +You will only generate the fields listed above. Each field has specific requirements: -There are 4 metadata fields that can be requested to you. - 1. label - 1 to 3 words that give a "title" to the Folder. If provided, you can use the current Folder name as starting point. - 2. description - less than 30 words that summarize the files contained in the Folder. If provided, use the current description and tags as starting point; but mainly use the file names. - 3. tags - list of strings (less than 3), where each string can take any value. Tags should highlight the most important field or thematic of the Folder. If there are current tags that represent additional information, add them to the list of tags. - 4. topics - list of strings (1 or 2), where each string must be one of the following topics that represent company departments ['Finance', 'Marketing', 'Engineering', 'HR', 'Operations', 'Sales', 'Other'] Choose a topic according to the files contained in the Folder. If there are current topics that represent additional information, add them to the list of topics. +1. label - A concise title (1-3 words) for the Folder. Use the current name as a starting point if available. +2. description - A concise summary (<30 words) of the Folder's contents, focusing primarily on the files it contains. +3. tags - Up to 3 keywords highlighting the Folder's main themes or content types. -There are some rules that you MUST follow: -- If any of the input parameters is equal to "No description provided" or is None or [] do not use that particular input -for generating the metadata fields. -- This time the user has requested ONLY the following metadata fields: {metadata_types} Your response should strictly -contain only the requested metadata fields. -- Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with -"NotEnoughData" for all values of dictionary keys. -- Return the result as a Python dictionary where the keys are the requested metadata fields, all the keys must be -lowercase and the values are the corresponding generated metadata. +RULES: +- Ignore any input parameter that is "No description provided", None, or an empty list []. +- Return ONLY the requested metadata fields as specified in {metadata_types}. +- If insufficient data exists to generate meaningful metadata, return "NotEnoughData" for those fields. +- If file_names is empty, you likely have insufficient information to generate accurate metadata. +- Return results as a Python dictionary with lowercase keys matching the requested metadata fields. +- Provide ONLY the Python dictionary in your response, no explanations or additional text. ---------------------------------------- ---------------------------------------- -Here are some examples: +EXAMPLES: -Example 1. +Example 1: Insufficient data +Input: +- Folder name: my-folder-1 +- Current description: No description provided +- Current tags: [] +- File names: [] +- Requested fields: ["label", "description"] -Given the following input parameters: - Folder name: my-folder-1 - Current Folder description: No description provided - Current tags for Folder: [] - File names (files stored inside the folder): [] - metadata_types: ["label", "description", "topics"] +Output: +{{ + "label": "NotEnoughData", + "description": "NotEnoughData" +}} +Example 2: Sales reports folder +Input: +- Folder name: my-folder-2 +- Current description: Demo folder with some sample data. +- Current tags: ['demo'] +- File names: ['sales_report_2024.pdf', 'sales_terminology.pdf', 'year_over_year_increase.png'] +- Requested fields: ["label", "description", "tags"] -response = { - "label": "NotEnoughData", - "description": "NotEnoughData", - "topics": "NotEnoughData" -} - -Example 2. -Given the following input parameters: - Folder name: my-folder-2 - Current Folder description: Demo folder with some sample data. - Current tags for Folder: ['demo'] - File names (files stored inside the folder): ['sales_report_2024.pdf', 'sales_terminology.pdf', 'year_over_year_increase.png'] - metadata_types: ["label", description", "tags"] - -response = { +Output: +{{ "label": "Sales Report 2024", - "description": "This folder contains the report alongside terminology and graphs to describe the sales of 2024." - "tags": ["sales", "2024"] -} + "description": "This folder contains sales reports for 2024, including terminology documentation and year-over-year performance visualizations.", + "tags": ["sales", "reports", "2024"] +}} + +Example 3: Marketing assets folder +Input: +- Folder name: marketing-assets +- Current description: No description provided +- Current tags: [] +- File names: ['brand_guidelines.pdf', 'logo_primary.png', 'logo_secondary.png', 'color_palette.pdf'] +- Requested fields: ["label", "description"] +Output: +{{ + "label": "Brand Assets", + "description": "Collection of brand identity materials including guidelines, primary and secondary logos, and color palette documentation." +}} diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_table_template.txt b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_table_template.txt index f11f069bb..e1088729e 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_table_template.txt +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_table_template.txt @@ -1,107 +1,85 @@ -Your task is to generate or improve the metadata fields for a Table. - -Use the following input parameters: - - Table name: {label} - - Current table description: {description} - - Current tags for table: {tags} - - Current topics for table: {topics} - - Column names: {columns} - - Column Descriptions: {column_descriptions} - - Sample data: {sample_data} - - Generate columns metadata: {generate_columns_metadata} - - Column URIs: {column_uris} - IGNORE this input if Generate columns metadata=False - -There are 4 metadata fields that can be requested to you. - 1. label - 1 to 3 words that give a "title" to the Table. If provided, you can use the current Table name as starting point. - 2. description - less than 30 words that summarize the content of the table. If provided, take the current Table description as starting point but mainly use the sample data (if provided), and then the column names and descriptions to generate the table description. - 3. tags - list of strings (less than 3), where each string can take any value. Tags should highlight the most important field or thematic of the Table. If there are current tags that represent additional information, add them to the list of tags. - 4. topics - list of strings (1 or 2), where each string must be one of the following topics that represent company departments ['Finance', 'Marketing', 'Engineering', 'HR', 'Operations', 'Sales', 'Other'] Choose a topic according to the Table description. If there are current topics that represent additional information, add them to the list of topics. - - -There are some rules that you MUST follow: -- If any of the input parameters is equal to "No description provided" or is None or [] do not use that particular input -for generating the metadata fields. -- This time the user has requested ONLY the following metadata fields: {metadata_types} Your response should strictly -contain only the requested metadata fields. -- Evaluate if the given parameters are sufficient for generating the requested metadata, if not, respond with -"NotEnoughData" for all values of dictionary keys. -- Return the result as a Python dictionary where the keys are the requested metadata fields, all the keys must be -lowercase and the values are the corresponding generated metadata. -- If Generate columns metadata = True, you also need to generate the description of the columns. For this task try to use the -sample data and the column names. Evaluate if that is sufficient for generating the column descriptions, if not, -each description will be equal to "NotEnoughData". Add the result to the Python dictionary with the key "columns_metadata" and as -value, another Python dictionary containing a list of objects whose keys are the columnUris and the values the generated column -descriptions. - ---------------------------------------- ---------------------------------------- -Here are some examples: - -Example 1. - -Given the following input parameters: - Table name: my-table-1 - Current table description: Demo data - Current tags for table: ["demo"] - Column URIs: [] - Column names: [] - Column Descriptions: [] - Sample data: None - metadata_types: ["label", "description", "topics"] - Generate columns metadata: False - - -response = { - "label": "NotEnoughData", - "description": "NotEnoughData", - "topics": "NotEnoughData" -} - -Example 2. -Given the following input parameters: - Table name: my-table-2 - Current table description: Demo table with some sample data. - Current tags for table: ['demo'] - Column URIs: ["asdfgh", "123ghj", "oplk34", "cvb890", "l29ghk", "cif41l"] - Column names: ['author', 'year', 'avg_rating', 'pages', 'genre', 'count'] - Column Descriptions: ['Author that wrote the book', 'Year of publication', 'Average rating in Amazon.com', 'pages', 'genre', 'books sold in Amazon.com'] - Sample data: None - metadata_types: ["label", description", "tags"] - Generate columns metadata: False - -response = { - "label": "books_sales_with_ratings", - "description": "This table is used for demo purposes and it contains book-related information including authorship, publication details, and performance metrics. It tracks books with their authors, publication years, and reader feedback through Amazon.com ratings." - "tags": ["demo", "sales"] -} - - -Example 3. -Given the following input parameters: - Table name: my-table-2 - Current table description: Demo table with some sample data. - Current tags for table: ['demo'] - Column URIs: ["asdfgh", "123ghj", "oplk34", "cvb890", "l29ghk", "cif41l"] - Column names: ['author', 'year', 'avg_rating_amzn', 'pages', 'genre', 'sales_amzn'] - Column Descriptions: [] - Sample data: {"rows": ["[\"\\\"JK Rowling\\\"\", \"\\\"1995\\\"\", \"\\\"8\\\"\", \"\\\"419\\\"\", \"\\\"Science Fiction\\\"\", \"\\\"225890345\\\"\"]", [\"\\\"Gabriel Garcia Marquez\\\"\", \"\\\"1913\\\"\", \"\\\"9\\\"\", \"\\\"337\\\"\", \"\\\"Magical Realism\\\"\", \"\\\"221133\\\"\"]], "fields": ["{\"name\": \"author\"}", "{\"name\": \"year\"}", "{\"name\": \"avg_rating\"}", "{\"name\": \"pages\"}", "{\"name\": \"genre\"}", "{\"name\": \"count\"}"], "__typename": "QueryPreviewResult"}}} - metadata_types: ["label", description", "tags"] - Generate columns metadata: True - -response = { - "label": "books_sales_with_ratings", - "description": "This table is used for demo purposes and it contains book-related information including authorship, publication details, and performance metrics. It tracks books with their authors, publication years, and reader feedback through Amazon.com ratings." - "tags": ["demo", "sales"], - "columns_metadata": { - "asdfgh": "Author that wrote the book", - "123ghj": "Year of publication" - "oplk34": "Average rating in Amazon.com" - "cvb890": "Number of pages" - "l29ghk": "Genre" - "cif41l": "Number of copies sold in Amazon.com" - } -} - - - - +You are a metadata generation assistant for AWS data assets. Your task is to generate or enhance metadata fields for a Table based on the provided information. + +INPUT PARAMETERS: +- Table name: {label} +- Current table description: {description} +- Current tags for table: {tags} +- Column names: {column_labels} +- Column Descriptions: {column_descriptions} +- Sample data: {sample_data} +- Generate columns metadata: {generate_columns_metadata} + +METADATA FIELDS REQUESTED: {metadata_types} +You will only generate the fields listed above. Each field has specific requirements: + +1. description - A concise summary (<30 words) of the Table's contents, focusing primarily on the sample data, column names, and descriptions. +2. tags - Up to 3 keywords highlighting the Table's main themes or content types. +3. subitem_descriptions - If Generate columns metadata=True, provide descriptions for each column based on sample data and column names. + +RULES: +- Ignore any input parameter that is "No description provided", None, or an empty list []. +- Return ONLY the requested metadata fields as specified in {metadata_types}. +- If insufficient data exists to generate meaningful metadata, return "NotEnoughData" for those fields. +- Return results as a Python dictionary with lowercase keys matching the requested metadata fields. +- If Generate columns metadata=True, include a "subitem_descriptions" key with a list of objects containing the unmodified column name {column_labels} and "description" (generated description). +- Provide ONLY the Python dictionary in your response, no explanations or additional text. + +EXAMPLES: + +Example 1: Insufficient data +Input: +- Table name: my-table-1 +- Current description: Demo data +- Current tags: ["demo"] +- Column names: [] +- Column Descriptions: [] +- Sample data: None +- Generate columns metadata: False +- Requested fields: ["description"] + +Output: +{{ + "description": "NotEnoughData" +}} + +Example 2: Book sales table without sample data +Input: +- Table name: my-table-2 +- Current description: Demo table with some sample data. +- Current tags: ['demo'] +- Column names: ['author', 'year', 'avg_rating', 'pages', 'genre', 'count'] +- Column Descriptions: ['Author that wrote the book', 'Year of publication', 'Average rating in Amazon.com', 'pages', 'genre', 'books sold in Amazon.com'] +- Sample data: None +- Generate columns metadata: False +- Requested fields: ["description", "tags"] + +Output: +{{ + "description": "Table tracking book sales performance with author information, publication year, ratings, page count, genre classification, and Amazon sales figures.", + "tags": ["books", "sales", "analytics"] +}} + +Example 3: Book sales table with sample data and column metadata +Input: +- Table name: my-table-2 +- Current description: Demo table with some sample data. +- Current tags: ['demo'] +- Column names: ['author', 'year', 'avg_rating_amzn', 'pages', 'genre', 'sales_amzn'] +- Column Descriptions: [] +- Sample data: {{"rows": ["[\"\\\"JK Rowling\\\"\", \"\\\"1995\\\"\", \"\\\"8\\\"\", \"\\\"419\\\"\", \"\\\"Science Fiction\\\"\", \"\\\"225890345\\\"\"]", [\"\\\"Gabriel Garcia Marquez\\\"\", \"\\\"1913\\\"\", \"\\\"9\\\"\", \"\\\"337\\\"\", \"\\\"Magical Realism\\\"\", \"\\\"221133\\\"\"]], "fields": [{{"name": "author"}}, {{"name": "year"}}, {{"name": "avg_rating"}}, {{"name": "pages"}}, {{"name": "genre"}}, {{"name": "count"}}], "__typename": "QueryPreviewResult"}} +- Generate columns metadata: True +- Requested fields: ["description", "tags", "subitem_descriptions"] + +Output: +{{ + "description": "Table tracking literary works by authors like JK Rowling and Gabriel Garcia Marquez, including publication years, Amazon ratings, page counts, genres, and sales figures.", + "tags": ["books", "sales", "ratings"], + "subitem_descriptions": [ + {{"label": "author", "description": "Name of the book's author (e.g., JK Rowling, Gabriel Garcia Marquez)"}}, + {{"label": "year", "description": "Year when the book was published (e.g., 1995, 1913)"}}, + {{"label": "avg_rating_amzn", "description": "Average customer rating on Amazon on a scale of 1-10"}}, + {{"label": "pages", "description": "Total number of pages in the book"}}, + {{"label": "genre", "description": "Literary category of the book (e.g., Science Fiction, Magical Realism)"}}, + {{"label": "sales_amzn", "description": "Number of copies sold on Amazon.com"}} + ] +}} diff --git a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py index 17e7eb733..9915b6e7e 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py @@ -1,5 +1,5 @@ from operator import or_ -from sqlalchemy import func, and_ +from sqlalchemy.orm import Query from dataall.base.db import paginate from dataall.base.db.exceptions import ObjectNotFound from dataall.modules.s3_datasets.db.dataset_models import DatasetTableColumn @@ -19,8 +19,8 @@ def save_and_commit(session, column: DatasetTableColumn): session.commit() @staticmethod - def paginate_active_columns_for_table(session, table_uri: str, filter: dict): - q = ( + def query_active_columns_for_table(session, table_uri: str) -> Query: + return ( session.query(DatasetTableColumn) .filter( DatasetTableColumn.tableUri == table_uri, @@ -29,6 +29,10 @@ def paginate_active_columns_for_table(session, table_uri: str, filter: dict): .order_by(DatasetTableColumn.columnType.asc()) ) + @staticmethod + def paginate_active_columns_for_table(session, table_uri: str, filter: dict): + q = DatasetColumnRepository.query_active_columns_for_table(session, table_uri) + if filter.get('filteredColumns'): q = q.filter(DatasetTableColumn.name.in_(filter['filteredColumns'])) @@ -44,28 +48,6 @@ def paginate_active_columns_for_table(session, table_uri: str, filter: dict): return paginate(query=q, page=filter.get('page', 1), page_size=filter.get('pageSize', 10)).to_dict() @staticmethod - def get_table_info_metadata_generation(session, table_uri: str): - result = ( - session.query( - DatasetTableColumn.GlueTableName, - DatasetTableColumn.AWSAccountId, - func.array_agg(DatasetTableColumn.description).label('description'), - func.array_agg(DatasetTableColumn.label).label('label'), - func.array_agg(DatasetTableColumn.columnUri).label('columnUri'), - ) - .filter(and_(DatasetTableColumn.tableUri == table_uri)) - .group_by(DatasetTableColumn.GlueTableName, DatasetTableColumn.AWSAccountId) - .first() - ) - return result - - @staticmethod - def query_active_columns_for_table(session, table_uri: str): - return ( - session.query(DatasetTableColumn) - .filter( - DatasetTableColumn.tableUri == table_uri, - DatasetTableColumn.deleted.is_(None), - ) - .order_by(DatasetTableColumn.columnType.asc()) - ) + def list_active_columns_for_table(session, table_uri: str): + q = DatasetColumnRepository.query_active_columns_for_table(session, table_uri) + return q.all() diff --git a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py index e69de29bb..2832554b7 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py @@ -0,0 +1,169 @@ +import logging + +from dataall.modules.s3_datasets.indexers.dataset_indexer import DatasetIndexer +from dataall.base.context import get_context +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService +from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService + +##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged +##from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService +from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository +from dataall.base.db.exceptions import ResourceAlreadyExists +from dataall.modules.s3_datasets.services.dataset_service import DatasetService +from dataall.modules.s3_datasets.aws.s3_location_client import S3LocationClient +from dataall.modules.s3_datasets.db.dataset_location_repositories import DatasetLocationRepository +from dataall.modules.s3_datasets.indexers.location_indexer import DatasetLocationIndexer +from dataall.modules.s3_datasets.services.dataset_permissions import ( + UPDATE_DATASET_FOLDER, + MANAGE_DATASETS, + CREATE_DATASET_FOLDER, + LIST_DATASET_FOLDERS, + DELETE_DATASET_FOLDER, +) +from dataall.modules.s3_datasets.services.dataset_permissions import DATASET_FOLDER_READ, GET_DATASET_FOLDER +from dataall.modules.s3_datasets.db.dataset_repositories import DatasetRepository +from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation, S3Dataset +from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient +from dataall.modules.s3_datasets.aws.s3_dataset_client import S3DatasetClient + +log = logging.getLogger(__name__) + + +class DatasetLocationService: + @staticmethod + def _get_dataset_uri(session, uri): + location = DatasetLocationRepository.get_location_by_uri(session, uri) + return location.datasetUri + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) + @ResourcePolicyService.has_resource_permission(CREATE_DATASET_FOLDER) + def create_storage_location(uri: str, data: dict): + with get_context().db_engine.scoped_session() as session: + exists = DatasetLocationRepository.exists(session, uri, data['prefix']) + + if exists: + raise ResourceAlreadyExists( + action='Create Folder', + message=f'Folder: {data["prefix"]} already exist on dataset {uri}', + ) + + dataset = DatasetRepository.get_dataset_by_uri(session, uri) + location = DatasetLocationRepository.create_dataset_location(session, dataset, data) + DatasetLocationService._attach_dataset_folder_read_permission(session, dataset, location.locationUri) + + if 'terms' in data.keys(): + DatasetLocationService._create_glossary_links(session, location, data['terms']) + + S3LocationClient(location, dataset).create_bucket_prefix() + + DatasetLocationIndexer.upsert(session=session, folder_uri=location.locationUri) + DatasetIndexer.upsert(session, dataset.datasetUri) + return location + + @staticmethod + @ResourcePolicyService.has_resource_permission(LIST_DATASET_FOLDERS) + def list_dataset_locations(uri: str, filter: dict = None): + with get_context().db_engine.scoped_session() as session: + return DatasetLocationRepository.list_dataset_locations(session=session, uri=uri, data=filter) + + @staticmethod + def get_storage_location(uri): + with get_context().db_engine.scoped_session() as session: + return DatasetLocationRepository.get_location_by_uri(session, uri) + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) + @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_FOLDER, parent_resource=_get_dataset_uri) + def update_storage_location(uri: str, data: dict): + with get_context().db_engine.scoped_session() as session: + location = DatasetLocationRepository.get_location_by_uri(session, uri) + for k in data.keys(): + setattr(location, k, data.get(k)) + + if 'terms' in data.keys(): + DatasetLocationService._create_glossary_links(session, location, data['terms']) + + DatasetLocationIndexer.upsert(session, folder_uri=location.locationUri) + DatasetIndexer.upsert(session, location.datasetUri) + + return location + + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) + @ResourcePolicyService.has_resource_permission(DELETE_DATASET_FOLDER, parent_resource=_get_dataset_uri) + def remove_storage_location(uri: str = None): + with get_context().db_engine.scoped_session() as session: + location = DatasetLocationRepository.get_location_by_uri(session, uri) + DatasetService.check_before_delete(session, location.locationUri, action=DELETE_DATASET_FOLDER) + DatasetService.execute_on_delete(session, location.locationUri, action=DELETE_DATASET_FOLDER) + dataset = DatasetRepository.get_dataset_by_uri(session, location.datasetUri) + DatasetLocationService._delete_dataset_folder_read_permission(session, dataset, location.locationUri) + DatasetLocationRepository.delete(session, location) + GlossaryRepository.delete_glossary_terms_links( + session, + target_uri=location.locationUri, + target_type='DatasetStorageLocation', + ) + DatasetLocationIndexer.delete_doc(doc_id=location.locationUri) + return True + + @staticmethod + def _create_glossary_links(session, location, terms): + GlossaryRepository.set_glossary_terms_links( + session, get_context().username, location.locationUri, 'Folder', terms + ) + + @staticmethod + def _attach_dataset_folder_read_permission(session, dataset: S3Dataset, location_uri): + """ + Attach Folder permissions to dataset groups + """ + permission_group = { + dataset.SamlAdminGroupName, + dataset.stewards if dataset.stewards is not None else dataset.SamlAdminGroupName, + } + for group in permission_group: + ResourcePolicyService.attach_resource_policy( + session=session, + group=group, + permissions=DATASET_FOLDER_READ, + resource_uri=location_uri, + resource_type=DatasetStorageLocation.__name__, + ) + + @staticmethod + def _delete_dataset_folder_read_permission(session, dataset: S3Dataset, location_uri): + """ + Delete Folder permissions to dataset groups + """ + permission_group = { + dataset.SamlAdminGroupName, + dataset.stewards if dataset.stewards is not None else dataset.SamlAdminGroupName, + } + for group in permission_group: + ResourcePolicyService.delete_resource_policy(session=session, group=group, resource_uri=location_uri) + + @staticmethod + @ResourcePolicyService.has_resource_permission(GET_DATASET_FOLDER) + def get_folder_restricted_information(uri: str, folder: DatasetStorageLocation): + context = get_context() + with context.db_engine.scoped_session() as session: + return DatasetRepository.get_dataset_by_uri(session, folder.datasetUri) + + @staticmethod + @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_FOLDER, parent_resource=_get_dataset_uri) + ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged + # @ResourceThresholdService.check_invocation_count( + # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' + # ) + def generate_metadata_for_folder(uri, metadata_types): + context = get_context() + with context.db_engine.scoped_session() as session: + folder = DatasetLocationRepository.get_location_by_uri(session, uri) + dataset = DatasetRepository.get_dataset_by_uri(session, folder.datasetUri) + files = S3DatasetClient(dataset).list_bucket_files(folder.S3BucketName, folder.S3Prefix) + metadata = BedrockClient().invoke_model_folder_metadata( + metadata_types=metadata_types, folder=folder, files=[f['Key'] for f in files] + ) + return [{'targetUri': uri, 'targetType': 'Folder', **metadata}] diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index 1088a066c..878e5d090 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -592,6 +592,7 @@ def list_dataset_tables_folders(uri, filter): @staticmethod @ResourcePolicyService.has_resource_permission(UPDATE_DATASET) + ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged # @ResourceThresholdService.check_invocation_count( # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' # ) @@ -607,4 +608,4 @@ def generate_metadata_for_dataset(uri, metadata_types): tables=tables, folders=folders, ) - return [{'targetUri': uri, 'targetType': 'S3_Dataset'} | metadata] + return [{'targetUri': uri, 'targetType': 'S3_Dataset', **metadata}] diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index a4012ffe0..78d4daade 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -117,6 +117,17 @@ def preview(uri: str): env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) return AthenaTableClient(env, table).get_table() + @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) + @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_TABLE, parent_resource=_get_dataset_uri) + def read_table_sample(uri: str): + context = get_context() + with context.db_engine.scoped_session() as session: + table: DatasetTable = DatasetTableRepository.get_dataset_table_by_uri(session, uri) + dataset = DatasetRepository.get_dataset_by_uri(session, table.datasetUri) + env = EnvironmentService.get_environment_by_uri(session, dataset.environmentUri) + return AthenaTableClient(env, table).get_table() + @staticmethod @ResourcePolicyService.has_resource_permission(GET_DATASET_TABLE) def get_glue_table_properties(uri: str): @@ -190,7 +201,8 @@ def _delete_dataset_table_read_permission(session, table_uri): ) @staticmethod - @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_TABLE) + @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_TABLE, parent_resource=_get_dataset_uri) + ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged # @ResourceThresholdService.check_invocation_count( # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' # ) @@ -198,14 +210,34 @@ def generate_metadata_for_table(uri, metadata_types, sample_data): context = get_context() with context.db_engine.scoped_session() as session: table = DatasetTableRepository.get_dataset_table_by_uri(session, uri) - table_columns = DatasetColumnRepository.get_table_info_metadata_generation(session, table.tableUri) + table_columns = DatasetColumnRepository.list_active_columns_for_table(session, table.tableUri) metadata = BedrockClient().invoke_model_table_metadata( table=table, columns=table_columns, metadata_types=metadata_types, sample_data=sample_data ) - columns_metadata = metadata.get('columns_metadata') - table_metadata = metadata.pop('columns_metadata') - - return [{'targetUri': uri, 'targetType': 'Table' | table_metadata}] + [ - {'targetUri': key, 'targetType': 'Table_Column', 'description': value} - for key, value in columns_metadata.items() - ] + columns_metadata = metadata.get('subitem_descriptions', []) + + # Create a copy of metadata without subitem_descriptions + table_metadata = {k: v for k, v in metadata.items() if k != 'subitem_descriptions'} + + result = [{'targetUri': uri, 'targetType': 'Table', **table_metadata}] + + # Add column metadata if available + if columns_metadata and isinstance(columns_metadata, list): + for item in columns_metadata: + # Find the column URI based on the label + column_uri = None + for col in table_columns: + if col.label == item.get('label'): + column_uri = col.columnUri + break + + if column_uri: + result.append( + { + 'targetUri': column_uri, + 'targetType': 'Table_Column', + 'description': item.get('description', ''), + } + ) + + return result diff --git a/backend/requirements.txt b/backend/requirements.txt index c66ca1836..f14589637 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ ariadne==0.17.0 aws-xray-sdk==2.4.3 -boto3==1.35.26 -fastapi == 0.115.5 +boto3==1.39.7 +fastapi == 0.116.1 nanoid==2.0.0 opensearch-py==1.0.0 PyAthena==2.3.0 @@ -13,5 +13,6 @@ requests_aws4auth==1.1.1 sqlalchemy==1.3.24 alembic==1.13.1 retrying==1.3.4 -langchain-aws==0.2.2 -langchain-core==0.3.11 +langchain-aws==0.2.28 +langchain-core==0.3.69 +pydantic==2.10.0 diff --git a/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js index a88cc5c1d..b55bad533 100644 --- a/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/GenerateMetadataComponent.js @@ -1,4 +1,4 @@ -import { useState, useCallback } from 'react'; +import { useState, useCallback, useEffect } from 'react'; import { Avatar, Box, @@ -12,6 +12,7 @@ import { FormLabel, Grid, InputLabel, + LinearProgress, MenuItem, Select, Switch, @@ -49,6 +50,8 @@ export const GenerateMetadataComponent = (props) => { const client = useClient(); const [loadingTableFolder, setLoadingTableFolder] = useState(false); const [tableFolderFilter, setTableFolderFilter] = useState(Defaults.filter); + const [generatingTargets, setGeneratingTargets] = useState(new Set()); + const [progress, setProgress] = useState(0); const handleChange = useCallback( async (event) => { setTargetType(event.target.value); @@ -84,9 +87,12 @@ export const GenerateMetadataComponent = (props) => { ); const handleMetadataChange = (event) => { + const { name, checked } = event.target; + + // Update the state with the new value setSelectedMetadataTypes({ ...selectedMetadataTypes, - [event.target.name]: event.target.checked + [name]: checked }); }; @@ -97,74 +103,150 @@ export const GenerateMetadataComponent = (props) => { } }; + // Uncheck Column Descriptions when no tables are selected + useEffect(() => { + const hasSelectedTables = targets.some( + (target) => target.targetType === 'Table' + ); + if (!hasSelectedTables && selectedMetadataTypes.columnDescriptions) { + setSelectedMetadataTypes({ + ...selectedMetadataTypes, + columnDescriptions: false + }); + } + }, [targets, selectedMetadataTypes]); + const generateMetadata = async () => { - for (let target of targets) { - let response = await client.mutate( - generateMetadataBedrock({ - resourceUri: target.targetUri, - targetType: target.targetType, - metadataTypes: Object.entries(selectedMetadataTypes) - .filter(([key, value]) => value === true) - .map(([key]) => key), - tableSampleData: {} - }) - ); - if (!response.errors) { - const matchingResponse = response.data.generateMetadata.find( - (item) => - item.targetUri === target.targetUri && - item.targetType === target.targetType - ); + try { + setProgress(0); + const totalTargets = targets.length; - if (matchingResponse) { - target.description = matchingResponse.description; - target.label = matchingResponse.label; - target.tags = matchingResponse.tags; - target.topics = matchingResponse.topics; - } - const hasNotEnoughData = [ - target.description, - target.label, - target.tags, - target.topics - ].some((value) => value === 'NotEnoughData'); + for (let i = 0; i < targets.length; i++) { + const target = targets[i]; + setGeneratingTargets((prev) => new Set([...prev, target.targetUri])); + + // Map columnDescriptions to topics for the API call if needed + const metadataTypesForApi = Object.entries(selectedMetadataTypes) + .filter(([key, value]) => value === true) + .map(([key]) => (key === 'columnDescriptions' ? 'topics' : key)); - if (hasNotEnoughData) { - enqueueSnackbar( - `Not enough data to generate metadata for ${target.name}`, - { - anchorOrigin: { - horizontal: 'right', - vertical: 'top' - }, - variant: 'warning' - } + let response = await client.mutate( + generateMetadataBedrock({ + resourceUri: target.targetUri, + targetType: target.targetType, + metadataTypes: metadataTypesForApi, + tableSampleData: {} + }) + ); + + if (!response.errors) { + const matchingResponse = response.data.generateMetadata.find( + (item) => + item.targetUri === target.targetUri && + item.targetType === target.targetType ); + + if (matchingResponse) { + target.description = matchingResponse.description; + target.label = matchingResponse.label; + target.tags = matchingResponse.tags; + target.topics = matchingResponse.topics; + } + const hasNotEnoughData = [ + target.description, + target.label, + target.tags, + target.topics + ].some((value) => value === 'NotEnoughData'); + + if (hasNotEnoughData) { + enqueueSnackbar( + `Not enough data to generate metadata for ${target.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'warning' + } + ); + } else { + enqueueSnackbar( + `Metadata generation is successful for ${target.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + } + ); + } } else { - enqueueSnackbar( - `Metadata generation is successful for ${target.name}`, - { - anchorOrigin: { - horizontal: 'right', - vertical: 'top' - }, - variant: 'success' - } - ); + target.description = 'Error'; + target.label = 'Error'; + target.tags = 'Error'; + target.topics = 'Error'; + dispatch({ + type: SET_ERROR, + error: response.errors[0].message + dataset.datasetUri + }); } - } else { - target.description = 'Error'; - target.label = 'Error'; - target.tags = 'Error'; - target.topics = 'Error'; - dispatch({ - type: SET_ERROR, - error: response.errors[0].message + dataset.datasetUri + + setGeneratingTargets((prev) => { + const newSet = new Set(prev); + newSet.delete(target.targetUri); + return newSet; }); + + // Update progress after each target is processed + setProgress(Math.round(((i + 1) / totalTargets) * 100)); } + setProgress(100); + setCurrentView('REVIEW_METADATA'); + } catch (error) { + dispatch({ + type: SET_ERROR, + error: error.message + }); + setGeneratingTargets(new Set()); // Clear generating state on error + setProgress(0); // Reset progress on error } - setCurrentView('REVIEW_METADATA'); }; + // If metadata is being generated, only show the progress bar + if (generatingTargets.size > 0) { + return ( + + + + + + + + + {`${progress}%`} + + + + + Generating metadata for {generatingTargets.size} of {targets.length}{' '} + targets + + + + ); + } + + // Normal UI when not generating metadata return ( <> { )} - + { name="label" checked={selectedMetadataTypes.label} onChange={handleMetadataChange} + disabled={ + targets.length > 0 && + targets.every((target) => target.targetType === 'Table') + } /> } label="Label" @@ -355,23 +441,34 @@ export const GenerateMetadataComponent = (props) => { label="Tags" /> {targetType !== 'Dataset' && ( - - } - label="Subitem Descriptions" - /> + <> + target.targetType === 'Table' + ) + } + /> + } + label="Column Descriptions (Tables)" + /> + )} diff --git a/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js index 8f9647315..788f1a201 100644 --- a/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js +++ b/frontend/src/modules/S3_Datasets/components/MetadataMainModal.js @@ -57,6 +57,7 @@ export const MetadataMainModal = (props) => { targets={targets} setTargets={setTargets} selectedMetadataTypes={selectedMetadataTypes} + onClose={onClose} /> )} diff --git a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js index 54012994b..8804bdb07 100644 --- a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js @@ -1,9 +1,17 @@ -import React, { useState } from 'react'; -import { Button, Box, Chip, Typography, CircularProgress } from '@mui/material'; +import React, { useState, useEffect } from 'react'; +import { + Button, + Box, + Chip, + Typography, + CircularProgress, + Backdrop +} from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import { useSnackbar } from 'notistack'; import PropTypes from 'prop-types'; import AutoModeIcon from '@mui/icons-material/AutoMode'; +import SaveIcon from '@mui/icons-material/Save'; import { Scrollbar } from 'design'; import { SET_ERROR, useDispatch } from 'globalErrors'; import { useClient } from 'services'; @@ -11,7 +19,7 @@ import { updateDatasetTable } from 'modules/Tables/services'; import { updateDatasetStorageLocation } from 'modules/Folders/services'; import { BatchUpdateDatasetTableColumn, - listSampleData, + listTableSampleData, updateDataset, generateMetadataBedrock } from '../services'; @@ -19,17 +27,24 @@ import SampleDataPopup from './SampleDataPopup'; import SubitemDescriptionsGrid from './SubitemDescriptionsGrid'; export const ReviewMetadataComponent = (props) => { - const { dataset, targets, setTargets, selectedMetadataTypes } = props; - /* eslint-disable no-console */ - console.log(targets); + const { dataset, targets, setTargets, selectedMetadataTypes, onClose } = + props; const { enqueueSnackbar } = useSnackbar(); const dispatch = useDispatch(); const client = useClient(); const [popupOpen, setPopupOpen] = useState(false); const [sampleData, setSampleData] = useState(null); const [targetUri, setTargetUri] = useState(null); - const [showPopup, setShowPopup] = React.useState(false); - const [subitemDescriptions, setSubitemDescriptions] = React.useState([]); + const [showPopup, setShowPopup] = useState(false); + const [subitemDescriptions, setSubitemDescriptions] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [generatingTargets, setGeneratingTargets] = useState(new Set()); + const [loadingSampleData, setLoadingSampleData] = useState(false); + + // Check for errors in targets data when component mounts or targets change + useEffect(() => { + // This effect is no longer needed + }, [targets]); const showSubItemsPopup = (subitemDescriptions) => { setSubitemDescriptions(subitemDescriptions); @@ -72,14 +87,15 @@ export const ReviewMetadataComponent = (props) => { async function handleRegenerate(table) { try { + setLoadingSampleData(true); const response = await client.query( - listSampleData({ + listTableSampleData({ tableUri: table.targetUri }) ); - openSampleDataPopup(response.data.listSampleData); - setTargetUri(table.targetUri); if (!response.errors) { + openSampleDataPopup(response.data.listTableSampleData); + setTargetUri(table.targetUri); enqueueSnackbar('Successfully read sample data', { variant: 'success' }); @@ -88,34 +104,36 @@ export const ReviewMetadataComponent = (props) => { } } catch (err) { dispatch({ type: SET_ERROR, error: err.message }); + } finally { + setLoadingSampleData(false); } } - const handleAcceptAndRegenerate = async () => { + + async function handleRegenerateMetadata(target) { try { - const targetIndex = targets.findIndex((t) => t.targetUri === targetUri); - if (targetIndex !== -1) { - const { __typename, ...sampleDataWithoutTypename } = sampleData; - const response = await client.mutate( - generateMetadataBedrock({ - resourceUri: targets[targetIndex].targetUri, - targetType: targets[targetIndex].targetType, - metadataTypes: Object.entries(selectedMetadataTypes) - .filter(([key, value]) => value === true) - .map(([key]) => key), - sampleData: sampleDataWithoutTypename - }) - ); + setGeneratingTargets((prev) => new Set([...prev, target.targetUri])); + const response = await client.mutate( + generateMetadataBedrock({ + resourceUri: target.targetUri, + targetType: target.targetType, + metadataTypes: Object.entries(selectedMetadataTypes) + .filter(([key, value]) => value === true) + .map(([key]) => key) + }) + ); - if (!response.errors) { + if (!response.errors) { + const targetIndex = targets.findIndex( + (t) => t.targetUri === target.targetUri + ); + if (targetIndex !== -1) { const updatedTarget = { ...targets[targetIndex], - description: response.data.generateMetadata.description, - label: response.data.generateMetadata.label, - name: response.data.generateMetadata.name, - tags: response.data.generateMetadata.tags, - topics: response.data.generateMetadata.topics, - subitem_descriptions: - response.data.generateMetadata.subitem_descriptions + description: response.data.generateMetadata[0].description, + label: response.data.generateMetadata[0].label, + tags: response.data.generateMetadata[0].tags, + topics: response.data.generateMetadata[0].topics, + subitem_descriptions: response.data.generateMetadata[0].topics || [] }; const updatedTargets = [...targets]; @@ -135,7 +153,23 @@ export const ReviewMetadataComponent = (props) => { ); } } else { - console.error(`Target with targetUri not found`); + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + dispatch({ type: SET_ERROR, error: err.message }); + } finally { + setGeneratingTargets((prev) => { + const newSet = new Set(prev); + newSet.delete(target.targetUri); + return newSet; + }); + } + } + const handleAcceptAndRegenerate = async () => { + try { + const target = targets.find((t) => t.targetUri === targetUri); + if (!target) { + console.error(`Target with targetUri ${targetUri} not found`); enqueueSnackbar(`Metadata generation is unsuccessful`, { anchorOrigin: { horizontal: 'right', @@ -143,6 +177,50 @@ export const ReviewMetadataComponent = (props) => { }, variant: 'error' }); + return; + } + const { __typename, ...sampleDataWithoutTypename } = sampleData; + const response = await client.mutate( + generateMetadataBedrock({ + resourceUri: target.targetUri, + targetType: target.targetType, + metadataTypes: Object.entries(selectedMetadataTypes) + .filter(([key, value]) => value === true) + .map(([key]) => key), + sampleData: sampleDataWithoutTypename + }) + ); + + if (!response.errors) { + const targetIndex = targets.findIndex((t) => t.targetUri === targetUri); + if (targetIndex !== -1) { + const updatedTarget = { + ...targets[targetIndex], + description: response.data.generateMetadata[0].description, + label: response.data.generateMetadata[0].label, + tags: response.data.generateMetadata[0].tags, + topics: response.data.generateMetadata[0].topics, + subitem_descriptions: response.data.generateMetadata[0].topics || [] + }; + + const updatedTargets = [...targets]; + updatedTargets[targetIndex] = updatedTarget; + + setTargets(updatedTargets); + + enqueueSnackbar( + `Metadata generation is successful for ${updatedTarget.name}`, + { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + } + ); + } + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); } closeSampleDataPopup(); @@ -164,7 +242,7 @@ export const ReviewMetadataComponent = (props) => { } ); if (target.targetType === 'S3_Dataset') { - updatedMetadata.KmsAlias = dataset.KmsAlias; + updatedMetadata.KmsAlias = dataset.restricted.KmsAlias; const response = await client.mutate( updateDataset({ datasetUri: target.targetUri, @@ -229,6 +307,11 @@ export const ReviewMetadataComponent = (props) => { variant: 'success' } ); + + // Close the modal when any update is successful + if (onClose) { + onClose(); + } } if (failedTargets.length > 0) { @@ -248,14 +331,56 @@ export const ReviewMetadataComponent = (props) => { return ( <> {Array.isArray(targets) && targets.length > 0 ? ( - + + + + - + node.targetUri} - rowHeight={80} + getRowHeight={() => 'auto'} + getEstimatedRowHeight={() => 100} + checkboxSelection + onSelectionModelChange={(newSelectionModel) => { + setSelectedRows(newSelectionModel); + }} + selectionModel={selectedRows} + isRowSelectable={(params) => { + const metadataFields = [ + 'label', + 'description', + 'tags', + 'topics' + ]; + return !metadataFields.some((field) => { + const value = params.row[field]; + return ( + value === 'Error' || + value === 'NotEnoughData' || + (Array.isArray(value) && value.includes('Error')) || + (Array.isArray(value) && value.includes('NotEnoughData')) + ); + }); + }} columns={[ { field: 'targetUri', hide: true }, { @@ -276,9 +401,11 @@ export const ReviewMetadataComponent = (props) => { flex: 2, editable: true, renderCell: (params) => - params.value === undefined ? ( - - ) : ['NotEnoughData', 'Error'].includes(params.value) ? ( + generatingTargets.has(params.row.targetUri) ? ( + + ) : params.value === 'NotEnoughData' ? ( + + ) : params.value === 'Error' ? ( ) : (
@@ -292,9 +419,11 @@ export const ReviewMetadataComponent = (props) => { flex: 3, editable: true, renderCell: (params) => - params.value === undefined ? ( - - ) : ['NotEnoughData', 'Error'].includes(params.value) ? ( + generatingTargets.has(params.row.targetUri) ? ( + + ) : params.value === 'NotEnoughData' ? ( + + ) : params.value === 'Error' ? ( ) : (
@@ -316,10 +445,28 @@ export const ReviewMetadataComponent = (props) => { return { ...row, tags }; }, renderCell: (params) => - params.value === undefined ? ( - - ) : ['NotEnoughData', 'Error'].includes(params.value) ? ( - + generatingTargets.has(params.row.targetUri) ? ( + + ) : params.value === 'NotEnoughData' || + (Array.isArray(params.value) && + params.value.includes('NotEnoughData')) ? ( + + ) : params.value === 'Error' || + (Array.isArray(params.value) && + params.value.includes('Error')) ? ( + ) : (
{Array.isArray(params.value) @@ -334,9 +481,11 @@ export const ReviewMetadataComponent = (props) => { flex: 2, editable: true, renderCell: (params) => - params.value === undefined ? ( - - ) : ['NotEnoughData', 'Error'].includes(params.value) ? ( + generatingTargets.has(params.row.targetUri) ? ( + + ) : params.value === 'NotEnoughData' ? ( + + ) : params.value === 'Error' ? ( ) : (
@@ -353,7 +502,7 @@ export const ReviewMetadataComponent = (props) => { params.value === undefined ? ( ) : params.value[0] === 'NotEnoughData' ? ( - + ) : ( - ) : ( - '-' - ) + {params.row.targetType === 'Table' && ( + + )} + + ) } ]} columnVisibilityModel={{ @@ -404,11 +591,7 @@ export const ReviewMetadataComponent = (props) => { topics: selectedMetadataTypes['topics'] ? selectedMetadataTypes['topics'] : false, - subitem_descriptions: selectedMetadataTypes[ - 'subitem_descriptions' - ] - ? selectedMetadataTypes['subitem_descriptions'] - : false + subitem_descriptions: false }} pageSize={10} rowsPerPageOptions={[5, 10, 20]} @@ -434,7 +617,17 @@ export const ReviewMetadataComponent = (props) => { borderBottom: '1px solid rgba(145, 158, 171, 0.24)' }, '& .MuiDataGrid-columnHeaders': { - borderBottom: 0.5 + borderBottom: 0.5, + padding: '0 8px' + }, + '& .MuiDataGrid-cell': { + whiteSpace: 'normal !important', + wordBreak: 'break-word', + padding: '16px 8px', + lineHeight: 1.5 + }, + '& .MuiDataGrid-main': { + margin: '0 8px' } }} /> @@ -451,21 +644,16 @@ export const ReviewMetadataComponent = (props) => { onSave={handleSaveSubitemDescriptions} /> )} - + + {loadingSampleData && ( + theme.zIndex.drawer + 1 }} + open={loadingSampleData} + > + + + )} + { ReviewMetadataComponent.propTypes = { dataset: PropTypes.object.isRequired, - targetType: PropTypes.string.isRequired, targets: PropTypes.array.isRequired, setTargets: PropTypes.func.isRequired, selectedMetadataTypes: PropTypes.object.isRequired diff --git a/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js b/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js index a271db9c7..a7a31d8fa 100644 --- a/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js +++ b/frontend/src/modules/S3_Datasets/components/SampleDataPopup.js @@ -22,10 +22,27 @@ const SampleDataPopup = ({ }} > - - By clicking the button below, you agree to share this sample data with - a third-party language model. - + + + + By clicking the button below, you agree to share this sample data + with a third-party language model. + + +
) }, - { - field: 'subitem_descriptions', - headerName: 'Subitem Descriptions', - flex: 3, - editable: false, - renderCell: (params) => - params.value === undefined ? ( - - ) : params.value[0] === 'NotEnoughData' ? ( - - ) : ( - - ) - }, { field: 'actions', headerName: 'Actions', @@ -590,8 +531,7 @@ export const ReviewMetadataComponent = (props) => { : false, topics: selectedMetadataTypes['topics'] ? selectedMetadataTypes['topics'] - : false, - subitem_descriptions: false + : false }} pageSize={10} rowsPerPageOptions={[5, 10, 20]} @@ -637,13 +577,6 @@ export const ReviewMetadataComponent = (props) => { ) : ( No metadata available )} - {showPopup && ( - - )} {loadingSampleData && ( ({ - variables: { - columns - }, - mutation: gql` - mutation BatchUpdateDatasetTableColumn( - $columns: [SubitemDescriptionInput] - ) { - batchUpdateDatasetTableColumn(columns: $columns) - } - ` -}); From ba78df7e9c2b941e8b4eed32ff92c042834e159a Mon Sep 17 00:00:00 2001 From: dlpzx Date: Tue, 22 Jul 2025 14:43:39 +0200 Subject: [PATCH 11/22] GenAI metadata generation - improvements --- .../s3_datasets/api/dataset/resolvers.py | 90 +++++ .../modules/s3_datasets/api/table/queries.py | 4 +- .../s3_datasets/services/dataset_service.py | 1 + .../components/ReviewMetadataComponent.js | 8 +- .../src/modules/S3_Datasets/services/index.js | 3 +- .../services/listTableSampleData.js | 15 - .../services/readTableSampleData.js | 15 + .../s3_datasets/test_generate_metadata.py | 324 ++++++++++++++++++ .../test_list_dataset_tables_folders.py | 171 +++++++++ tests/permissions.py | 5 + tests/test_permissions.py | 4 + 11 files changed, 617 insertions(+), 23 deletions(-) delete mode 100644 frontend/src/modules/S3_Datasets/services/listTableSampleData.js create mode 100644 frontend/src/modules/S3_Datasets/services/readTableSampleData.js create mode 100644 tests/modules/s3_datasets/test_generate_metadata.py create mode 100644 tests/modules/s3_datasets/test_list_dataset_tables_folders.py diff --git a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py index e131f27fd..3fb1b40c5 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py @@ -175,6 +175,7 @@ def generate_metadata( tableSampleData: dict = {}, ): RequestValidator.validate_uri(param_name='resourceUri', param_value=resourceUri) + RequestValidator.validate_table_sample_data(tableSampleData) if any(metadata_type not in [item.value for item in MetadataGenerationTypes] for metadata_type in metadataTypes): raise InvalidInput( 'metadataType', @@ -264,3 +265,92 @@ def validate_import_request(data): RequestValidator.validate_creation_request(data) if not data.get('bucketName'): raise RequiredParameter('bucketName') + + @staticmethod + def validate_table_sample_data(table_sample_data: dict): + """ + Validates tableSampleData parameter structure to match readTableSampleData API output. + Expected structure: + { + "fields": [JSON string objects with "name" property], + "rows": [JSON array strings matching field count] + } + """ + if not table_sample_data: + return # Empty dict is allowed as default parameter + + if not isinstance(table_sample_data, dict): + raise InvalidInput( + param_name='tableSampleData', + param_value=str(table_sample_data), + constraint='must be a dictionary object', + ) + + # Validate fields array + fields = table_sample_data.get('fields') + if fields is not None: + if not isinstance(fields, list): + raise InvalidInput( + param_name='tableSampleData.fields', param_value=str(fields), constraint='must be an array' + ) + + for i, field in enumerate(fields): + if not isinstance(field, str): + raise InvalidInput( + param_name=f'tableSampleData.fields[{i}]', + param_value=str(field), + constraint='must be a JSON string', + ) + + try: + import json + + field_obj = json.loads(field) + if not isinstance(field_obj, dict) or 'name' not in field_obj: + raise InvalidInput( + param_name=f'tableSampleData.fields[{i}]', + param_value=field, + constraint='must be a JSON object with "name" property', + ) + except json.JSONDecodeError: + raise InvalidInput( + param_name=f'tableSampleData.fields[{i}]', param_value=field, constraint='must be valid JSON' + ) + + # Validate rows array + rows = table_sample_data.get('rows') + if rows is not None: + if not isinstance(rows, list): + raise InvalidInput( + param_name='tableSampleData.rows', param_value=str(rows), constraint='must be an array' + ) + + expected_field_count = len(fields) if fields else 0 + + for i, row in enumerate(rows): + if not isinstance(row, str): + raise InvalidInput( + param_name=f'tableSampleData.rows[{i}]', + param_value=str(row), + constraint='must be a JSON string', + ) + + try: + import json + + row_array = json.loads(row) + if not isinstance(row_array, list): + raise InvalidInput( + param_name=f'tableSampleData.rows[{i}]', param_value=row, constraint='must be a JSON array' + ) + + if expected_field_count > 0 and len(row_array) != expected_field_count: + raise InvalidInput( + param_name=f'tableSampleData.rows[{i}]', + param_value=row, + constraint=f'must contain {expected_field_count} elements to match fields count', + ) + except json.JSONDecodeError: + raise InvalidInput( + param_name=f'tableSampleData.rows[{i}]', param_value=row, constraint='must be valid JSON' + ) diff --git a/backend/dataall/modules/s3_datasets/api/table/queries.py b/backend/dataall/modules/s3_datasets/api/table/queries.py index fc207a65f..314e1e339 100644 --- a/backend/dataall/modules/s3_datasets/api/table/queries.py +++ b/backend/dataall/modules/s3_datasets/api/table/queries.py @@ -43,8 +43,8 @@ type=gql.Ref('QueryPreviewResult'), ) -listTableSampleData = gql.QueryField( - name='listTableSampleData', +readTableSampleData = gql.QueryField( + name='readTableSampleData', args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], type=gql.Ref('QueryPreviewResult'), resolver=read_sample_data, diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index 878e5d090..ea75e3dc6 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -591,6 +591,7 @@ def list_dataset_tables_folders(uri, filter): return DatasetRepository.paginated_dataset_tables_folders(session, uri, filter) @staticmethod + @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) @ResourcePolicyService.has_resource_permission(UPDATE_DATASET) ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged # @ResourceThresholdService.check_invocation_count( diff --git a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js index c34afe780..8ee78d131 100644 --- a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js @@ -18,7 +18,7 @@ import { useClient } from 'services'; import { updateDatasetTable } from 'modules/Tables/services'; import { updateDatasetStorageLocation } from 'modules/Folders/services'; import { - listTableSampleData, + readTableSampleData, updateDataset, generateMetadataBedrock } from '../services'; @@ -55,12 +55,12 @@ export const ReviewMetadataComponent = (props) => { try { setLoadingSampleData(true); const response = await client.query( - listTableSampleData({ + readTableSampleData({ tableUri: table.targetUri }) ); if (!response.errors) { - openSampleDataPopup(response.data.listTableSampleData); + openSampleDataPopup(response.data.readTableSampleData); setTargetUri(table.targetUri); enqueueSnackbar('Successfully read sample data', { variant: 'success' @@ -152,7 +152,7 @@ export const ReviewMetadataComponent = (props) => { metadataTypes: Object.entries(selectedMetadataTypes) .filter(([key, value]) => value === true) .map(([key]) => key), - sampleData: sampleDataWithoutTypename + tableSampleData: sampleDataWithoutTypename }) ); diff --git a/frontend/src/modules/S3_Datasets/services/index.js b/frontend/src/modules/S3_Datasets/services/index.js index ac6e1fa58..db1a87f09 100644 --- a/frontend/src/modules/S3_Datasets/services/index.js +++ b/frontend/src/modules/S3_Datasets/services/index.js @@ -1,4 +1,3 @@ -export * from './batchUpdateTableColumnDescriptions'; export * from './createDataset'; export * from './deleteDataset'; export * from './generateDatasetAccessToken'; @@ -7,7 +6,7 @@ export * from './getDatasetPresignedUrl'; export * from './importDataset'; export * from './listDatasetStorageLocations'; export * from './listDatasetTablesFolders'; -export * from './listTableSampleData'; +export * from './readTableSampleData'; export * from './startGlueCrawler'; export * from './syncTables'; export * from './updateDataset'; diff --git a/frontend/src/modules/S3_Datasets/services/listTableSampleData.js b/frontend/src/modules/S3_Datasets/services/listTableSampleData.js deleted file mode 100644 index ee4d400c5..000000000 --- a/frontend/src/modules/S3_Datasets/services/listTableSampleData.js +++ /dev/null @@ -1,15 +0,0 @@ -import { gql } from 'apollo-boost'; - -export const listTableSampleData = ({ tableUri }) => ({ - variables: { - tableUri - }, - query: gql` - query listTableSampleData($tableUri: String!) { - listTableSampleData(tableUri: $tableUri) { - fields - rows - } - } - ` -}); diff --git a/frontend/src/modules/S3_Datasets/services/readTableSampleData.js b/frontend/src/modules/S3_Datasets/services/readTableSampleData.js new file mode 100644 index 000000000..5dedd7f09 --- /dev/null +++ b/frontend/src/modules/S3_Datasets/services/readTableSampleData.js @@ -0,0 +1,15 @@ +import { gql } from 'apollo-boost'; + +export const readTableSampleData = ({ tableUri }) => ({ + variables: { + tableUri + }, + query: gql` + query readTableSampleData($tableUri: String!) { + readTableSampleData(tableUri: $tableUri) { + fields + rows + } + } + ` +}); diff --git a/tests/modules/s3_datasets/test_generate_metadata.py b/tests/modules/s3_datasets/test_generate_metadata.py new file mode 100644 index 000000000..00b440526 --- /dev/null +++ b/tests/modules/s3_datasets/test_generate_metadata.py @@ -0,0 +1,324 @@ +import pytest +from unittest.mock import MagicMock, patch + +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTargets + + +@pytest.fixture(scope='function') +def mock_bedrock_client(mocker): + mock_client = MagicMock() + mocker.patch('dataall.modules.s3_datasets.services.dataset_service.BedrockClient', return_value=mock_client) + mocker.patch('dataall.modules.s3_datasets.services.dataset_table_service.BedrockClient', return_value=mock_client) + mocker.patch( + 'dataall.modules.s3_datasets.services.dataset_location_service.BedrockClient', return_value=mock_client + ) + mock_client.invoke_model_dataset_metadata.return_value = { + 'description': 'Test dataset description', + 'label': 'Test Dataset Label', + 'tags': ['tag1', 'tag2'], + 'topics': ['Finances', 'Products'], + } + mock_client.invoke_model_table_metadata.return_value = { + 'description': 'Test table description', + 'tags': ['table_tag1', 'table_tag2'], + } + mock_client.invoke_model_folder_metadata.return_value = { + 'description': 'Test folder description', + 'label': 'Test Folder Label', + 'tags': ['folder_tag1', 'folder_tag2'], + } + return mock_client + + +@pytest.fixture(scope='function') +def mock_s3_client(mocker): + mock_client = MagicMock() + mocker.patch( + 'dataall.modules.s3_datasets.services.dataset_location_service.S3DatasetClient', return_value=mock_client + ) + mock_client.list_bucket_files.return_value = [{'Key': 'file1.csv'}, {'Key': 'file2.json'}] + return mock_client + + +def test_generate_metadata_for_dataset(client, dataset_fixture, mock_bedrock_client, group): + """Test generating metadata for a dataset""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + label + description + tags + topics + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + resourceUri=dataset_fixture.datasetUri, + targetType=MetadataGenerationTargets.S3_Dataset.value, + metadataTypes=['description', 'label', 'tags', 'topics'], + ) + + assert not response.errors + assert response.data.generateMetadata[0].targetUri == dataset_fixture.datasetUri + assert response.data.generateMetadata[0].targetType == 'S3_Dataset' + assert response.data.generateMetadata[0].label == 'Test Dataset Label' + assert response.data.generateMetadata[0].description == 'Test dataset description' + assert 'tag1' in response.data.generateMetadata[0].tags + assert 'tag2' in response.data.generateMetadata[0].tags + assert 'Finances' in response.data.generateMetadata[0].topics + assert 'Products' in response.data.generateMetadata[0].topics + + # Verify BedrockClient was called with correct parameters + mock_bedrock_client.invoke_model_dataset_metadata.assert_called_once() + args, kwargs = mock_bedrock_client.invoke_model_dataset_metadata.call_args + assert kwargs['metadata_types'] == ['description', 'label', 'tags', 'topics'] + assert kwargs['dataset'] is not None + + +def test_generate_metadata_for_table(client, table_fixture, mock_bedrock_client, group): + """Test generating metadata for a table""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + description + tags + } + } + """, + username=table_fixture.owner, + groups=[group.name], + resourceUri=table_fixture.tableUri, + targetType=MetadataGenerationTargets.Table.value, + metadataTypes=['description', 'tags'], + ) + + assert not response.errors + assert response.data.generateMetadata[0].targetUri == table_fixture.tableUri + assert response.data.generateMetadata[0].targetType == 'Table' + assert response.data.generateMetadata[0].description == 'Test table description' + assert 'table_tag1' in response.data.generateMetadata[0].tags + assert 'table_tag2' in response.data.generateMetadata[0].tags + + # Verify BedrockClient was called with correct parameters + mock_bedrock_client.invoke_model_table_metadata.assert_called_once() + args, kwargs = mock_bedrock_client.invoke_model_table_metadata.call_args + assert kwargs['metadata_types'] == ['description', 'tags'] + assert kwargs['table'] is not None + + +def test_generate_metadata_for_table_with_sample_data(client, table_fixture, mock_bedrock_client, group): + """Test generating metadata for a table with sample data""" + sample_data = {'fields': ['{"name": "id"}', '{"name": "name"}'], 'rows': ['["1", "John"]', '["2", "Jane"]']} + + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]!, + $tableSampleData: TableSampleData + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes, + tableSampleData: $tableSampleData + ) { + targetUri + targetType + description + tags + } + } + """, + username=table_fixture.owner, + groups=[group.name], + resourceUri=table_fixture.tableUri, + targetType=MetadataGenerationTargets.Table.value, + metadataTypes=['description', 'tags'], + tableSampleData=sample_data, + ) + + assert not response.errors + assert response.data.generateMetadata[0].targetUri == table_fixture.tableUri + assert response.data.generateMetadata[0].targetType == 'Table' + + # Verify BedrockClient was called with correct parameters + mock_bedrock_client.invoke_model_table_metadata.assert_called() + args, kwargs = mock_bedrock_client.invoke_model_table_metadata.call_args + assert kwargs['metadata_types'] == ['description', 'tags'] + assert kwargs['table'] is not None + assert kwargs['sample_data'] == sample_data + + +def test_generate_metadata_for_folder( + client, folder_fixture, mock_bedrock_client, mock_s3_client, group, dataset_fixture +): + """Test generating metadata for a folder""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + label + description + tags + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + resourceUri=folder_fixture.locationUri, + targetType=MetadataGenerationTargets.Folder.value, + metadataTypes=['description', 'label', 'tags'], + ) + + assert not response.errors + assert response.data.generateMetadata[0].targetUri == folder_fixture.locationUri + assert response.data.generateMetadata[0].targetType == 'Folder' + assert response.data.generateMetadata[0].label == 'Test Folder Label' + assert response.data.generateMetadata[0].description == 'Test folder description' + assert 'folder_tag1' in response.data.generateMetadata[0].tags + assert 'folder_tag2' in response.data.generateMetadata[0].tags + + # Verify BedrockClient was called with correct parameters + mock_bedrock_client.invoke_model_folder_metadata.assert_called_once() + args, kwargs = mock_bedrock_client.invoke_model_folder_metadata.call_args + assert kwargs['metadata_types'] == ['description', 'label', 'tags'] + assert kwargs['folder'] is not None + + # Verify S3DatasetClient was called to list files + mock_s3_client.list_bucket_files.assert_called_once() + + +def test_generate_metadata_invalid_target_type(client, dataset_fixture, mock_bedrock_client, group): + """Test generating metadata with invalid target type""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + label + description + tags + topics + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + resourceUri=dataset_fixture.datasetUri, + targetType='InvalidType', + metadataTypes=['description', 'label', 'tags', 'topics'], + ) + + assert response.errors + assert 'InvalidType' in response.errors[0].message + + +def test_generate_metadata_invalid_metadata_type(client, dataset_fixture, mock_bedrock_client, group): + """Test generating metadata with invalid metadata type""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + label + description + tags + topics + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + resourceUri=dataset_fixture.datasetUri, + targetType=MetadataGenerationTargets.S3_Dataset.value, + metadataTypes=['description', 'invalid_type'], + ) + + assert response.errors + assert 'metadataType' in response.errors[0].message + + +def test_generate_metadata_unauthorized(client, dataset_fixture, mock_bedrock_client): + """Test generating metadata with unauthorized user""" + response = client.query( + """ + mutation generateMetadata( + $resourceUri: String!, + $targetType: MetadataGenerationTargets!, + $metadataTypes: [String]! + ) { + generateMetadata( + resourceUri: $resourceUri, + targetType: $targetType, + metadataTypes: $metadataTypes + ) { + targetUri + targetType + label + description + tags + topics + } + } + """, + username='unauthorized_user', + resourceUri=dataset_fixture.datasetUri, + targetType=MetadataGenerationTargets.S3_Dataset.value, + metadataTypes=['description', 'label', 'tags', 'topics'], + ) + + assert response.errors + assert 'UnauthorizedOperation' in response.errors[0].message diff --git a/tests/modules/s3_datasets/test_list_dataset_tables_folders.py b/tests/modules/s3_datasets/test_list_dataset_tables_folders.py new file mode 100644 index 000000000..ccf19b7c3 --- /dev/null +++ b/tests/modules/s3_datasets/test_list_dataset_tables_folders.py @@ -0,0 +1,171 @@ +import pytest +from unittest.mock import MagicMock + +from dataall.modules.s3_datasets.db.dataset_models import DatasetTable, DatasetStorageLocation + + +def test_list_dataset_tables_folders_basic(client, dataset_fixture, table, location): + """Test basic listing of dataset tables and folders""" + # Create some test tables and folders + table1 = table(dataset=dataset_fixture, name='test_table_1', username=dataset_fixture.owner) + table2 = table(dataset=dataset_fixture, name='test_table_2', username=dataset_fixture.owner) + location1 = location(dataset=dataset_fixture, name='test_folder_1', username=dataset_fixture.owner) + location2 = location(dataset=dataset_fixture, name='test_folder_2', username=dataset_fixture.owner) + + response = client.query( + """ + query listDatasetTablesFolders( + $datasetUri: String!, + $filter: DatasetFilter + ) { + listDatasetTablesFolders( + datasetUri: $datasetUri, + filter: $filter + ) { + count + page + pages + hasNext + hasPrevious + nodes { + name + targetType + targetUri + } + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + datasetUri=dataset_fixture.datasetUri, + filter={}, + ) + + assert not response.errors + assert response.data.listDatasetTablesFolders.count >= 4 # At least our 2 tables + 2 folders + + # Check that we have both tables and folders in the results + nodes = response.data.listDatasetTablesFolders.nodes + table_nodes = [node for node in nodes if node.targetType == 'Table'] + folder_nodes = [node for node in nodes if node.targetType == 'Folder'] + + assert len(table_nodes) >= 2 + assert len(folder_nodes) >= 2 + + # Verify specific items are present + table_names = [node.name for node in table_nodes] + folder_names = [node.name for node in folder_nodes] + + assert 'test_table_1' in table_names + assert 'test_table_2' in table_names + assert 'test_folder_1' in folder_names + assert 'test_folder_2' in folder_names + + +def test_list_dataset_tables_folders_with_pagination(client, dataset_fixture, table, location): + """Test listing with pagination""" + # Create multiple items to test pagination + for i in range(5): + table(dataset=dataset_fixture, name=f'pagination_table_{i}', username=dataset_fixture.owner) + location(dataset=dataset_fixture, name=f'pagination_folder_{i}', username=dataset_fixture.owner) + + # Test first page with page size 3 + response = client.query( + """ + query listDatasetTablesFolders( + $datasetUri: String!, + $filter: DatasetFilter + ) { + listDatasetTablesFolders( + datasetUri: $datasetUri, + filter: $filter + ) { + count + page + pages + hasNext + hasPrevious + nodes { + name + targetType + targetUri + } + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + datasetUri=dataset_fixture.datasetUri, + filter={'page': 1, 'pageSize': 3}, + ) + + assert not response.errors + assert response.data.listDatasetTablesFolders.page == 1 + assert len(response.data.listDatasetTablesFolders.nodes) == 3 + assert response.data.listDatasetTablesFolders.hasNext is True + assert response.data.listDatasetTablesFolders.hasPrevious is False + + # Test second page + response = client.query( + """ + query listDatasetTablesFolders( + $datasetUri: String!, + $filter: DatasetFilter + ) { + listDatasetTablesFolders( + datasetUri: $datasetUri, + filter: $filter + ) { + count + page + pages + hasNext + hasPrevious + nodes { + name + targetType + targetUri + } + } + } + """, + username=dataset_fixture.owner, + groups=[dataset_fixture.SamlAdminGroupName], + datasetUri=dataset_fixture.datasetUri, + filter={'page': 2, 'pageSize': 3}, + ) + + assert not response.errors + assert response.data.listDatasetTablesFolders.page == 2 + assert len(response.data.listDatasetTablesFolders.nodes) == 3 + assert response.data.listDatasetTablesFolders.hasPrevious is True + + +def test_list_dataset_tables_folders_unauthorized(client, dataset_fixture): + """Test listing with unauthorized user""" + response = client.query( + """ + query listDatasetTablesFolders( + $datasetUri: String!, + $filter: DatasetFilter + ) { + listDatasetTablesFolders( + datasetUri: $datasetUri, + filter: $filter + ) { + count + nodes { + name + targetType + targetUri + } + } + } + """, + username='unauthorized_user', + datasetUri=dataset_fixture.datasetUri, + filter={}, + ) + + assert response.errors + assert 'UnauthorizedOperation' in response.errors[0].message diff --git a/tests/permissions.py b/tests/permissions.py index 2facce8ed..88e3f1fac 100644 --- a/tests/permissions.py +++ b/tests/permissions.py @@ -545,6 +545,7 @@ def __post_init__(self): field_id('Mutation', 'generateDatasetAccessToken'): TestData( tenant_perm=MANAGE_DATASETS, resource_perm=CREDENTIALS_DATASET ), + field_id('Mutation', 'generateMetadata'): TestData(tenant_perm=MANAGE_DATASETS, resource_perm=UPDATE_DATASET), field_id('Mutation', 'importDashboard'): TestData(tenant_perm=MANAGE_DASHBOARDS, resource_perm=CREATE_DASHBOARD), field_id('Mutation', 'importDataset'): TestData(tenant_perm=MANAGE_DATASETS, resource_perm=CREATE_DATASET), field_id('Mutation', 'importRedshiftDataset'): TestData( @@ -908,6 +909,9 @@ def __post_init__(self): field_id('Query', 'listDatasetTables'): TestData( resource_ignore=IgnoreReason.NOTREQUIRED, tenant_ignore=IgnoreReason.NOTREQUIRED ), + field_id('Query', 'listDatasetTablesFolders'): TestData( + resource_perm=GET_DATASET, tenant_ignore=IgnoreReason.NOTREQUIRED + ), field_id('Query', 'listDatasets'): TestData( resource_ignore=IgnoreReason.USERLIMITED, tenant_ignore=IgnoreReason.USERLIMITED ), @@ -1025,6 +1029,7 @@ def __post_init__(self): field_id('Query', 'listWorksheets'): TestData( resource_ignore=IgnoreReason.USERLIMITED, tenant_ignore=IgnoreReason.USERLIMITED ), + field_id('Query', 'readTableSampleData'): TestData(resource_perm=UPDATE_DATASET_TABLE, tenant_perm=MANAGE_DATASETS), field_id('Query', 'previewTable'): TestData(resource_ignore=IgnoreReason.CUSTOM, tenant_ignore=IgnoreReason.CUSTOM), field_id('Query', 'queryEnums'): TestData( resource_ignore=IgnoreReason.NOTREQUIRED, tenant_ignore=IgnoreReason.NOTREQUIRED diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 06b155607..363aa2e3e 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -55,6 +55,10 @@ def setup_networks(mock_storage, **kwargs): mock_storage.context.db_engine.scoped_session().__enter__().query().filter().all.return_value = [MagicMock()] +def setup_Mutation_generateMetadata(iargs, **kwargs): + iargs['targetType'] = 'S3_Dataset' + + setup_EnvironmentSimplified_networks = setup_networks setup_Environment_networks = setup_networks From b3cc20ff3e22a88f28af023d2d74c598b5dc4387 Mon Sep 17 00:00:00 2001 From: dlpzx Date: Fri, 8 Aug 2025 14:43:46 +0200 Subject: [PATCH 12/22] GenAI metadata generation - resolve conflicts+add guardrails --- .../metadata_generation_folder_template.txt | 2 +- .../s3_datasets/aws/s3_dataset_client.py | 2 +- .../db/dataset_column_repositories.py | 4 +- .../db/dataset_location_repositories.py | 7 +- .../s3_datasets/db/dataset_repositories.py | 7 +- .../s3_datasets/services/dataset_service.py | 4 +- .../services/dataset_table_service.py | 7 +- .../components/ReviewMetadataComponent.js | 8 +- frontend/yarn.lock | 2802 ++++++++++++++--- 9 files changed, 2413 insertions(+), 430 deletions(-) diff --git a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt index ea20630e8..3b2d89b5c 100644 --- a/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt +++ b/backend/dataall/modules/s3_datasets/aws/bedrock_prompts/metadata_generation_folder_template.txt @@ -34,7 +34,7 @@ Input: Output: {{ - "label": "NotEnoughData", + "label": "my-folder-1", "description": "NotEnoughData" }} diff --git a/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py b/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py index 778edb508..45b32f63d 100644 --- a/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py +++ b/backend/dataall/modules/s3_datasets/aws/s3_dataset_client.py @@ -81,7 +81,7 @@ def list_bucket_files(self, bucket_name, prefix): Bucket=bucket_name, Prefix=prefix, ExpectedBucketOwner=dataset.AwsAccountId, - MaxKeys=1000, + MaxKeys=100, ) return response.get('Contents', []) except ClientError as e: diff --git a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py index 9915b6e7e..ef00a47b8 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_column_repositories.py @@ -48,6 +48,8 @@ def paginate_active_columns_for_table(session, table_uri: str, filter: dict): return paginate(query=q, page=filter.get('page', 1), page_size=filter.get('pageSize', 10)).to_dict() @staticmethod - def list_active_columns_for_table(session, table_uri: str): + def list_active_columns_for_table(session, table_uri: str, limit=None): q = DatasetColumnRepository.query_active_columns_for_table(session, table_uri) + if limit: + q = q.limit(limit) return q.all() diff --git a/backend/dataall/modules/s3_datasets/db/dataset_location_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_location_repositories.py index a910ddb34..2b52ed9e3 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_location_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_location_repositories.py @@ -97,9 +97,12 @@ def delete_dataset_locations(session, dataset_uri) -> bool: return True @staticmethod - def get_dataset_folders(session, dataset_uri): + def get_dataset_folders(session, dataset_uri, limit=None): """return the dataset folders""" - return session.query(DatasetStorageLocation).filter(DatasetStorageLocation.datasetUri == dataset_uri).all() + query = session.query(DatasetStorageLocation).filter(DatasetStorageLocation.datasetUri == dataset_uri) + if limit: + query = query.limit(limit) + return query.all() @staticmethod def paginated_dataset_locations(session, uri, data=None) -> dict: diff --git a/backend/dataall/modules/s3_datasets/db/dataset_repositories.py b/backend/dataall/modules/s3_datasets/db/dataset_repositories.py index c0198d81b..89cfc6cdc 100644 --- a/backend/dataall/modules/s3_datasets/db/dataset_repositories.py +++ b/backend/dataall/modules/s3_datasets/db/dataset_repositories.py @@ -186,9 +186,12 @@ def update_glue_database_status(session, dataset_uri): dataset.glueDatabaseCreated = True @staticmethod - def get_dataset_tables(session, dataset_uri): + def get_dataset_tables(session, dataset_uri, limit=None): """return the dataset tables""" - return session.query(DatasetTable).filter(DatasetTable.datasetUri == dataset_uri).all() + query = session.query(DatasetTable).filter(DatasetTable.datasetUri == dataset_uri) + if limit: + query = query.limit(limit) + return query.all() @staticmethod def delete_dataset(session, dataset) -> bool: diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index ea75e3dc6..4759628f4 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -601,8 +601,8 @@ def generate_metadata_for_dataset(uri, metadata_types): context = get_context() with context.db_engine.scoped_session() as session: dataset = DatasetBaseRepository.get_dataset_by_uri(session, uri) - tables = DatasetRepository.get_dataset_tables(session, dataset.datasetUri) - folders = DatasetLocationRepository.get_dataset_folders(session, dataset.datasetUri) + tables = DatasetRepository.get_dataset_tables(session, dataset.datasetUri, limit=50) + folders = DatasetLocationRepository.get_dataset_folders(session, dataset.datasetUri, limit=50) metadata = BedrockClient().invoke_model_dataset_metadata( metadata_types=metadata_types, dataset=dataset, diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index 5d7c3804a..a9486225b 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -33,6 +33,7 @@ from dataall.base.db import exceptions from dataall.modules.s3_datasets.aws.bedrock_metadata_client import BedrockClient from dataall.modules.s3_datasets.db.dataset_column_repositories import DatasetColumnRepository +from dataall.modules.s3_datasets.services.dataset_enums import MetadataGenerationTypes log = logging.getLogger(__name__) @@ -207,12 +208,14 @@ def _delete_dataset_table_read_permission(session, table_uri): # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' # ) def generate_metadata_for_table(uri, metadata_types, sample_data): + metadataTypesForTable = [MetadataGenerationTypes.Description, MetadataGenerationTypes.Tag] + table_metadata_types = [item for item in metadata_types if item in metadataTypesForTable] context = get_context() with context.db_engine.scoped_session() as session: table = DatasetTableRepository.get_dataset_table_by_uri(session, uri) - table_columns = DatasetColumnRepository.list_active_columns_for_table(session, table.tableUri) + table_columns = DatasetColumnRepository.list_active_columns_for_table(session, table.tableUri, limit=50) metadata = BedrockClient().invoke_model_table_metadata( - table=table, columns=table_columns, metadata_types=metadata_types, sample_data=sample_data + table=table, columns=table_columns, metadata_types=table_metadata_types, sample_data=sample_data ) result = [{'targetUri': uri, 'targetType': 'Table', **metadata}] diff --git a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js index 8ee78d131..8334e8417 100644 --- a/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js +++ b/frontend/src/modules/S3_Datasets/components/ReviewMetadataComponent.js @@ -95,10 +95,10 @@ export const ReviewMetadataComponent = (props) => { if (targetIndex !== -1) { const updatedTarget = { ...targets[targetIndex], - description: response.data.generateMetadata[0].description, - label: response.data.generateMetadata[0].label, - tags: response.data.generateMetadata[0].tags, - topics: response.data.generateMetadata[0].topics + description: response.data.generateMetadata[0].description ?? '', + label: response.data.generateMetadata[0].label ?? '', + tags: response.data.generateMetadata[0].tags ?? '', + topics: response.data.generateMetadata[0].topics ?? '' }; const updatedTargets = [...targets]; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 748b7f63f..3115c1d60 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@adobe/css-tools@4.3.3", "@adobe/css-tools@^4.0.1": +"@adobe/css-tools@4.3.3": version "4.3.3" resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz" integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== @@ -289,15 +289,6 @@ fast-xml-parser "^4.2.5" tslib "^1.8.0" -"@aws-crypto/crc32@2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz" - integrity sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA== - dependencies: - "@aws-crypto/util" "^2.0.0" - "@aws-sdk/types" "^3.1.0" - tslib "^1.11.1" - "@aws-crypto/crc32@^1.0.0": version "1.2.2" resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-1.2.2.tgz" @@ -307,6 +298,15 @@ "@aws-sdk/types" "^3.1.0" tslib "^1.11.1" +"@aws-crypto/crc32@2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz" + integrity sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + "@aws-crypto/ie11-detection@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-1.0.0.tgz" @@ -321,6 +321,19 @@ dependencies: tslib "^1.11.1" +"@aws-crypto/sha256-browser@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-1.2.2.tgz" + integrity sha512-0tNR4kBtJp+9S0kis4+JLab3eg6QWuIeuPhzaYoYwNUXGBgsWIkktA2mnilet+EGWzf3n1zknJXC4X4DVyyXbg== + dependencies: + "@aws-crypto/ie11-detection" "^1.0.0" + "@aws-crypto/sha256-js" "^1.2.2" + "@aws-crypto/supports-web-crypto" "^1.0.0" + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + tslib "^1.11.1" + "@aws-crypto/sha256-browser@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz" @@ -335,20 +348,7 @@ "@aws-sdk/util-utf8-browser" "^3.0.0" tslib "^1.11.1" -"@aws-crypto/sha256-browser@^1.0.0": - version "1.2.2" - resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-1.2.2.tgz" - integrity sha512-0tNR4kBtJp+9S0kis4+JLab3eg6QWuIeuPhzaYoYwNUXGBgsWIkktA2mnilet+EGWzf3n1zknJXC4X4DVyyXbg== - dependencies: - "@aws-crypto/ie11-detection" "^1.0.0" - "@aws-crypto/sha256-js" "^1.2.2" - "@aws-crypto/supports-web-crypto" "^1.0.0" - "@aws-crypto/util" "^1.2.2" - "@aws-sdk/types" "^3.1.0" - "@aws-sdk/util-locate-window" "^3.0.0" - tslib "^1.11.1" - -"@aws-crypto/sha256-js@1.2.2", "@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.2": +"@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.2", "@aws-crypto/sha256-js@1.2.2": version "1.2.2" resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz" integrity sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g== @@ -357,7 +357,7 @@ "@aws-sdk/types" "^3.1.0" tslib "^1.11.1" -"@aws-crypto/sha256-js@2.0.0", "@aws-crypto/sha256-js@^2.0.0": +"@aws-crypto/sha256-js@^2.0.0", "@aws-crypto/sha256-js@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz" integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== @@ -1641,12 +1641,7 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" -"@aws-sdk/types@3.186.0": - version "3.186.0" - resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.186.0.tgz" - integrity sha512-NatmSU37U+XauMFJCdFI6nougC20JUFZar+ump5wVv0i54H+2Refg1YbFDxSs0FY28TSB9jfhWIpfFBmXgL5MQ== - -"@aws-sdk/types@3.6.1", "@aws-sdk/types@^3.1.0": +"@aws-sdk/types@^3.1.0", "@aws-sdk/types@3.6.1": version "3.6.1" resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.6.1.tgz" integrity sha512-4Dx3eRTrUHLxhFdLJL8zdNGzVsJfAxtxPYYGmIddUkO2Gj3WA1TGjdfG4XN/ClI6e1XonCHafQX3UYO/mgnH3g== @@ -1659,6 +1654,11 @@ "@smithy/types" "^3.0.0" tslib "^2.6.2" +"@aws-sdk/types@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.186.0.tgz" + integrity sha512-NatmSU37U+XauMFJCdFI6nougC20JUFZar+ump5wVv0i54H+2Refg1YbFDxSs0FY28TSB9jfhWIpfFBmXgL5MQ== + "@aws-sdk/url-parser-native@3.6.1": version "3.6.1" resolved "https://registry.npmjs.org/@aws-sdk/url-parser-native/-/url-parser-native-3.6.1.tgz" @@ -1868,6 +1868,13 @@ "@aws-sdk/types" "3.6.1" tslib "^1.8.0" +"@aws-sdk/util-utf8-browser@^3.0.0", "@aws-sdk/util-utf8-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.6.1.tgz" + integrity sha512-gZPySY6JU5gswnw3nGOEHl3tYE7vPKvtXGYoS2NRabfDKRejFvu+4/nNW6SSpoOxk6LSXsrWB39NO51k+G4PVA== + dependencies: + tslib "^1.8.0" + "@aws-sdk/util-utf8-browser@3.186.0": version "3.186.0" resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.186.0.tgz" @@ -1875,13 +1882,6 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/util-utf8-browser@3.6.1", "@aws-sdk/util-utf8-browser@^3.0.0": - version "3.6.1" - resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.6.1.tgz" - integrity sha512-gZPySY6JU5gswnw3nGOEHl3tYE7vPKvtXGYoS2NRabfDKRejFvu+4/nNW6SSpoOxk6LSXsrWB39NO51k+G4PVA== - dependencies: - tslib "^1.8.0" - "@aws-sdk/util-utf8-node@3.186.0": version "3.186.0" resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.186.0.tgz" @@ -1916,12 +1916,12 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.7": +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz" integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.1.0", "@babel/core@^7.11.0", "@babel/core@^7.11.1", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.13.16", "@babel/core@^7.16.0", "@babel/core@^7.20.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz" integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== @@ -1951,7 +1951,7 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.24.7", "@babel/generator@^7.26.10", "@babel/generator@^7.7.2": +"@babel/generator@^7.20.0", "@babel/generator@^7.24.7", "@babel/generator@^7.26.10", "@babel/generator@^7.7.2": version "7.26.10" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz" integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang== @@ -1977,7 +1977,7 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7": +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz" integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== @@ -2023,7 +2023,7 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.24.7": +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz" integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== @@ -2084,7 +2084,7 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz" integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== -"@babel/helper-remap-async-to-generator@^7.24.7": +"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz" integrity sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA== @@ -2158,7 +2158,7 @@ "@babel/template" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.24.7", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.24.7", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": version "7.27.0" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz" integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== @@ -2197,7 +2197,17 @@ "@babel/helper-environment-visitor" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-proposal-class-properties@^7.16.0": +"@babel/plugin-proposal-async-generator-functions@^7.0.0": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.18.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== @@ -2214,7 +2224,23 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-decorators" "^7.24.7" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": +"@babel/plugin-proposal-export-default-from@^7.0.0": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.24.7.tgz" + integrity sha512-CcmFwUJ3tKhLjPdt4NP+SHMshebytF8ZTYOv5ZDpkzq2sin80Wb5vJrGt8fhPrORQCfoSa0LAxC/DW+GAC5+Hw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-export-default-from" "^7.24.7" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.0": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== @@ -2222,7 +2248,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.16.0": +"@babel/plugin-proposal-numeric-separator@^7.0.0", "@babel/plugin-proposal-numeric-separator@^7.16.0": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== @@ -2230,7 +2256,26 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-optional-chaining@^7.16.0": +"@babel/plugin-proposal-object-rest-spread@^7.20.0": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.0.0": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.20.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz" integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== @@ -2287,13 +2332,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-export-default-from@^7.0.0", "@babel/plugin-syntax-export-default-from@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.24.7.tgz" + integrity sha512-bTPz4/635WQ9WhwsyPdxUJDVpsi/X9BMmy/8Rf/UAlOO4jSql4CxUCjWI5PiM+jG+c4LVPTScoTw80geFj9+Bw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-export-namespace-from@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" @@ -2301,7 +2353,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.24.7": +"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.18.0", "@babel/plugin-syntax-flow@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz" integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== @@ -2350,7 +2402,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-syntax-nullish-coalescing-operator@^7.0.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== @@ -2378,7 +2430,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.8.3": +"@babel/plugin-syntax-optional-chaining@^7.0.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -2414,7 +2466,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.24.7": +"@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz" integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== @@ -2431,7 +2483,7 @@ "@babel/helper-remap-async-to-generator" "^7.24.7" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-async-to-generator@^7.24.7": +"@babel/plugin-transform-async-to-generator@^7.20.0", "@babel/plugin-transform-async-to-generator@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz" integrity sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA== @@ -2447,7 +2499,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-block-scoping@^7.24.7": +"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz" integrity sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== @@ -2471,7 +2523,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.24.7": +"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz" integrity sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== @@ -2485,7 +2537,7 @@ "@babel/helper-split-export-declaration" "^7.24.7" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.24.7": +"@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz" integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== @@ -2493,7 +2545,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/template" "^7.24.7" -"@babel/plugin-transform-destructuring@^7.24.7": +"@babel/plugin-transform-destructuring@^7.20.0", "@babel/plugin-transform-destructuring@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz" integrity sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== @@ -2539,7 +2591,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-flow-strip-types@^7.16.0": +"@babel/plugin-transform-flow-strip-types@^7.16.0", "@babel/plugin-transform-flow-strip-types@^7.20.0", "@babel/plugin-transform-flow-strip-types@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz" integrity sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== @@ -2555,7 +2607,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" -"@babel/plugin-transform-function-name@^7.24.7": +"@babel/plugin-transform-function-name@^7.0.0", "@babel/plugin-transform-function-name@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz" integrity sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== @@ -2572,7 +2624,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.24.7": +"@babel/plugin-transform-literals@^7.0.0", "@babel/plugin-transform-literals@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz" integrity sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== @@ -2602,7 +2654,7 @@ "@babel/helper-module-transforms" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-modules-commonjs@^7.24.7": +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz" integrity sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== @@ -2629,7 +2681,7 @@ "@babel/helper-module-transforms" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": +"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz" integrity sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g== @@ -2695,14 +2747,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.24.7": +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz" integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-private-methods@^7.24.7": +"@babel/plugin-transform-private-methods@^7.22.5", "@babel/plugin-transform-private-methods@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz" integrity sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ== @@ -2710,7 +2762,7 @@ "@babel/helper-create-class-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-private-property-in-object@^7.24.7": +"@babel/plugin-transform-private-property-in-object@^7.22.11", "@babel/plugin-transform-private-property-in-object@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz" integrity sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA== @@ -2734,7 +2786,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.24.7": +"@babel/plugin-transform-react-display-name@^7.0.0", "@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz" integrity sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== @@ -2748,7 +2800,21 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.24.7" -"@babel/plugin-transform-react-jsx@^7.24.7": +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz" + integrity sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-react-jsx-source@^7.0.0": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz" + integrity sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + +"@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz" integrity sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== @@ -2782,7 +2848,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-runtime@^7.16.4": +"@babel/plugin-transform-runtime@^7.0.0", "@babel/plugin-transform-runtime@^7.16.4": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz" integrity sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw== @@ -2794,14 +2860,14 @@ babel-plugin-polyfill-regenerator "^0.6.1" semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.24.7": +"@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz" integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-spread@^7.24.7": +"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz" integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== @@ -2809,7 +2875,7 @@ "@babel/helper-plugin-utils" "^7.24.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" -"@babel/plugin-transform-sticky-regex@^7.24.7": +"@babel/plugin-transform-sticky-regex@^7.0.0", "@babel/plugin-transform-sticky-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz" integrity sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g== @@ -2830,7 +2896,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-typescript@^7.24.7": +"@babel/plugin-transform-typescript@^7.24.7", "@babel/plugin-transform-typescript@^7.5.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz" integrity sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw== @@ -2855,7 +2921,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/plugin-transform-unicode-regex@^7.24.7": +"@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.24.7": version "7.24.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz" integrity sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg== @@ -2871,7 +2937,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.24.7" "@babel/helper-plugin-utils" "^7.24.7" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": +"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": version "7.24.7" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz" integrity sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ== @@ -2958,6 +3024,15 @@ core-js-compat "^3.31.0" semver "^6.3.1" +"@babel/preset-flow@^7.13.13": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.7.tgz" + integrity sha512-NL3Lo0NorCU607zU3NwRyJbpaB6E3t0xtd3LfAQKDfkeX4/ggcDXvkmkW42QWT5owUeW/jAe4hn+2qvkV1IbfQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" + "@babel/plugin-transform-flow-strip-types" "^7.24.7" + "@babel/preset-modules@0.1.6-no-external-plugins": version "0.1.6-no-external-plugins" resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz" @@ -2979,7 +3054,7 @@ "@babel/plugin-transform-react-jsx-development" "^7.24.7" "@babel/plugin-transform-react-pure-annotations" "^7.24.7" -"@babel/preset-typescript@^7.16.0": +"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.0": version "7.24.7" resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz" integrity sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ== @@ -2990,19 +3065,30 @@ "@babel/plugin-transform-modules-commonjs" "^7.24.7" "@babel/plugin-transform-typescript" "^7.24.7" +"@babel/register@^7.13.16": + version "7.24.6" + resolved "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz" + integrity sha512-WSuFCc2wCqMeXkz/i3yfAAsxwWflEgbVkZzivgAmXl/MxrXeoYFZOOPllbC8R8WTF7u61wSRQtDVZ1879cdu6w== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.6" + source-map-support "^0.5.16" + "@babel/regjsgen@^0.8.0": version "0.8.0" resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.27.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz" integrity sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.24.7", "@babel/template@^7.26.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.24.7", "@babel/template@^7.26.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3": version "7.27.0" resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz" integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== @@ -3011,7 +3097,7 @@ "@babel/parser" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/traverse@7.26.10", "@babel/traverse@^7.24.7", "@babel/traverse@^7.7.2": +"@babel/traverse@7.26.10": version "7.26.10" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz" integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A== @@ -3024,7 +3110,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.27.0" resolved "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -3219,7 +3305,7 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" -"@emotion/core@^10.0.28": +"@emotion/core@^10.0.27", "@emotion/core@^10.0.28": version "10.3.1" resolved "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz" integrity sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww== @@ -3240,22 +3326,15 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== - "@emotion/hash@^0.9.1": version "0.9.1" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== -"@emotion/is-prop-valid@0.8.8": - version "0.8.8" - resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz" - integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== - dependencies: - "@emotion/memoize" "0.7.4" +"@emotion/hash@0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== "@emotion/is-prop-valid@^1.2.2": version "1.2.2" @@ -3264,17 +3343,24 @@ dependencies: "@emotion/memoize" "^0.8.1" -"@emotion/memoize@0.7.4": - version "0.7.4" - resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz" - integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== +"@emotion/is-prop-valid@0.8.8": + version "0.8.8" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" "@emotion/memoize@^0.8.1": version "0.8.1" resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== -"@emotion/react@^11.8.2": +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0", "@emotion/react@^11.8.2", "@emotion/react@^11.9.0": version "11.11.4" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz" integrity sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw== @@ -3299,7 +3385,7 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" -"@emotion/serialize@^1.1.2", "@emotion/serialize@^1.1.3", "@emotion/serialize@^1.1.4": +"@emotion/serialize@^1.1.2": version "1.1.4" resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== @@ -3310,16 +3396,38 @@ "@emotion/utils" "^1.2.1" csstype "^3.0.2" -"@emotion/sheet@0.9.4": - version "0.9.4" - resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" - integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== +"@emotion/serialize@^1.1.3": + version "1.1.4" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" + integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/serialize@^1.1.4": + version "1.1.4" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz" + integrity sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ== + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" "@emotion/sheet@^1.2.2": version "1.2.2" resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz" integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== +"@emotion/sheet@0.9.4": + version "0.9.4" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" + integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== + "@emotion/styled-base@^10.3.0": version "10.3.0" resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz" @@ -3338,7 +3446,7 @@ "@emotion/styled-base" "^10.3.0" babel-plugin-emotion "^10.0.27" -"@emotion/styled@^11.8.1": +"@emotion/styled@^11.3.0", "@emotion/styled@^11.8.1": version "11.11.5" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz" integrity sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ== @@ -3355,41 +3463,41 @@ resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz" integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== -"@emotion/unitless@0.7.5": - version "0.7.5" - resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" - integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== - "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz" integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== +"@emotion/unitless@0.7.5": + version "0.7.5" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + "@emotion/use-insertion-effect-with-fallbacks@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz" integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== -"@emotion/utils@0.11.3": - version "0.11.3" - resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz" - integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== - "@emotion/utils@^1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz" integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== -"@emotion/weak-memoize@0.2.5": - version "0.2.5" - resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" - integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@emotion/utils@0.11.3": + version "0.11.3" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz" + integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== "@emotion/weak-memoize@^0.3.1": version "0.3.1" resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww== +"@emotion/weak-memoize@0.2.5": + version "0.2.5" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" + integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -3454,6 +3562,18 @@ resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== +"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": + version "9.3.0" + resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" @@ -3485,6 +3605,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@isaacs/ttlcache@^1.4.1": + version "1.4.1" + resolved "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz" + integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -3559,6 +3684,13 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/create-cache-key-function@^29.6.3": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz" + integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== + dependencies: + "@jest/types" "^29.6.3" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" @@ -3569,6 +3701,16 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" @@ -3588,6 +3730,18 @@ jest-mock "^27.5.1" jest-util "^27.5.1" +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" @@ -3702,6 +3856,17 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" @@ -3801,7 +3966,7 @@ dependencies: "@monaco-editor/loader" "^1.4.0" -"@mui/base@5.0.0-beta.40", "@mui/base@^5.0.0-beta.20": +"@mui/base@^5.0.0-beta.20", "@mui/base@5.0.0-beta.40": version "5.0.0-beta.40" resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz" integrity sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ== @@ -3839,7 +4004,7 @@ clsx "^2.1.0" prop-types "^15.8.1" -"@mui/material@^5.5.2": +"@mui/material@^5.0.0", "@mui/material@^5.4.1", "@mui/material@^5.5.2", "@mui/material@^5.8.6", "@mui/material@>=5.15.0": version "5.15.19" resolved "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz" integrity sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ== @@ -3899,7 +4064,7 @@ jss-plugin-vendor-prefixer "^10.10.0" prop-types "^15.8.1" -"@mui/system@^5.15.15": +"@mui/system@^5.15.15", "@mui/system@^5.4.1", "@mui/system@^5.8.0": version "5.15.15" resolved "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz" integrity sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ== @@ -3985,7 +4150,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -4021,6 +4186,310 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@react-native-community/cli-clean@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-13.6.8.tgz" + integrity sha512-B1uxlm1N4BQuWFvBL3yRl3LVvydjswsdbTi7tMrHMtSxfRio1p9HjcmDzlzKco09Y+8qBGgakm3jcMZGLbhXQQ== + dependencies: + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + execa "^5.0.0" + fast-glob "^3.3.2" + +"@react-native-community/cli-config@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-13.6.8.tgz" + integrity sha512-RabCkIsWdP4Ex/sf1uSP9qxc30utm+0uIJAjrZkNQynm7T4Lyqn/kT3LKm4yM6M0Qk61YxGguiaXF4601vAduw== + dependencies: + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + cosmiconfig "^5.1.0" + deepmerge "^4.3.0" + fast-glob "^3.3.2" + joi "^17.2.1" + +"@react-native-community/cli-debugger-ui@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-13.6.8.tgz" + integrity sha512-2cS+MX/Su6sVSjqpDftFOXbK7EuPg98xzsPkdPhkQnkZwvXqodK9CAMuDMbx3lBHHtrPrpMbBCpFmPN8iVOnlA== + dependencies: + serve-static "^1.13.1" + +"@react-native-community/cli-doctor@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-13.6.8.tgz" + integrity sha512-/3Vdy9J3hyiu0y3nd/CU3kBqPlTRxnLXg7V6jrA1jbTOlZAMyV9imEkrqEaGK0SMOyMhh9Pipf98Ozhk0Nl4QA== + dependencies: + "@react-native-community/cli-config" "13.6.8" + "@react-native-community/cli-platform-android" "13.6.8" + "@react-native-community/cli-platform-apple" "13.6.8" + "@react-native-community/cli-platform-ios" "13.6.8" + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + command-exists "^1.2.8" + deepmerge "^4.3.0" + envinfo "^7.10.0" + execa "^5.0.0" + hermes-profile-transformer "^0.0.6" + node-stream-zip "^1.9.1" + ora "^5.4.1" + semver "^7.5.2" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + yaml "^2.2.1" + +"@react-native-community/cli-hermes@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-13.6.8.tgz" + integrity sha512-lZi/OBFuZUj5cLK94oEgtrtmxGoqeYVRcnHXl/R5c4put9PDl+qH2bEMlGZkFiw57ae3UZKr3TMk+1s4jh3FYQ== + dependencies: + "@react-native-community/cli-platform-android" "13.6.8" + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + hermes-profile-transformer "^0.0.6" + +"@react-native-community/cli-platform-android@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-13.6.8.tgz" + integrity sha512-vWrqeLRRTwp2kO33nbrAgbYn8HR2c2CpIfyVJY9Ckk7HGUSwDyxdcSu7YBvt2ShdfLZH0HctWFNXsgGrfg6BDw== + dependencies: + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + execa "^5.0.0" + fast-glob "^3.3.2" + fast-xml-parser "^4.2.4" + logkitty "^0.7.1" + +"@react-native-community/cli-platform-apple@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-13.6.8.tgz" + integrity sha512-1JPohnlXPqU44zns3ALEzIbH2cKRw6JtEDJERgLuEUbs2r2NeJgqDbKyZ7fTTO8o+pegDnn6+Rr7qGVVOuUzzg== + dependencies: + "@react-native-community/cli-tools" "13.6.8" + chalk "^4.1.2" + execa "^5.0.0" + fast-glob "^3.3.2" + fast-xml-parser "^4.0.12" + ora "^5.4.1" + +"@react-native-community/cli-platform-ios@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-13.6.8.tgz" + integrity sha512-/IIcIRM8qaoD7iZqsvtf6Qq1AwtChWYfB9sTn3mTiolZ5Zd5bXH37g+6liPfAICRkj2Ptq3iXmjrDVUQAxrOXw== + dependencies: + "@react-native-community/cli-platform-apple" "13.6.8" + +"@react-native-community/cli-server-api@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-13.6.8.tgz" + integrity sha512-Lx664oWTzpVfbKUTy+3GIX7e+Mt5Zn+zdkM4ehllNdik/lbB3tM9Nrg8PSvOfI+tTXs2w55+nIydLfH+0FqJVg== + dependencies: + "@react-native-community/cli-debugger-ui" "13.6.8" + "@react-native-community/cli-tools" "13.6.8" + compression "^1.7.1" + connect "^3.6.5" + errorhandler "^1.5.1" + nocache "^3.0.1" + pretty-format "^26.6.2" + serve-static "^1.13.1" + ws "^6.2.2" + +"@react-native-community/cli-tools@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-13.6.8.tgz" + integrity sha512-1MYlae9EkbjC7DBYOGMH5xF9yDoeNYUKgEdDjL6WAUBoF2gtwiZPM6igLKi/+dhb5sCtC7fiLrLi0Oevdf+RmQ== + dependencies: + appdirsjs "^1.2.4" + chalk "^4.1.2" + execa "^5.0.0" + find-up "^5.0.0" + mime "^2.4.1" + node-fetch "^2.6.0" + open "^6.2.0" + ora "^5.4.1" + semver "^7.5.2" + shell-quote "^1.7.3" + sudo-prompt "^9.0.0" + +"@react-native-community/cli-types@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-13.6.8.tgz" + integrity sha512-C4mVByy0i+/NPuPhdMLBR7ubEVkjVS1VwoQu/BoG1crJFNE+167QXAzH01eFbXndsjZaMWmD4Gerx7TYc6lHfA== + dependencies: + joi "^17.2.1" + +"@react-native-community/cli@13.6.8": + version "13.6.8" + resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.8.tgz" + integrity sha512-0lRdgLNaXixWY4BfFRl1J6Ao9Lapo2z+++iE7TD4GAbuxOWJSyFi+KUA8XNfSDyML4jFO02MZgyBPxAWdaminQ== + dependencies: + "@react-native-community/cli-clean" "13.6.8" + "@react-native-community/cli-config" "13.6.8" + "@react-native-community/cli-debugger-ui" "13.6.8" + "@react-native-community/cli-doctor" "13.6.8" + "@react-native-community/cli-hermes" "13.6.8" + "@react-native-community/cli-server-api" "13.6.8" + "@react-native-community/cli-tools" "13.6.8" + "@react-native-community/cli-types" "13.6.8" + chalk "^4.1.2" + commander "^9.4.1" + deepmerge "^4.3.0" + execa "^5.0.0" + find-up "^4.1.0" + fs-extra "^8.1.0" + graceful-fs "^4.1.3" + prompts "^2.4.2" + semver "^7.5.2" + +"@react-native/assets-registry@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.74.84.tgz" + integrity sha512-dzUhwyaX04QosWZ8zyaaNB/WYZIdeDN1lcpfQbqiOhZJShRH+FLTDVONE/dqlMQrP+EO7lDqF0RrlIt9lnOCQQ== + +"@react-native/babel-plugin-codegen@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.74.84.tgz" + integrity sha512-UR4uiii5szIJA84mSC6GJOfYKDq7/ThyetOQT62+BBcyGeHVtHlNLNRzgaMeLqIQaT8Fq4pccMI+7QqLOMXzdw== + dependencies: + "@react-native/codegen" "0.74.84" + +"@react-native/babel-preset@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.74.84.tgz" + integrity sha512-WUfu6Y4aGuVdocQZvx33BJiQWFH6kRCHYbZfBn2psgFrSRLgQWEQrDCxqPFObNAVSayM0rNhp2FvI5K/Eyeqlg== + dependencies: + "@babel/core" "^7.20.0" + "@babel/plugin-proposal-async-generator-functions" "^7.0.0" + "@babel/plugin-proposal-class-properties" "^7.18.0" + "@babel/plugin-proposal-export-default-from" "^7.0.0" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.0" + "@babel/plugin-proposal-numeric-separator" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.20.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-default-from" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.18.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.20.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.0.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.20.0" + "@babel/plugin-transform-flow-strip-types" "^7.20.0" + "@babel/plugin-transform-function-name" "^7.0.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-modules-commonjs" "^7.0.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0" + "@babel/plugin-transform-parameters" "^7.0.0" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.11" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + "@babel/plugin-transform-runtime" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-sticky-regex" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.5.0" + "@babel/plugin-transform-unicode-regex" "^7.0.0" + "@babel/template" "^7.0.0" + "@react-native/babel-plugin-codegen" "0.74.84" + babel-plugin-transform-flow-enums "^0.0.2" + react-refresh "^0.14.0" + +"@react-native/codegen@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.74.84.tgz" + integrity sha512-0hXlnu9i0o8v+gXKQi+x6T471L85kCDwW4WrJiYAeOheWrQdNNW6rC3g8+LL7HXAf7QcHGU/8/d57iYfdVK2BQ== + dependencies: + "@babel/parser" "^7.20.0" + glob "^7.1.1" + hermes-parser "0.19.1" + invariant "^2.2.4" + jscodeshift "^0.14.0" + mkdirp "^0.5.1" + nullthrows "^1.1.1" + +"@react-native/community-cli-plugin@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.74.84.tgz" + integrity sha512-GBKE+1sUh86fS2XXV46gMCNHMc1KetshMbYJ0AhDhldpaILZHqRBX50mdVsiYVvkzp4QjM0nmYqefuJ9NVwicQ== + dependencies: + "@react-native-community/cli-server-api" "13.6.8" + "@react-native-community/cli-tools" "13.6.8" + "@react-native/dev-middleware" "0.74.84" + "@react-native/metro-babel-transformer" "0.74.84" + chalk "^4.0.0" + execa "^5.1.1" + metro "^0.80.3" + metro-config "^0.80.3" + metro-core "^0.80.3" + node-fetch "^2.2.0" + querystring "^0.2.1" + readline "^1.3.0" + +"@react-native/debugger-frontend@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.74.84.tgz" + integrity sha512-YUEA03UNFbiYzHpYxlcS2D9+3eNT5YLGkl5yRg3nOSN6KbCc/OttGnNZme+tuSOJwjMN/vcvtDKYkTqjJw8U0A== + +"@react-native/dev-middleware@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.74.84.tgz" + integrity sha512-veYw/WmyrAOQHUiIeULzn2duJQnXDPiKq2jZ/lcmDo6jsLirpp+Q73lx09TYgy/oVoPRuV0nfmU3x9B6EV/7qQ== + dependencies: + "@isaacs/ttlcache" "^1.4.1" + "@react-native/debugger-frontend" "0.74.84" + "@rnx-kit/chromium-edge-launcher" "^1.0.0" + chrome-launcher "^0.15.2" + connect "^3.6.5" + debug "^2.2.0" + node-fetch "^2.2.0" + nullthrows "^1.1.1" + open "^7.0.3" + selfsigned "^2.4.1" + serve-static "^1.13.1" + temp-dir "^2.0.0" + ws "^6.2.2" + +"@react-native/gradle-plugin@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.74.84.tgz" + integrity sha512-wYWC5WWXqzCCe4PDogz9pNc4xH5ZamahW5XGSbrrYJ5V3walZ+7z43V6iEBJkZbLjj9YBcSttkXYGr1Xh4veAg== + +"@react-native/js-polyfills@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.74.84.tgz" + integrity sha512-+PgxuUjBw9JVlz6m4ECsIJMLbDopnr4rpLmsG32hQaJrg0wMuvHtsgAY/J/aVCSG2GNUXexfjrnhc+O9yGOZXQ== + +"@react-native/metro-babel-transformer@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.74.84.tgz" + integrity sha512-YtVGq7jkgyUECv5yt4BOFbOXyW4ddUn8+dnwGGpJKdfhXYL5o5++AxNdE+2x+SZdkj3JUVekGKPwRabFECABaw== + dependencies: + "@babel/core" "^7.20.0" + "@react-native/babel-preset" "0.74.84" + hermes-parser "0.19.1" + nullthrows "^1.1.1" + +"@react-native/normalize-colors@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.84.tgz" + integrity sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A== + +"@react-native/virtualized-lists@0.74.84": + version "0.74.84" + resolved "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.74.84.tgz" + integrity sha512-XcV+qdqt2WihaY4iRm/M1FdSy+18lecU9mRXNmy9YK8g9Th/8XbNtmmKI0qWBx3KxyuXMH/zd0ps05YTrX16kw== + dependencies: + invariant "^2.2.4" + nullthrows "^1.1.1" + "@reduxjs/toolkit@^1.8.0": version "1.9.7" resolved "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz" @@ -4031,9 +4500,21 @@ redux-thunk "^2.4.2" reselect "^4.1.8" -"@rollup/plugin-babel@^5.2.0": - version "5.3.1" - resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz" +"@rnx-kit/chromium-edge-launcher@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@rnx-kit/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz" + integrity sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg== + dependencies: + "@types/node" "^18.0.0" + escape-string-regexp "^4.0.0" + is-wsl "^2.2.0" + lighthouse-logger "^1.0.0" + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@rollup/plugin-babel@^5.2.0": + version "5.3.1" + resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz" integrity sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q== dependencies: "@babel/helper-module-imports" "^7.10.4" @@ -4073,6 +4554,23 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz" integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== +"@sideway/address@^4.1.5": + version "4.1.5" + resolved "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz" + integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" @@ -4090,6 +4588,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" @@ -4231,6 +4743,20 @@ lz-string "^1.5.0" pretty-format "^27.0.2" +"@testing-library/dom@>=7.21.4": + version "10.1.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz" + integrity sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + "@testing-library/jest-dom@^5.16.2": version "5.17.0" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz" @@ -4297,7 +4823,7 @@ resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9": version "7.20.5" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -4504,6 +5030,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^18.0.0": + version "18.19.34" + resolved "https://registry.npmjs.org/@types/node/-/node-18.19.34.tgz" + integrity sha512-eXF4pfBNV5DAMKGbI02NnDtWrQ40hAN558/2vvS4gMpMIxaf6JmD7YjnZbq0Q9TDSSkKBamime8ewRoomHdt4g== + dependencies: + undici-types "~5.26.4" + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" @@ -4558,7 +5091,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17": +"@types/react@*", "@types/react@^17", "@types/react@^17.0.0 || ^18.0.0": version "17.0.80" resolved "https://registry.npmjs.org/@types/react/-/react-17.0.80.tgz" integrity sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA== @@ -4567,6 +5100,14 @@ "@types/scheduler" "^0.16" csstype "^3.0.2" +"@types/react@^18.2.6": + version "18.3.3" + resolved "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz" + integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" @@ -4649,6 +5190,13 @@ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== +"@types/yargs@^15.0.0": + version "15.0.19" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz" + integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^16.0.0": version "16.0.9" resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz" @@ -4668,7 +5216,7 @@ resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.7.tgz" integrity sha512-LKzNTjj+2j09wAo/vvVjzgw5qckJJzhdGgWHW7j69QIGdq/KnZrMAMIHQiWGl3Ccflh5/CudBAntTPYdprPltA== -"@typescript-eslint/eslint-plugin@^5.5.0": +"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== @@ -4691,7 +5239,7 @@ dependencies: "@typescript-eslint/utils" "5.62.0" -"@typescript-eslint/parser@^5.5.0": +"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== @@ -4737,7 +5285,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": +"@typescript-eslint/utils@^5.58.0", "@typescript-eslint/utils@5.62.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -4764,7 +5312,7 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": +"@webassemblyjs/ast@^1.12.1", "@webassemblyjs/ast@1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== @@ -4865,7 +5413,7 @@ "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": +"@webassemblyjs/wasm-parser@^1.12.1", "@webassemblyjs/wasm-parser@1.12.1": version "1.12.1" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== @@ -4955,7 +5503,14 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== -accepts@~1.3.4, accepts@~1.3.8: +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -4986,16 +5541,16 @@ acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + acorn@^7.1.1: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" @@ -5035,7 +5590,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -5045,7 +5600,27 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: +ajv@^8.0.0: + version "8.16.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +ajv@^8.6.0, ajv@>=8: + version "8.16.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +ajv@^8.8.2, ajv@^8.9.0: version "8.16.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz" integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== @@ -5071,6 +5646,11 @@ amazon-quicksight-embedding-sdk@^1.18.1: resolved "https://registry.npmjs.org/amazon-quicksight-embedding-sdk/-/amazon-quicksight-embedding-sdk-1.20.1.tgz" integrity sha512-JYBCeq1Rq1OOeZQB9ry85uASTTtqZzp5hFv0ihIX7XyD9anvcoW6GHo4ttzdkJMTMMAnzXhIBe9CRKm3tHMzeg== +anser@^1.4.9: + version "1.4.10" + resolved "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz" + integrity sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww== + ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" @@ -5078,6 +5658,15 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: dependencies: type-fest "^0.21.3" +ansi-fragments@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz" + integrity sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w== + dependencies: + colorette "^1.0.7" + slice-ansi "^2.0.0" + strip-ansi "^5.0.0" + ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" @@ -5088,7 +5677,12 @@ ansi-html@^0.0.9: resolved "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz" integrity sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg== -ansi-regex@^5.0.1: +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + +ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -5098,7 +5692,7 @@ ansi-regex@^6.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -5135,7 +5729,7 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apexcharts@^3.33.2: +apexcharts@^3.33.2, apexcharts@^3.41.0: version "3.49.1" resolved "https://registry.npmjs.org/apexcharts/-/apexcharts-3.49.1.tgz" integrity sha512-MqGtlq/KQuO8j0BBsUJYlRG8VBctKwYdwuBtajHgHTmSgUU3Oai+8oYN/rKCXwXzrUlYA+GiMgotAIbXY2BCGw== @@ -5174,7 +5768,7 @@ apollo-cache-inmemory@^1.6.6: ts-invariant "^0.4.0" tslib "^1.10.0" -apollo-cache@1.3.5, apollo-cache@^1.3.5: +apollo-cache@^1.3.5, apollo-cache@1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz" integrity sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA== @@ -5233,7 +5827,7 @@ apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.14: tslib "^1.9.3" zen-observable-ts "^0.8.21" -apollo-utilities@1.3.4, apollo-utilities@^1.3.0, apollo-utilities@^1.3.4: +apollo-utilities@^1.3.0, apollo-utilities@^1.3.4, apollo-utilities@1.3.4: version "1.3.4" resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz" integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== @@ -5252,6 +5846,11 @@ appbase-js@^5.3.4: querystring "^0.2.0" url-parser-lite "^0.1.0" +appdirsjs@^1.2.4: + version "1.2.7" + resolved "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz" + integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== + arg@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" @@ -5269,6 +5868,13 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-query@^5.0.0, aria-query@^5.3.0, aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + aria-query@5.1.3: version "5.1.3" resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" @@ -5276,13 +5882,6 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" -aria-query@^5.0.0, aria-query@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" @@ -5415,6 +6014,23 @@ ast-types-flow@^0.0.8: resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== +ast-types@0.15.2: + version "0.15.2" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz" + integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== + dependencies: + tslib "^2.0.1" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async@^3.2.3: version "3.2.5" resolved "https://registry.npmjs.org/async/-/async-3.2.5.tgz" @@ -5494,6 +6110,11 @@ axobject-query@^3.2.1: dependencies: dequal "^2.0.3" +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + babel-jest@^27.4.2, babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" @@ -5607,6 +6228,13 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz" integrity sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw== +babel-plugin-transform-flow-enums@^0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz" + integrity sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ== + dependencies: + "@babel/plugin-syntax-flow" "^7.12.1" + babel-plugin-transform-react-remove-prop-types@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz" @@ -5670,7 +6298,7 @@ base-64@1.0.0: resolved "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz" integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -5701,12 +6329,21 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.20.3, body-parser@^1.20.3: +body-parser@^1.20.3: version "1.20.3" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz" integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== @@ -5757,7 +6394,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@3.0.3, braces@^3.0.3, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2, braces@3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -5769,7 +6406,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.23.0: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2, browserslist@^4.23.0, "browserslist@>= 4", "browserslist@>= 4.21.0", browserslist@>=4: version "4.23.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz" integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== @@ -5791,16 +6428,15 @@ buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@4.9.2: - version "4.9.2" - resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" + base64-js "^1.3.1" + ieee754 "^1.1.13" -buffer@^5.4.3: +buffer@^5.5.0: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -5808,6 +6444,15 @@ buffer@^5.4.3: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@4.9.2: + version "4.9.2" + resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + builtin-modules@^3.1.0: version "3.3.0" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" @@ -5837,6 +6482,25 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" + integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" + integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" + integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -5864,12 +6528,17 @@ camelcase-keys@6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" -camelcase@^5.3.1: +camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0, camelcase@^6.2.1: +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +camelcase@^6.2.1: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -5916,7 +6585,31 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -5954,11 +6647,26 @@ chokidar@^3.4.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chrome-launcher@^0.15.2: + version "0.15.2" + resolved "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz" + integrity sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ== + dependencies: + "@types/node" "*" + escape-string-regexp "^4.0.0" + is-wsl "^2.2.0" + lighthouse-logger "^1.0.0" + chrome-trace-event@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz" integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" @@ -5981,6 +6689,27 @@ clean-css@^5.2.2: dependencies: source-map "~0.6.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" @@ -5990,7 +6719,35 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clsx@^1.1.0, clsx@^1.2.1: +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +clsx@^1.1.0: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +clsx@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -6033,21 +6790,26 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + colord@^2.9.1: version "2.9.3" resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== +colorette@^1.0.7: + version "1.4.0" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + colorette@^2.0.10: version "2.0.20" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" @@ -6060,7 +6822,17 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.20.0, commander@^2.20.3: +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^2.20.3: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -6080,6 +6852,11 @@ commander@^8.3.0: resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^9.4.1: + version "9.5.0" + resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + common-tags@^1.8.0: version "1.8.2" resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" @@ -6092,14 +6869,14 @@ commondir@^1.0.1: compressible@~2.0.18: version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" -compression@^1.7.4: +compression@^1.7.1, compression@^1.7.4: version "1.8.1" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.8.1.tgz#4a45d909ac16509195a9a28bd91094889c180d79" + resolved "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz" integrity sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w== dependencies: bytes "3.1.2" @@ -6125,6 +6902,16 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== +connect@^3.6.5: + version "3.7.0" + resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" @@ -6152,16 +6939,16 @@ cookie-signature@1.0.6: resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== - cookie@^0.4.0: version "0.4.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + copy-to-clipboard@^3.3.1: version "3.3.3" resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" @@ -6191,6 +6978,26 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cosmiconfig@^5.0.5: + version "5.2.1" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cosmiconfig@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" @@ -6227,7 +7034,7 @@ cross-fetch@^3.0.4, cross-fetch@^3.1.5: dependencies: node-fetch "^2.6.12" -cross-spawn@7.0.5, cross-spawn@^6.0.5, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@7.0.5: version "7.0.5" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz" integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== @@ -6322,15 +7129,15 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: - mdn-data "2.0.4" + mdn-data "2.0.14" source-map "^0.6.1" -css-tree@^1.1.2, css-tree@^1.1.3: +css-tree@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== @@ -6338,6 +7145,14 @@ css-tree@^1.1.2, css-tree@^1.1.3: mdn-data "2.0.14" source-map "^0.6.1" +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + css-vendor@^2.0.8: version "2.0.8" resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz" @@ -6454,7 +7269,12 @@ csstype@^2.5.7: resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz" integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== -csstype@^3.0.2, csstype@^3.1.3: +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +csstype@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -6500,31 +7320,38 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -date-fns@^2.28.0: +date-fns@^2.0.0, date-fns@^2.25.0, date-fns@^2.28.0: version "2.30.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== dependencies: "@babel/runtime" "^7.21.0" -dayjs@^1.11.0, dayjs@^1.11.7: +dayjs@^1.10.7, dayjs@^1.11.0, dayjs@^1.11.7, dayjs@^1.8.15, dayjs@^1.8.17: version "1.11.11" resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz" integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== -debug@2.6.9, debug@^2.6.0: +debug@^2.2.0: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.5" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== +debug@^2.6.0: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: - ms "2.1.2" + ms "2.0.0" + +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" debug@^3.2.7: version "3.2.7" @@ -6533,6 +7360,25 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: + version "4.3.5" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + decimal.js@^10.2.1: version "10.4.3" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" @@ -6582,6 +7428,11 @@ deepmerge@^4.2.2: resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +deepmerge@^4.3.0: + version "4.3.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" @@ -6589,6 +7440,13 @@ default-gateway@^6.0.3: dependencies: execa "^5.0.0" +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" @@ -6617,16 +7475,21 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +denodeify@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/denodeify/-/denodeify-1.2.1.tgz" + integrity sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg== depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + dequal@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" @@ -6723,14 +7586,6 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -6740,16 +7595,24 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domelementtype@1: - version "1.3.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domelementtype@1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" @@ -6838,7 +7701,7 @@ echarts-for-react@^3.0.2: fast-deep-equal "^3.1.3" size-sensor "^1.0.1" -echarts@^5.3.3: +"echarts@^3.0.0 || ^4.0.0 || ^5.0.0", echarts@^5.3.3: version "5.5.0" resolved "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz" integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== @@ -6851,7 +7714,7 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@3.1.10, ejs@^3.1.6: +ejs@3.1.10: version "3.1.10" resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -6915,7 +7778,7 @@ enhanced-resolve@^5.17.1: graceful-fs "^4.2.4" tapable "^2.2.0" -entities@2.2.0, entities@^2.0.0: +entities@^2.0.0, entities@2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== @@ -6928,6 +7791,11 @@ env-cmd@^10.1.0: commander "^4.0.0" cross-spawn "^7.0.0" +envinfo@^7.10.0: + version "7.13.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" @@ -6942,6 +7810,14 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.3.4" +errorhandler@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz" + integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== + dependencies: + accepts "~1.3.7" + escape-html "~1.0.3" + es-abstract@^1.17.2, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz" @@ -7184,7 +8060,7 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.27.5: +eslint-plugin-import@^2.25.3, eslint-plugin-import@^2.27.5, eslint-plugin-import@>=1.4.0: version "2.29.1" resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== @@ -7279,7 +8155,7 @@ eslint-plugin-testing-library@^5.0.1: dependencies: "@typescript-eslint/utils" "^5.58.0" -eslint-scope@5.1.1, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -7295,6 +8171,14 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" @@ -7316,7 +8200,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: +eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0 || ^9.0.0", eslint@^8.0.0, eslint@^8.1.0, eslint@^8.3.0, "eslint@>= 6", eslint@>=7.0.0, eslint@>=7.28.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -7369,16 +8253,16 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esprima@1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz" integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A== -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esquery@^1.4.2: version "1.5.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" @@ -7393,7 +8277,12 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -7418,6 +8307,11 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0, event-target-shim@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -7435,7 +8329,7 @@ exec-sh@^0.2.0: dependencies: merge "^1.2.0" -execa@^5.0.0: +execa@^5.0.0, execa@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -7476,7 +8370,7 @@ expect@^29.0.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express@4.20.0, express@^4.17.3: +express@4.20.0: version "4.20.0" resolved "https://registry.npmjs.org/express/-/express-4.20.0.tgz" integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw== @@ -7528,7 +8422,7 @@ fast-diff@^1.1.2: resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9, fast-glob@^3.3.0: +fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -7549,7 +8443,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-xml-parser@4.2.5, fast-xml-parser@4.4.1, fast-xml-parser@^4.2.5: +fast-xml-parser@4.4.1: version "4.4.1" resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz" integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== @@ -7623,6 +8517,19 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" @@ -7636,6 +8543,15 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" @@ -7657,7 +8573,15 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -7687,7 +8611,17 @@ flatted@^3.2.9: resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -follow-redirects@1.15.6, follow-redirects@^1.0.0, follow-redirects@^1.15.6: +flow-enums-runtime@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz" + integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw== + +flow-parser@0.*: + version "0.237.2" + resolved "https://registry.npmjs.org/flow-parser/-/flow-parser-0.237.2.tgz" + integrity sha512-mvI/kdfr3l1waaPbThPA8dJa77nHXrfZIun+SWvFwSwDjmeByU7mGJGRmv1+7guU6ccyLV8e1lqZA1lD4iMGnQ== + +follow-redirects@1.15.6: version "1.15.6" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== @@ -7786,7 +8720,26 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0, fs-extra@^9.0.1: +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.1: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -7808,7 +8761,7 @@ fs.realpath@^1.0.0: fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: @@ -7836,7 +8789,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7889,7 +8842,7 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -7903,6 +8856,13 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" @@ -7984,7 +8944,7 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -8001,7 +8961,7 @@ graphql-tag@^2.12.6, graphql-tag@^2.4.2: dependencies: tslib "^2.1.0" -graphql@15.8.0: +"graphql@^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", "graphql@^0.11.3 || ^0.12.3 || ^0.13.0 || ^14.0.0 || ^15.0.0", "graphql@^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "graphql@^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "graphql@^15.0.0 || ^16.0.0", graphql@15.8.0: version "15.8.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz" integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== @@ -8074,6 +9034,37 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hermes-estree@0.19.1: + version "0.19.1" + resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.19.1.tgz" + integrity sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g== + +hermes-estree@0.20.1: + version "0.20.1" + resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.20.1.tgz" + integrity sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg== + +hermes-parser@0.19.1: + version "0.19.1" + resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.19.1.tgz" + integrity sha512-Vp+bXzxYJWrpEuJ/vXxUsLnt0+y4q9zyi4zUlkLqD8FKv4LjIfOvP69R/9Lty3dCyKh0E2BU7Eypqr63/rKT/A== + dependencies: + hermes-estree "0.19.1" + +hermes-parser@0.20.1: + version "0.20.1" + resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.20.1.tgz" + integrity sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA== + dependencies: + hermes-estree "0.20.1" + +hermes-profile-transformer@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz" + integrity sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ== + dependencies: + source-map "^0.7.3" + history@^5.0.3: version "5.3.0" resolved "https://registry.npmjs.org/history/-/history-5.3.0.tgz" @@ -8164,6 +9155,16 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -8175,16 +9176,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - http-parser-js@>=0.5.1: version "0.5.8" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" @@ -8199,7 +9190,7 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" -http-proxy-middleware@2.0.7, http-proxy-middleware@^2.0.3: +http-proxy-middleware@2.0.7: version "2.0.7" resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz" integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== @@ -8237,13 +9228,6 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.5.tgz" integrity sha512-fedL7PRwmeVkgyhu9hLeTBaI6wcGk7JGJswdaRsa5aUbkXI1kr1xZwTPBtaYPpwf56878iDek6VbVnuWMebJmw== -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" @@ -8251,21 +9235,28 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -idb@5.0.6: - version "5.0.6" - resolved "https://registry.npmjs.org/idb/-/idb-5.0.6.tgz" - integrity sha512-/PFvOWPzRcEPmlDt5jEvzVZVs0wyd/EvGvkDIcbBpGuMMLQKrTPG0TxvE2UJtgZtCQCmOtM2QD7yQJBVEjKGOw== - idb@^7.0.1: version "7.1.1" resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== +idb@5.0.6: + version "5.0.6" + resolved "https://registry.npmjs.org/idb/-/idb-5.0.6.tgz" + integrity sha512-/PFvOWPzRcEPmlDt5jEvzVZVs0wyd/EvGvkDIcbBpGuMMLQKrTPG0TxvE2UJtgZtCQCmOtM2QD7yQJBVEjKGOw== + identity-obj-proxy@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" @@ -8283,16 +9274,31 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== -immer@9.0.6: - version "9.0.6" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz" - integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== +image-size@^1.0.2: + version "1.2.1" + resolved "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz" + integrity sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw== + dependencies: + queue "6.0.2" immer@^9.0.21, immer@^9.0.7: version "9.0.21" resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== +immer@9.0.6: + version "9.0.6" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz" + integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" + integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" @@ -8327,7 +9333,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -8358,21 +9364,16 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ip@1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" - integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== +ipaddr.js@^2.0.1: + version "2.2.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: - version "2.2.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" - integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== - is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" @@ -8449,6 +9450,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" + integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== + is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -8466,6 +9472,11 @@ is-finalizationregistry@^1.0.2: dependencies: call-bind "^1.0.2" +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" @@ -8495,6 +9506,11 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz" integrity sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g== +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" @@ -8537,6 +9553,13 @@ is-plain-obj@^3.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" @@ -8603,6 +9626,11 @@ is-typedarray@^1.0.0: resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakmap@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" @@ -8623,7 +9651,12 @@ is-weakset@^2.0.3: call-bind "^1.0.7" get-intrinsic "^1.2.4" -is-wsl@^2.2.0: +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -8645,6 +9678,11 @@ isexe@^2.0.0: resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + isomorphic-unfetch@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz" @@ -8870,6 +9908,18 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" +jest-environment-node@^29.6.3: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" @@ -9004,6 +10054,15 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" @@ -9028,7 +10087,7 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" -jest-resolve@^27.4.2, jest-resolve@^27.5.1: +jest-resolve@*, jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== @@ -9183,6 +10242,18 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" +jest-validate@^29.6.3: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + jest-watch-typeahead@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz" @@ -9250,7 +10321,17 @@ jest-worker@^28.0.2: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.4.3: +jest-worker@^29.6.3: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +"jest@^27.0.0 || ^28.0.0", jest@^27.4.3: version "27.5.1" resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== @@ -9264,6 +10345,17 @@ jiti@^1.21.0: resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +joi@^17.2.1: + version "17.13.1" + resolved "https://registry.npmjs.org/joi/-/joi-17.13.1.tgz" + integrity sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg== + dependencies: + "@hapi/hoek" "^9.3.0" + "@hapi/topo" "^5.1.0" + "@sideway/address" "^4.1.5" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz" @@ -9289,6 +10381,41 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsc-android@^250231.0.0: + version "250231.0.0" + resolved "https://registry.npmjs.org/jsc-android/-/jsc-android-250231.0.0.tgz" + integrity sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw== + +jsc-safe-url@^0.2.2: + version "0.2.4" + resolved "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz" + integrity sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q== + +jscodeshift@^0.14.0: + version "0.14.0" + resolved "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.14.0.tgz" + integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" + babel-core "^7.0.0-bridge.0" + chalk "^4.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^4.0.4" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.21.0" + temp "^0.8.4" + write-file-atomic "^2.3.0" + jsdom@^16.6.0: version "16.7.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" @@ -9337,6 +10464,11 @@ json-buffer@3.0.1: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" @@ -9374,6 +10506,13 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" @@ -9457,7 +10596,7 @@ jss-plugin-vendor-prefixer@^10.10.0: css-vendor "^2.0.8" jss "10.10.0" -jss@10.10.0, jss@^10.10.0: +jss@^10.10.0, jss@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz" integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== @@ -9545,6 +10684,14 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lighthouse-logger@^1.0.0: + version "1.4.2" + resolved "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz" + integrity sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g== + dependencies: + debug "^2.6.9" + marky "^1.2.2" + lilconfig@^2.0.3, lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" @@ -9641,6 +10788,23 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +logkitty@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz" + integrity sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ== + dependencies: + ansi-fragments "^0.2.1" + dayjs "^1.8.15" + yargs "^15.1.0" + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -9679,6 +10843,14 @@ magic-string@^0.25.0, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -9705,6 +10877,11 @@ map-obj@^4.0.0: resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== +marky@^1.2.2: + version "1.2.5" + resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz" + integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" @@ -9732,6 +10909,11 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" +memoize-one@^5.0.0: + version "5.2.1" + resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz" @@ -9742,21 +10924,205 @@ merge-stream@^2.0.0: resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz" + integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -merge@2.1.1, merge@^1.2.0: - version "2.1.1" - resolved "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz" - integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== - methods@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +metro-babel-transformer@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.80.9.tgz" + integrity sha512-d76BSm64KZam1nifRZlNJmtwIgAeZhZG3fi3K+EmPOlrR8rDtBxQHDSN3fSGeNB9CirdTyabTMQCkCup6BXFSQ== + dependencies: + "@babel/core" "^7.20.0" + hermes-parser "0.20.1" + nullthrows "^1.1.1" + +metro-cache-key@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.80.9.tgz" + integrity sha512-hRcYGhEiWIdM87hU0fBlcGr+tHDEAT+7LYNCW89p5JhErFt/QaAkVx4fb5bW3YtXGv5BTV7AspWPERoIb99CXg== + +metro-cache@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-cache/-/metro-cache-0.80.9.tgz" + integrity sha512-ujEdSI43QwI+Dj2xuNax8LMo8UgKuXJEdxJkzGPU6iIx42nYa1byQ+aADv/iPh5sh5a//h5FopraW5voXSgm2w== + dependencies: + metro-core "0.80.9" + rimraf "^3.0.2" + +metro-config@^0.80.3, metro-config@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.80.9.tgz" + integrity sha512-28wW7CqS3eJrunRGnsibWldqgwRP9ywBEf7kg+uzUHkSFJNKPM1K3UNSngHmH0EZjomizqQA2Zi6/y6VdZMolg== + dependencies: + connect "^3.6.5" + cosmiconfig "^5.0.5" + jest-validate "^29.6.3" + metro "0.80.9" + metro-cache "0.80.9" + metro-core "0.80.9" + metro-runtime "0.80.9" + +metro-core@^0.80.3, metro-core@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.80.9.tgz" + integrity sha512-tbltWQn+XTdULkGdzHIxlxk4SdnKxttvQQV3wpqqFbHDteR4gwCyTR2RyYJvxgU7HELfHtrVbqgqAdlPByUSbg== + dependencies: + lodash.throttle "^4.1.1" + metro-resolver "0.80.9" + +metro-file-map@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.80.9.tgz" + integrity sha512-sBUjVtQMHagItJH/wGU9sn3k2u0nrCl0CdR4SFMO1tksXLKbkigyQx4cbpcyPVOAmGTVuy3jyvBlELaGCAhplQ== + dependencies: + anymatch "^3.0.3" + debug "^2.2.0" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + invariant "^2.2.4" + jest-worker "^29.6.3" + micromatch "^4.0.4" + node-abort-controller "^3.1.1" + nullthrows "^1.1.1" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +metro-minify-terser@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.80.9.tgz" + integrity sha512-FEeCeFbkvvPuhjixZ1FYrXtO0araTpV6UbcnGgDUpH7s7eR5FG/PiJz3TsuuPP/HwCK19cZtQydcA2QrCw446A== + dependencies: + terser "^5.15.0" + +metro-resolver@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.80.9.tgz" + integrity sha512-wAPIjkN59BQN6gocVsAvvpZ1+LQkkqUaswlT++cJafE/e54GoVkMNCmrR4BsgQHr9DknZ5Um/nKueeN7kaEz9w== + +metro-runtime@^0.80.3, metro-runtime@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.80.9.tgz" + integrity sha512-8PTVIgrVcyU+X/rVCy/9yxNlvXsBCk5JwwkbAm/Dm+Abo6NBGtNjWF0M1Xo/NWCb4phamNWcD7cHdR91HhbJvg== + dependencies: + "@babel/runtime" "^7.0.0" + +metro-source-map@^0.80.3, metro-source-map@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.80.9.tgz" + integrity sha512-RMn+XS4VTJIwMPOUSj61xlxgBvPeY4G6s5uIn6kt6HB6A/k9ekhr65UkkDD7WzHYs3a9o869qU8tvOZvqeQzgw== + dependencies: + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" + invariant "^2.2.4" + metro-symbolicate "0.80.9" + nullthrows "^1.1.1" + ob1 "0.80.9" + source-map "^0.5.6" + vlq "^1.0.0" + +metro-symbolicate@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.80.9.tgz" + integrity sha512-Ykae12rdqSs98hg41RKEToojuIW85wNdmSe/eHUgMkzbvCFNVgcC0w3dKZEhSsqQOXapXRlLtHkaHLil0UD/EA== + dependencies: + invariant "^2.2.4" + metro-source-map "0.80.9" + nullthrows "^1.1.1" + source-map "^0.5.6" + through2 "^2.0.1" + vlq "^1.0.0" + +metro-transform-plugins@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.80.9.tgz" + integrity sha512-UlDk/uc8UdfLNJhPbF3tvwajyuuygBcyp+yBuS/q0z3QSuN/EbLllY3rK8OTD9n4h00qZ/qgxGv/lMFJkwP4vg== + dependencies: + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.20.0" + nullthrows "^1.1.1" + +metro-transform-worker@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.80.9.tgz" + integrity sha512-c/IrzMUVnI0hSVVit4TXzt3A1GiUltGVlzCmLJWxNrBGHGrJhvgePj38+GXl1Xf4Fd4vx6qLUkKMQ3ux73bFLQ== + dependencies: + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" + metro "0.80.9" + metro-babel-transformer "0.80.9" + metro-cache "0.80.9" + metro-cache-key "0.80.9" + metro-minify-terser "0.80.9" + metro-source-map "0.80.9" + metro-transform-plugins "0.80.9" + nullthrows "^1.1.1" + +metro@^0.80.3, metro@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/metro/-/metro-0.80.9.tgz" + integrity sha512-Bc57Xf3GO2Xe4UWQsBj/oW6YfLPABEu8jfDVDiNmJvoQW4CO34oDPuYKe4KlXzXhcuNsqOtSxpbjCRRVjhhREg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.0" + "@babel/parser" "^7.20.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.20.0" + "@babel/types" "^7.20.0" + accepts "^1.3.7" + chalk "^4.0.0" + ci-info "^2.0.0" + connect "^3.6.5" + debug "^2.2.0" + denodeify "^1.2.1" + error-stack-parser "^2.0.6" + graceful-fs "^4.2.4" + hermes-parser "0.20.1" + image-size "^1.0.2" + invariant "^2.2.4" + jest-worker "^29.6.3" + jsc-safe-url "^0.2.2" + lodash.throttle "^4.1.1" + metro-babel-transformer "0.80.9" + metro-cache "0.80.9" + metro-cache-key "0.80.9" + metro-config "0.80.9" + metro-core "0.80.9" + metro-file-map "0.80.9" + metro-resolver "0.80.9" + metro-runtime "0.80.9" + metro-source-map "0.80.9" + metro-symbolicate "0.80.9" + metro-transform-plugins "0.80.9" + metro-transform-worker "0.80.9" + mime-types "^2.1.27" + node-fetch "^2.2.0" + nullthrows "^1.1.1" + rimraf "^3.0.2" + serialize-error "^2.1.0" + source-map "^0.5.6" + strip-ansi "^6.0.0" + throat "^5.0.0" + ws "^7.5.1" + yargs "^17.6.2" + micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.8" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" @@ -9765,7 +11131,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== @@ -9777,6 +11143,11 @@ mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, dependencies: mime-db "1.52.0" +mime@^2.4.1: + version "2.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -9805,7 +11176,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -9836,23 +11207,33 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +"monaco-editor@>= 0.21.0 < 1", "monaco-editor@>= 0.25.0 < 1": + version "0.49.0" + resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.49.0.tgz" + integrity sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ== -ms@2.1.2, ms@^2.1.1: +ms@^2.1.1, ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" @@ -9895,17 +11276,17 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -negotiator@~0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - -neo-async@^2.6.2: +neo-async@^2.5.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -9918,7 +11299,24 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-fetch@^2.6.1, node-fetch@^2.6.12: +nocache@^3.0.1: + version "3.0.4" + resolved "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz" + integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw== + +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + +node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12: 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== @@ -9940,6 +11338,11 @@ node-releases@^2.0.14: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-stream-zip@^1.9.1: + version "1.15.0" + resolved "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz" + integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -9975,18 +11378,28 @@ nprogress@^0.2.0: resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== -nth-check@^1.0.2, nth-check@^2.0.1: +nth-check@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" +nullthrows@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" + integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== + nwsapi@^2.2.0: version "2.2.10" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz" integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== +ob1@0.80.9: + version "0.80.9" + resolved "https://registry.npmjs.org/ob1/-/ob1-0.80.9.tgz" + integrity sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA== + object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" @@ -10089,7 +11502,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -oidc-client-ts@^2.4.0: +oidc-client-ts@^2.2.1, oidc-client-ts@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-2.4.0.tgz" integrity sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w== @@ -10097,6 +11510,13 @@ oidc-client-ts@^2.4.0: crypto-js "^4.2.0" jwt-decode "^3.1.2" +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -10106,7 +11526,7 @@ on-finished@2.4.1: on-headers@~1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz" integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== once@^1.3.0: @@ -10116,13 +11536,28 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" +open@^6.2.0: + version "6.4.0" + resolved "https://registry.npmjs.org/open/-/open-6.4.0.tgz" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +open@^7.0.3: + version "7.4.2" + resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz" @@ -10173,7 +11608,29 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -p-limit@^2.0.0, p-limit@^2.2.0: +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -10241,6 +11698,14 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" @@ -10302,7 +11767,7 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.10, path-to-regexp@0.1.12: +path-to-regexp@0.1.12: version "0.1.12" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== @@ -10332,11 +11797,23 @@ pify@^2.3.0: resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -pirates@^4.0.1, pirates@^4.0.4: +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.6: version "4.0.6" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -10896,7 +12373,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.31, postcss@^8.2.14, postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4: +postcss@8.4.31: version "8.4.31" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== @@ -10922,7 +12399,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.7: +prettier@>=2.0.0, prettier@2.8.7: version "2.8.7" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz" integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== @@ -10940,6 +12417,26 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" +pretty-format@^26.5.2: + version "26.6.2" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" @@ -10959,7 +12456,16 @@ pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^29.0.0, pretty-format@^29.7.0: +pretty-format@^29.0.0: + version "29.7.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -10973,7 +12479,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -promise@^8.1.0: +promise@^8.1.0, promise@^8.3.0: version "8.3.0" resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz" integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== @@ -10988,7 +12494,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1, prop-types@>=15: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -11020,16 +12526,16 @@ psl@^1.1.33: resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== - punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + q@^1.1.2: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" @@ -11049,16 +12555,16 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +querystring@^0.2.0, querystring@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + querystring@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== -querystring@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== - querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -11069,6 +12575,13 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue@6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" @@ -11167,7 +12680,15 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^17.0.2: +react-devtools-core@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-5.2.0.tgz" + integrity sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A== + dependencies: + shell-quote "^1.6.1" + ws "^7" + +"react-dom@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, react-dom@>=15, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -11221,20 +12742,30 @@ react-if@^4.1.1: resolved "https://registry.npmjs.org/react-if/-/react-if-4.1.5.tgz" integrity sha512-Uk+Ub2gC83PAakuU4+7iLdTEP4LPi2ihNEPCtz/vr8SLGbzkMApbpYbkDZ5z9zYXurd0gg+EK/bpOLFFC1r1eQ== -react-is@^16.13.1, react-is@^16.7.0: +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: + 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== + +react-is@^16.13.1: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.1, react-is@^17.0.2: +react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: version "17.0.2" resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0, react-is@^18.2.0: - 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== +react-is@^17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-native-get-random-values@^1.4.0: version "1.11.0" @@ -11250,12 +12781,55 @@ react-native-url-polyfill@^1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" +react-native@*, react-native@>=0.56: + version "0.74.2" + resolved "https://registry.npmjs.org/react-native/-/react-native-0.74.2.tgz" + integrity sha512-EBMBjPPL4/GjHMP4NqsZabT3gI5WU9cSmduABGAGrd8uIcmTZ5F2Ng9k6gFmRm7n8e8CULxDNu98ZpQfBjl7Bw== + dependencies: + "@jest/create-cache-key-function" "^29.6.3" + "@react-native-community/cli" "13.6.8" + "@react-native-community/cli-platform-android" "13.6.8" + "@react-native-community/cli-platform-ios" "13.6.8" + "@react-native/assets-registry" "0.74.84" + "@react-native/codegen" "0.74.84" + "@react-native/community-cli-plugin" "0.74.84" + "@react-native/gradle-plugin" "0.74.84" + "@react-native/js-polyfills" "0.74.84" + "@react-native/normalize-colors" "0.74.84" + "@react-native/virtualized-lists" "0.74.84" + abort-controller "^3.0.0" + anser "^1.4.9" + ansi-regex "^5.0.0" + base64-js "^1.5.1" + chalk "^4.0.0" + event-target-shim "^5.0.1" + flow-enums-runtime "^0.0.6" + invariant "^2.2.4" + jest-environment-node "^29.6.3" + jsc-android "^250231.0.0" + memoize-one "^5.0.0" + metro-runtime "^0.80.3" + metro-source-map "^0.80.3" + mkdirp "^0.5.1" + nullthrows "^1.1.1" + pretty-format "^26.5.2" + promise "^8.3.0" + react-devtools-core "^5.0.0" + react-refresh "^0.14.0" + react-shallow-renderer "^16.15.0" + regenerator-runtime "^0.13.2" + scheduler "0.24.0-canary-efb381bbf-20230505" + stacktrace-parser "^0.1.10" + whatwg-fetch "^3.0.0" + ws "^6.2.2" + yargs "^17.6.2" + react-oidc-context@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-2.3.1.tgz" integrity sha512-WdhmEU6odNzMk9pvOScxUkf6/1aduiI/nQryr7+iCl2VDnYLASDTIV/zy58KuK4VXG3fBaRKukc/mRpMjF9a3Q== -react-redux@^6.0.1, react-redux@^7.2.6: +"react-redux@^7.2.1 || ^8.0.2", react-redux@^7.2.6: version "7.2.9" resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz" integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ== @@ -11267,11 +12841,16 @@ react-redux@^6.0.1, react-redux@^7.2.6: prop-types "^15.7.2" react-is "^17.0.2" -react-refresh@^0.11.0: +react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0": version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-refresh@^0.14.0: + version "0.14.2" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== + react-router-dom@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0.tgz" @@ -11341,6 +12920,14 @@ react-scripts@^5.0.1: optionalDependencies: fsevents "^2.3.2" +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" + react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" @@ -11351,7 +12938,7 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^17.0.2: +react@*, "react@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react@^15.0.0 || >=16.0.0", "react@^15.3.0 || 16 || 17 || 18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17.0.0 || ^18", "react@^16.x || ^17.x || ^18.x", react@^17.0.0, "react@^17.0.0 || ^18.0.0", react@^17.0.2, "react@^17.0.2 || ^18.0.0", react@<18.0.0, "react@>= 16", "react@>= 16.8", react@>=0.13, react@>=0.13.x, react@>=0.14.x, react@>=15, react@>=16.3.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0, "react@~0.13.x || ~0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0", react@17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -11359,6 +12946,13 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" +react@18.2.0: + 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" + read-cache@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" @@ -11379,7 +12973,7 @@ readable-stream@^2.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6: +readable-stream@^3.0.6, readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -11388,6 +12982,19 @@ readable-stream@^3.0.6: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" @@ -11395,6 +13002,21 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +readline@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz" + integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg== + +recast@^0.21.0: + version "0.21.5" + resolved "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz" + integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== + dependencies: + ast-types "0.15.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + recursive-readdir@^2.2.2: version "2.2.3" resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" @@ -11415,7 +13037,7 @@ redux-thunk@^2.3.0, redux-thunk@^2.4.2: resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz" integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== -redux@^4.0.0, redux@^4.2.1: +redux@^4, redux@^4.0.0, redux@^4.2.1: 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== @@ -11447,6 +13069,11 @@ regenerate@^1.4.2: resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== +regenerator-runtime@^0.13.2: + version "0.13.11" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-runtime@^0.13.9: version "0.13.11" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" @@ -11529,6 +13156,11 @@ require-from-string@^2.0.2: resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" @@ -11546,6 +13178,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -11556,7 +13193,7 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url-loader@5.0.0, resolve-url-loader@^4.0.0: +resolve-url-loader@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz" integrity sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg== @@ -11595,6 +13232,14 @@ response-iterator@^0.2.6: resolved "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz" integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" @@ -11617,6 +13262,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rollup-plugin-terser@^7.0.0: version "7.0.2" resolved "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz" @@ -11627,7 +13279,7 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup@3.29.5, rollup@^2.43.1: +rollup@3.29.5: version "3.29.5" resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz" integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== @@ -11651,7 +13303,7 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -11708,14 +13360,12 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== +scheduler@0.24.0-canary-efb381bbf-20230505: + version "0.24.0-canary-efb381bbf-20230505" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz" + integrity sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA== dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" + loose-envify "^1.1.0" schema-utils@^2.6.5: version "2.7.1" @@ -11726,7 +13376,25 @@ schema-utils@^2.6.5: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: +schema-utils@^3.0.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.1: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -11745,12 +13413,21 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: +selfsigned@^2.1.1, selfsigned@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== @@ -11758,17 +13435,57 @@ selfsigned@^2.1.1: "@types/node-forge" "^1.3.0" node-forge "^1" -semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: +semver@^5.6.0: + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: +semver@^7.3.2: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.3.5: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.3.7: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.5.2: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.5.3: + version "7.6.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@^7.5.4: version "7.6.2" resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== -send@0.18.0, send@0.19.0: +send@0.19.0: version "0.19.0" resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== @@ -11787,6 +13504,11 @@ send@0.18.0, send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +serialize-error@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz" + integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" @@ -11814,7 +13536,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.16.0: +serve-static@^1.13.1, serve-static@1.16.0: version "1.16.0" resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz" integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA== @@ -11824,6 +13546,11 @@ serve-static@1.16.0: parseurl "~1.3.3" send "0.18.0" +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" @@ -11856,6 +13583,13 @@ setprototypeof@1.2.0: resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" @@ -11873,7 +13607,7 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3, shell-quote@^1.8.1: +shell-quote@^1.6.1, shell-quote@^1.7.3, shell-quote@^1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== @@ -11938,6 +13672,15 @@ slash@^4.0.0: resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slice-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" @@ -11966,7 +13709,7 @@ source-map-loader@^3.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.1" -source-map-support@^0.5.6, source-map-support@~0.5.20: +source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11974,15 +13717,20 @@ source-map-support@^0.5.6, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.7.3: version "0.7.4" @@ -11996,6 +13744,21 @@ source-map@^0.8.0-beta.0: dependencies: whatwg-url "^7.0.0" +source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" @@ -12046,6 +13809,13 @@ stackframe@^1.3.4: resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz" integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + state-local@^1.0.6: version "1.0.7" resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz" @@ -12058,16 +13828,21 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - "statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" @@ -12075,6 +13850,20 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -12105,7 +13894,7 @@ string-natural-compare@^3.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12169,20 +13958,6 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" @@ -12199,6 +13974,20 @@ stringify-object@^3.3.0: dependencies: ansi-regex "^5.0.1" +strip-ansi@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -12281,6 +14070,11 @@ sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +sudo-prompt@^9.0.0: + version "9.2.1" + resolved "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz" + integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" @@ -12288,7 +14082,14 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -12341,7 +14142,7 @@ svg.filter.js@^2.0.2: dependencies: svg.js "^2.2.5" -svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5: +svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5, svg.js@>=2.3.x: version "2.7.1" resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz" integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA== @@ -12465,6 +14266,13 @@ temp-dir@^2.0.0: resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== +temp@^0.8.4: + version "0.8.4" + resolved "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + tempy@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz" @@ -12494,7 +14302,7 @@ terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.3.10: serialize-javascript "^6.0.1" terser "^5.26.0" -terser@^5.0.0, terser@^5.10.0, terser@^5.26.0: +terser@^5.0.0, terser@^5.10.0, terser@^5.15.0, terser@^5.26.0: version "5.31.1" resolved "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz" integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== @@ -12532,11 +14340,24 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + throat@^6.0.1: version "6.0.2" resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== +through2@^2.0.1: + version "2.0.5" + resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + thunky@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" @@ -12637,21 +14458,31 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" - integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@^1.10.0, tslib@^1.8.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^1.11.1, tslib@^1.8.0: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3: +tslib@^1.8.1: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -12688,11 +14519,16 @@ type-fest@^0.20.2: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-fest@^0.21.3: +type-fest@^0.21.3, "type-fest@>=0.17.0 <5.0.0": version "0.21.3" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" @@ -12752,6 +14588,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +"typescript@^3.2.1 || ^4", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + ulid@2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz" @@ -12820,6 +14661,11 @@ universal-cookie@^4.0.4: "@types/cookie" "^0.3.3" cookie "^0.4.0" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + universalify@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" @@ -12830,7 +14676,7 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== @@ -12878,7 +14724,7 @@ url-search-params-polyfill@^7.0.0: resolved "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-7.0.1.tgz" integrity sha512-bAw7L2E+jn9XHG5P9zrPnHdO0yJub4U+yXJOdpcpkr7OBd9T8oll4lUos0iSGRcDvfZoLUKfx9a6aNmIhJ4+mQ== -url@0.11.0, url@^0.11.0: +url@^0.11.0, url@0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== @@ -12911,21 +14757,31 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@3.4.0, uuid@^3.0.0, uuid@^3.2.1: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - uuid@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz" integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== +uuid@^3.0.0: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^3.2.1: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz" @@ -12940,6 +14796,11 @@ vary@~1.1.2: resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vlq@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz" + integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" @@ -12984,6 +14845,13 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + web-vitals@^2.1.4: version "2.1.4" resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz" @@ -13009,7 +14877,7 @@ webidl-conversions@^6.1.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-dev-middleware@5.3.4, webpack-dev-middleware@^5.3.4: +webpack-dev-middleware@5.3.4: version "5.3.4" resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz" integrity sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q== @@ -13020,7 +14888,7 @@ webpack-dev-middleware@5.3.4, webpack-dev-middleware@^5.3.4: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.6.0: +webpack-dev-server@^4.6.0, "webpack-dev-server@3.x || 4.x || 5.x": version "4.15.2" resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz" integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== @@ -13085,7 +14953,7 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.64.4, webpack@^5.94.0: +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.64.4, webpack@^5.94.0, "webpack@>= 4", webpack@>=2, "webpack@>=4.43.0 <6.0.0": version "5.94.0" resolved "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz" integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== @@ -13114,7 +14982,7 @@ webpack@^5.64.4, webpack@^5.94.0: watchpack "^2.4.1" webpack-sources "^3.2.3" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: +websocket-driver@^0.7.4, websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -13135,7 +15003,7 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" -whatwg-fetch@^3.6.2: +whatwg-fetch@^3.0.0, whatwg-fetch@^3.6.2: version "3.6.20" resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz" integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== @@ -13171,7 +15039,16 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -whatwg-url@^8.0.0, whatwg-url@^8.5.0: +whatwg-url@^8.0.0: + version "8.7.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== @@ -13219,6 +15096,11 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: version "1.1.15" resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz" @@ -13427,6 +15309,15 @@ workbox-window@6.6.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -13450,6 +15341,15 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@^2.3.0: + version "2.4.3" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" @@ -13460,10 +15360,24 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@^7.4.6, ws@^8.13.0, ws@^8.17.1: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +ws@^6.2.2: + version "6.2.3" + resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.2.3: + version "6.2.3" + resolved "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== + dependencies: + async-limiter "~1.0.0" + +ws@^7, ws@^7.4.6, ws@^7.5.1: + version "7.5.10" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== xml-name-validator@^3.0.0: version "3.0.0" @@ -13483,6 +15397,16 @@ xss@^1.0.11: commander "^2.20.3" cssfilter "0.0.10" +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" @@ -13498,16 +15422,51 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.1: + version "2.4.5" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + yaml@^2.3.4: version "2.4.5" resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz" integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^20.2.2: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^15.1.0: + version "15.4.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" @@ -13521,6 +15480,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" @@ -13539,14 +15511,6 @@ yup@^0.32.11: property-expr "^2.0.4" toposort "^2.0.2" -zen-observable-ts@0.8.19: - version "0.8.19" - resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz" - integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== - dependencies: - tslib "^1.9.3" - zen-observable "^0.8.0" - zen-observable-ts@^0.8.21: version "0.8.21" resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz" @@ -13562,16 +15526,24 @@ zen-observable-ts@^1.2.5: dependencies: zen-observable "0.8.15" -zen-observable@0.8.15, zen-observable@^0.8.0: - version "0.8.15" - resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== +zen-observable-ts@0.8.19: + version "0.8.19" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz" + integrity sha512-u1a2rpE13G+jSzrg3aiCqXU5tN2kw41b+cBZGmnc+30YimdkKiDj9bTowcB41eL77/17RF/h+393AuVgShyheQ== + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" zen-observable@^0.7.0: version "0.7.1" resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.7.1.tgz" integrity sha512-OI6VMSe0yeqaouIXtedC+F55Sr6r9ppS7+wTbSexkYdHbdt4ctTuPNXP/rwm7GTVI63YBc+EBT0b0tl7YnJLRg== +zen-observable@^0.8.0, zen-observable@0.8.15: + version "0.8.15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== + zen-push@0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/zen-push/-/zen-push-0.2.1.tgz" From c5bcd75f843d25ff5343eaffd9418a0e04164fab Mon Sep 17 00:00:00 2001 From: dlpzx Date: Mon, 1 Sep 2025 12:07:45 +0200 Subject: [PATCH 13/22] GenAI metadata generation - improvements --- .../modules/s3_datasets/services/dataset_table_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index a9486225b..3c0b4718d 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -208,7 +208,7 @@ def _delete_dataset_table_read_permission(session, table_uri): # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' # ) def generate_metadata_for_table(uri, metadata_types, sample_data): - metadataTypesForTable = [MetadataGenerationTypes.Description, MetadataGenerationTypes.Tag] + metadataTypesForTable = [MetadataGenerationTypes.Description.value, MetadataGenerationTypes.Tag.value] table_metadata_types = [item for item in metadata_types if item in metadataTypesForTable] context = get_context() with context.db_engine.scoped_session() as session: From 4dfbc88a153a9de5bc64d361b99e4eb7354cc94f Mon Sep 17 00:00:00 2001 From: kalosp Date: Fri, 10 Oct 2025 14:34:55 +0300 Subject: [PATCH 14/22] add resource_threshold --- backend/dataall/base/db/exceptions.py | 23 ++++++++ .../core/resource_threshold/__init__.py | 0 .../db/resource_threshold_models.py | 12 ++++ .../db/resource_threshold_repositories.py | 56 +++++++++++++++++++ .../resource_threshold/services/__init__.py | 0 .../services/resource_threshold_service.py | 42 ++++++++++++++ backend/migrations/env.py | 1 + ...2258cd8d6e9f_resource_threshholds_added.py | 37 ++++++++++++ 8 files changed, 171 insertions(+) create mode 100644 backend/dataall/core/resource_threshold/__init__.py create mode 100644 backend/dataall/core/resource_threshold/db/resource_threshold_models.py create mode 100644 backend/dataall/core/resource_threshold/db/resource_threshold_repositories.py create mode 100644 backend/dataall/core/resource_threshold/services/__init__.py create mode 100644 backend/dataall/core/resource_threshold/services/resource_threshold_service.py create mode 100644 backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py diff --git a/backend/dataall/base/db/exceptions.py b/backend/dataall/base/db/exceptions.py index c3c5d91e9..77329f585 100644 --- a/backend/dataall/base/db/exceptions.py +++ b/backend/dataall/base/db/exceptions.py @@ -193,3 +193,26 @@ def __init__(self, action, message): def __str__(self): return f'{self.message}' + + +class ResourceThresholdExceeded(Exception): + def __init__(self, username, action): + self.username = username + self.action = action + self.message = f""" + An error occurred (ResourceThresholdExceeded) when calling {self.action} operation: + Requests exceeded max daily invocation count for User: {self.username} + """ + + def __str__(self): + return f'{self.message}' + + +class ModelGuardrailException(Exception): + def __init__(self, message): + self.message = f""" + An error occurred (ModelGuardrailException) when invoking the model: {message} + """ + + def __str__(self): + return f'{self.message}' diff --git a/backend/dataall/core/resource_threshold/__init__.py b/backend/dataall/core/resource_threshold/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/dataall/core/resource_threshold/db/resource_threshold_models.py b/backend/dataall/core/resource_threshold/db/resource_threshold_models.py new file mode 100644 index 000000000..ba5e9bece --- /dev/null +++ b/backend/dataall/core/resource_threshold/db/resource_threshold_models.py @@ -0,0 +1,12 @@ +from dataall.base.db import Base, utils +from sqlalchemy import String, Integer, Column, Date +from datetime import date + + +class ResourceThreshold(Base): + __tablename__ = 'resource_threshold' + actionUri = Column(String(64), primary_key=True, default=utils.uuid('resource_threshold')) + username = Column(String(64), nullable=False) + actionType = Column(String(64), nullable=False) + date = Column(Date, default=date.today, nullable=False) + count = Column(Integer, default=1, nullable=False) diff --git a/backend/dataall/core/resource_threshold/db/resource_threshold_repositories.py b/backend/dataall/core/resource_threshold/db/resource_threshold_repositories.py new file mode 100644 index 000000000..90bac8c35 --- /dev/null +++ b/backend/dataall/core/resource_threshold/db/resource_threshold_repositories.py @@ -0,0 +1,56 @@ +from dataall.core.resource_threshold.db.resource_threshold_models import ResourceThreshold +from sqlalchemy import and_ +from datetime import date + + +class ResourceThresholdRepository: + @staticmethod + def get_count_today(session, username, action_type): + amount = ( + session.query(ResourceThreshold.count) + .filter( + and_( + ResourceThreshold.username == username, + ResourceThreshold.actionType == action_type, + ResourceThreshold.date == date.today(), + ) + ) + .scalar() + ) + return amount if amount else 0 + + @staticmethod + def add_entry(session, username, action_type): + user_entry = ResourceThresholdRepository._get_user_entry(session, username, action_type) + if user_entry: + session.query(ResourceThreshold).filter( + and_( + ResourceThreshold.username == username, + ResourceThreshold.actionType == action_type, + ) + ).update({ResourceThreshold.count: 1, ResourceThreshold.date: date.today()}, synchronize_session=False) + session.commit() + else: + action_entry = ResourceThreshold(username=username, actionType=action_type) + session.add(action_entry) + session.commit() + + @staticmethod + def increment_count(session, username, action_type): + session.query(ResourceThreshold).filter( + and_( + ResourceThreshold.username == username, + ResourceThreshold.actionType == action_type, + ResourceThreshold.date == date.today(), + ) + ).update({ResourceThreshold.count: ResourceThreshold.count + 1}, synchronize_session=False) + session.commit() + + @staticmethod + def _get_user_entry(session, username, action_type): + entry = ( + session.query(ResourceThreshold) + .filter(and_(ResourceThreshold.username == username, ResourceThreshold.actionType == action_type)) + .first() + ) + return entry diff --git a/backend/dataall/core/resource_threshold/services/__init__.py b/backend/dataall/core/resource_threshold/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/backend/dataall/core/resource_threshold/services/resource_threshold_service.py b/backend/dataall/core/resource_threshold/services/resource_threshold_service.py new file mode 100644 index 000000000..53574ae14 --- /dev/null +++ b/backend/dataall/core/resource_threshold/services/resource_threshold_service.py @@ -0,0 +1,42 @@ +from dataall.core.resource_threshold.db.resource_threshold_repositories import ResourceThresholdRepository +from dataall.base.db import exceptions +from functools import wraps +from dataall.base.config import config +from dataall.base.context import get_context + +import logging + +log = logging.getLogger(__name__) + + +class ResourceThresholdService: + @staticmethod + def check_invocation_count(action_type, max_count_config_path): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + context = get_context() + with context.db_engine.scoped_session() as session: + count = ResourceThresholdRepository.get_count_today( + session=session, username=context.username, action_type=action_type + ) + max_count = config.get_property(max_count_config_path, 10) + log.info( + f'User {context.username} has invoked {action_type} {count} times today of max {max_count}' + ) + if count < max_count: + if count == 0: + ResourceThresholdRepository.add_entry( + session=session, username=context.username, action_type=action_type + ) + else: + ResourceThresholdRepository.increment_count( + session=session, username=context.username, action_type=action_type + ) + return func(*args, **kwargs) + else: + raise exceptions.ResourceThresholdExceeded(username=context.username, action=action_type) + + return wrapper + + return decorator diff --git a/backend/migrations/env.py b/backend/migrations/env.py index 2816677cc..84b6a1094 100644 --- a/backend/migrations/env.py +++ b/backend/migrations/env.py @@ -20,6 +20,7 @@ from dataall.modules.omics.db.omics_models import OmicsWorkflow, OmicsRun from dataall.modules.metadata_forms.db.metadata_form_models import * from dataall.modules.redshift_datasets.db.redshift_models import RedshiftDataset, RedshiftTable, RedshiftConnection +from dataall.core.resource_threshold.db.resource_threshold_models import ResourceThreshold # fmt: on # enable ruff-format back diff --git a/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py b/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py new file mode 100644 index 000000000..1a4f76741 --- /dev/null +++ b/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py @@ -0,0 +1,37 @@ +"""resource_threshholds_added + +Revision ID: 2258cd8d6e9f +Revises: 5a798acc6282 +Create Date: 2024-08-22 12:31:38.465650 + +""" + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2258cd8d6e9f' +down_revision = '5a798acc6282' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + 'resource_threshold', + sa.Column('actionUri', sa.String(length=64), nullable=False), + sa.Column('username', sa.String(length=64), nullable=False), + sa.Column('actionType', sa.String(length=64), nullable=False), + sa.Column('date', sa.Date(), nullable=False), + sa.Column('count', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('actionUri'), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('resource_threshold') + # ### end Alembic commands ### From d986e2023f7afb1148fa2e9f28853cabc96d7b35 Mon Sep 17 00:00:00 2001 From: kalosp Date: Fri, 10 Oct 2025 14:38:57 +0300 Subject: [PATCH 15/22] apply resource_thresholds --- .../s3_datasets/services/dataset_location_service.py | 10 ++++------ .../modules/s3_datasets/services/dataset_service.py | 10 ++++------ .../s3_datasets/services/dataset_table_service.py | 10 ++++------ .../2258cd8d6e9f_resource_threshholds_added.py | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py index 2832554b7..c270448c9 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_location_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_location_service.py @@ -5,8 +5,7 @@ from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService -##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged -##from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService +from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.base.db.exceptions import ResourceAlreadyExists from dataall.modules.s3_datasets.services.dataset_service import DatasetService @@ -153,10 +152,9 @@ def get_folder_restricted_information(uri: str, folder: DatasetStorageLocation): @staticmethod @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_FOLDER, parent_resource=_get_dataset_uri) - ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged - # @ResourceThresholdService.check_invocation_count( - # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' - # ) + @ResourceThresholdService.check_invocation_count( + 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' + ) def generate_metadata_for_folder(uri, metadata_types): context = get_context() with context.db_engine.scoped_session() as session: diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index 4759628f4..c0fc35eee 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -20,8 +20,7 @@ from dataall.core.stacks.db.stack_models import Stack from dataall.core.tasks.db.task_models import Task -##TODO -##from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService +from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.modules.s3_datasets.db.dataset_bucket_repositories import DatasetBucketRepository from dataall.modules.shares_base.db.share_object_repositories import ShareObjectRepository @@ -593,10 +592,9 @@ def list_dataset_tables_folders(uri, filter): @staticmethod @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) @ResourcePolicyService.has_resource_permission(UPDATE_DATASET) - ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged - # @ResourceThresholdService.check_invocation_count( - # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' - # ) + @ResourceThresholdService.check_invocation_count( + 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' + ) def generate_metadata_for_dataset(uri, metadata_types): context = get_context() with context.db_engine.scoped_session() as session: diff --git a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py index 3c0b4718d..aef0b4800 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_table_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_table_service.py @@ -3,8 +3,7 @@ from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService -##TODO -##from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService +from dataall.core.resource_threshold.services.resource_threshold_service import ResourceThresholdService from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.core.environment.services.environment_service import EnvironmentService from dataall.modules.s3_datasets.aws.athena_table_client import AthenaTableClient @@ -203,10 +202,9 @@ def _delete_dataset_table_read_permission(session, table_uri): @staticmethod @ResourcePolicyService.has_resource_permission(UPDATE_DATASET_TABLE, parent_resource=_get_dataset_uri) - ##TODO Uncomment the following to use the ResourceThresholdService once https://github.com/data-dot-all/dataall/pull/1653 is merged - # @ResourceThresholdService.check_invocation_count( - # 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' - # ) + @ResourceThresholdService.check_invocation_count( + 'metadata', 'modules.s3_datasets.features.generate_metadata_ai.max_count_per_day' + ) def generate_metadata_for_table(uri, metadata_types, sample_data): metadataTypesForTable = [MetadataGenerationTypes.Description.value, MetadataGenerationTypes.Tag.value] table_metadata_types = [item for item in metadata_types if item in metadataTypesForTable] diff --git a/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py b/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py index 1a4f76741..9d21b0f29 100644 --- a/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py +++ b/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = '2258cd8d6e9f' -down_revision = '5a798acc6282' +down_revision = 'ba2da94739ab' branch_labels = None depends_on = None From d1486e2ce4a565c45460df3631f5a3db89a27b96 Mon Sep 17 00:00:00 2001 From: kalosp Date: Fri, 10 Oct 2025 14:55:52 +0300 Subject: [PATCH 16/22] upgrade boto3 across --- backend/dataall/base/cdkproxy/requirements.txt | 4 ++-- backend/requirements.txt | 2 +- deploy/requirements.txt | 4 ++-- tests_new/integration_tests/requirements.txt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/dataall/base/cdkproxy/requirements.txt b/backend/dataall/base/cdkproxy/requirements.txt index 894bb006d..fb087a0b0 100644 --- a/backend/dataall/base/cdkproxy/requirements.txt +++ b/backend/dataall/base/cdkproxy/requirements.txt @@ -1,6 +1,6 @@ aws-cdk-lib==2.208.0 -boto3==1.39.7 -boto3-stubs==1.35.26 +boto3==1.40.48 +boto3-stubs==1.40.48 cdk-nag==2.7.2 fastapi == 0.116.1 PyYAML==6.0.2 diff --git a/backend/requirements.txt b/backend/requirements.txt index 3903bbd8a..2003db8dc 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ ariadne==0.26.2 aws-xray-sdk==2.4.3 -boto3==1.39.7 +boto3==1.40.48 fastapi == 0.116.1 nanoid==2.0.0 opensearch-py==3.0.0 diff --git a/deploy/requirements.txt b/deploy/requirements.txt index ed048e7b7..a1fc689fa 100644 --- a/deploy/requirements.txt +++ b/deploy/requirements.txt @@ -1,6 +1,6 @@ aws-cdk-lib==2.208.0 -boto3==1.35.26 -boto3-stubs==1.35.26 +boto3==1.40.48 +boto3-stubs==1.40.48 cdk-nag==2.7.2 typeguard==4.2.1 cdk-klayers==0.3.0 diff --git a/tests_new/integration_tests/requirements.txt b/tests_new/integration_tests/requirements.txt index 13071e25d..d555c09e4 100644 --- a/tests_new/integration_tests/requirements.txt +++ b/tests_new/integration_tests/requirements.txt @@ -1,5 +1,5 @@ assertpy==1.1.0 -boto3==1.35.26 +boto3==1.40.48 munch==2.5.0 pytest==7.3.1 pytest-cov==3.0.0 From 7f3383d02b8d1276e419121e4f0aec0e02046d71 Mon Sep 17 00:00:00 2001 From: kalosp Date: Mon, 13 Oct 2025 17:08:16 +0300 Subject: [PATCH 17/22] fix migration filename --- ...holds_added.py => 2258cd8d6e9f_resource_thresholds_added.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/migrations/versions/{2258cd8d6e9f_resource_threshholds_added.py => 2258cd8d6e9f_resource_thresholds_added.py} (97%) diff --git a/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py similarity index 97% rename from backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py rename to backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py index 9d21b0f29..a5db5e5db 100644 --- a/backend/migrations/versions/2258cd8d6e9f_resource_threshholds_added.py +++ b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py @@ -1,4 +1,4 @@ -"""resource_threshholds_added +"""resource_thresholds_added Revision ID: 2258cd8d6e9f Revises: 5a798acc6282 From 32e679a00a6cdcae7b0474af8b93c49d43b45212 Mon Sep 17 00:00:00 2001 From: kalosp Date: Mon, 13 Oct 2025 17:08:31 +0300 Subject: [PATCH 18/22] fix argument name --- .../resource_threshold/services/resource_threshold_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/dataall/core/resource_threshold/services/resource_threshold_service.py b/backend/dataall/core/resource_threshold/services/resource_threshold_service.py index 53574ae14..882e19186 100644 --- a/backend/dataall/core/resource_threshold/services/resource_threshold_service.py +++ b/backend/dataall/core/resource_threshold/services/resource_threshold_service.py @@ -11,7 +11,7 @@ class ResourceThresholdService: @staticmethod - def check_invocation_count(action_type, max_count_config_path): + def check_invocation_count(action_type, max_daily_count_config_path): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): @@ -20,7 +20,7 @@ def wrapper(*args, **kwargs): count = ResourceThresholdRepository.get_count_today( session=session, username=context.username, action_type=action_type ) - max_count = config.get_property(max_count_config_path, 10) + max_count = config.get_property(max_daily_count_config_path, 10) log.info( f'User {context.username} has invoked {action_type} {count} times today of max {max_count}' ) From 310dfe92843c47054fac555ad79e78eed286e108 Mon Sep 17 00:00:00 2001 From: kalosp Date: Wed, 15 Oct 2025 11:18:15 +0300 Subject: [PATCH 19/22] reset alembic down_revision --- .../versions/2258cd8d6e9f_resource_thresholds_added.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py index a5db5e5db..21b0cf750 100644 --- a/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py +++ b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py @@ -1,7 +1,7 @@ """resource_thresholds_added Revision ID: 2258cd8d6e9f -Revises: 5a798acc6282 +Revises: a1b2c3d4e5f7 Create Date: 2024-08-22 12:31:38.465650 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = '2258cd8d6e9f' -down_revision = 'ba2da94739ab' +down_revision = 'a1b2c3d4e5f7' branch_labels = None depends_on = None From f9a782aa776967b27bf96a55aa067fe4a194c1ca Mon Sep 17 00:00:00 2001 From: kalosp Date: Wed, 15 Oct 2025 12:20:28 +0300 Subject: [PATCH 20/22] sync alembic revisions --- .../versions/2258cd8d6e9f_resource_thresholds_added.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py index 21b0cf750..00df0c680 100644 --- a/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py +++ b/backend/migrations/versions/2258cd8d6e9f_resource_thresholds_added.py @@ -1,7 +1,7 @@ """resource_thresholds_added Revision ID: 2258cd8d6e9f -Revises: a1b2c3d4e5f7 +Revises: 89c3bfe59cad Create Date: 2024-08-22 12:31:38.465650 """ @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = '2258cd8d6e9f' -down_revision = 'a1b2c3d4e5f7' +down_revision = '89c3bfe59cad' branch_labels = None depends_on = None From ef3faf89a185ed4094342676f1d21958c1ed1d52 Mon Sep 17 00:00:00 2001 From: kalosp Date: Wed, 15 Oct 2025 12:24:51 +0300 Subject: [PATCH 21/22] print db history on PR --- .github/workflows/alembic-tests.yml | 2 ++ Makefile | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/alembic-tests.yml b/.github/workflows/alembic-tests.yml index e904c9da7..edfa756b0 100644 --- a/.github/workflows/alembic-tests.yml +++ b/.github/workflows/alembic-tests.yml @@ -40,5 +40,7 @@ jobs: cache: 'pip' - name: Drop tables run: make drop-tables + - name: Show history + run: make history-db - name: Upgrade tables run: make upgrade-db diff --git a/Makefile b/Makefile index 25caf3344..d6e6bd3e3 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,11 @@ upgrade-db: upgrade-pip install-backend export PYTHONPATH=./backend && \ alembic -c backend/alembic.ini upgrade head +history-db: upgrade-pip install-backend + pip install 'alembic' + export PYTHONPATH=./backend && \ + alembic -c backend/alembic.ini history + generate-migrations: upgrade-pip install-backend pip install 'alembic' export PYTHONPATH=./backend && \ From cae812c13ff792abb92487530fb942961b2117f7 Mon Sep 17 00:00:00 2001 From: kalosp Date: Wed, 15 Oct 2025 12:27:30 +0300 Subject: [PATCH 22/22] fix alembic doc format --- .../versions/a1b2c3d4e5f7_fix_mf_trigger_entity_type.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/migrations/versions/a1b2c3d4e5f7_fix_mf_trigger_entity_type.py b/backend/migrations/versions/a1b2c3d4e5f7_fix_mf_trigger_entity_type.py index 704e56f4e..14fb47bd6 100644 --- a/backend/migrations/versions/a1b2c3d4e5f7_fix_mf_trigger_entity_type.py +++ b/backend/migrations/versions/a1b2c3d4e5f7_fix_mf_trigger_entity_type.py @@ -1,7 +1,9 @@ """fix_mf_trigger_entity_type + Revision ID: a1b2c3d4e5f7 Revises: ba2da94739ab Create Date: 2025-10-03 12:00:00.000000 + """ from alembic import op