From 637ae9c01beecbc7d5c5f1cc99dc918584caa451 Mon Sep 17 00:00:00 2001 From: Kjartan Einarsson Date: Mon, 25 Mar 2024 14:06:50 -0700 Subject: [PATCH 1/7] initial clean --- api/src/constants/artifacts.ts | 11 - api/src/constants/attachments.ts | 4 - api/src/constants/roles.ts | 12 - .../json-schema/transformation-schema.test.ts | 287 ----------- api/src/json-schema/transformation-schema.ts | 221 --------- api/src/json-schema/validation-schema.test.ts | 241 --------- api/src/json-schema/validation-schema.ts | 457 ------------------ 7 files changed, 1233 deletions(-) delete mode 100644 api/src/constants/artifacts.ts delete mode 100644 api/src/constants/attachments.ts delete mode 100644 api/src/json-schema/transformation-schema.test.ts delete mode 100644 api/src/json-schema/transformation-schema.ts delete mode 100644 api/src/json-schema/validation-schema.test.ts delete mode 100644 api/src/json-schema/validation-schema.ts diff --git a/api/src/constants/artifacts.ts b/api/src/constants/artifacts.ts deleted file mode 100644 index 0f34ce86f..000000000 --- a/api/src/constants/artifacts.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum ARTIFACT_TYPE { - ARCHIVE = 'Archive', - AUDIO = 'Audio', - DATA = 'Data', - IMAGE = 'Image', - OTHER = 'Other', - REPORT = 'Report', - SPATIAL = 'Spatial', - SPREADSHEET = 'Spreadsheet', - VIDEO = 'Video' -} diff --git a/api/src/constants/attachments.ts b/api/src/constants/attachments.ts deleted file mode 100644 index 7c47db481..000000000 --- a/api/src/constants/attachments.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ATTACHMENT_TYPE { - REPORT = 'Report', - OTHER = 'Other' -} diff --git a/api/src/constants/roles.ts b/api/src/constants/roles.ts index 2e3455c4e..dd4f0dec7 100644 --- a/api/src/constants/roles.ts +++ b/api/src/constants/roles.ts @@ -8,15 +8,3 @@ export enum SYSTEM_ROLE { SYSTEM_ADMIN = 'System Administrator', DATA_ADMINISTRATOR = 'Data Administrator' } - -/** - * Project level roles. - * - * @export - * @enum {number} - */ -export enum PROJECT_ROLE { - PROJECT_LEAD = 'Project Lead', - PROJECT_EDITOR = 'Editor', - PROJECT_VIEWER = 'Viewer' -} diff --git a/api/src/json-schema/transformation-schema.test.ts b/api/src/json-schema/transformation-schema.test.ts deleted file mode 100644 index ca4b2322c..000000000 --- a/api/src/json-schema/transformation-schema.test.ts +++ /dev/null @@ -1,287 +0,0 @@ -import Ajv from 'ajv'; -import { expect } from 'chai'; -import { describe } from 'mocha'; -import { submissionTransformationSchema } from './transformation-schema'; - -// Useful online json-schema validator: https://www.jsonschemavalidator.net/ - -describe('submissionTransformationSchema', () => { - const ajv = new Ajv(); - - it('is valid schema', () => { - expect(ajv.validateSchema(submissionTransformationSchema)).to.be.true; - }); -}); - -describe('example submission transformation schema', () => { - const exampleSchema = { - flatten: [ - { - fileName: 'Effort & Site Conditions', - uniqueId: ['Survey Area', 'Block ID/SU ID', 'Stratum'] - }, - { - fileName: 'Observations', - uniqueId: ['Waypoint'], - parent: { - fileName: 'Effort & Site Conditions', - uniqueId: ['Survey Area', 'Block ID/SU ID', 'Stratum'] - } - }, - { - fileName: 'UTM_LatLong', - uniqueId: ['Waypoint'], - parent: { - fileName: 'Observations', - uniqueId: ['Waypoint'] - } - }, - { - fileName: 'Strata Metadata', - uniqueId: ['Stratum'], - parent: { - fileName: 'Observations', - uniqueId: ['Stratum'] - } - } - ], - transform: [ - { - condition: { - if: { - columns: ['Lone Cows'], - not: true - } - }, - transformations: [ - { - fields: { - id: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum', 'Waypoint'], - separator: ':' - }, - eventID: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum'], - separator: ':' - }, - eventDate: { - columns: ['Date'] - }, - verbatimCoordinatesUTM: { - columns: ['UTM Zone', 'Easting', 'Northing'] - }, - verbatimCoordinatesLatLong: { - columns: ['lat', 'long'] - }, - occurrenceID: { - columns: ['Waypoint'], - unique: 'occ' - }, - individualCount: { - columns: ['Lone Cows'] - }, - vernacularName: { - columns: ['Species'] - }, - lifestage: { - value: 'Adult' - }, - sex: { - value: 'Female' - } - } - } - ] - }, - { - condition: { - if: { - columns: ['Cow W/1 calf'] - } - }, - transformations: [ - { - fields: { - id: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum', 'Waypoint'], - separator: ':' - }, - eventID: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum'], - separator: ':' - }, - eventDate: { - columns: ['Date'] - }, - verbatimCoordinatesUTM: { - columns: ['UTM Zone', 'Easting', 'Northing'] - }, - verbatimCoordinatesLatLong: { - columns: ['lat', 'long'] - }, - occurrenceID: { - columns: ['Waypoint'], - unique: 'occ' - }, - individualCount: { - columns: ['Cow W/1 calf'] - }, - vernacularName: { - columns: ['Species'] - }, - lifestage: { - value: 'Adult' - }, - sex: { - value: 'Female' - }, - resourceID: { - columns: ['Waypoint'], - unique: 'occ' - }, - relatedResourceID: { - value: '' - }, - relationshipOfResource: { - value: 'mother of' - } - } - }, - { - fields: { - id: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum', 'Waypoint'], - separator: ':' - }, - eventID: { - columns: ['Survey Area', 'Block ID/SU ID', 'Stratum'], - separator: ':' - }, - eventDate: { - columns: ['Date'] - }, - verbatimCoordinatesUTM: { - columns: ['UTM Zone', 'Easting', 'Northing'] - }, - verbatimCoordinatesLatLong: { - columns: ['lat', 'long'] - }, - occurrenceID: { - columns: ['Waypoint'], - unique: 'occ' - }, - individualCount: { - value: 1 - }, - vernacularName: { - columns: ['Species'] - }, - lifestage: { - value: 'Yearling' - }, - sex: { - value: 'Unknown' - }, - resourceID: { - columns: ['Waypoint'], - unique: 'occ' - }, - relatedResourceID: { - value: '' - }, - relationshipOfResource: { - value: 'offspring of' - } - } - } - ], - postTransformations: [ - { - relationship: { - spreadColumn: 'individualCount', - uniqueIdColumn: 'occurrenceID' - } - } - ] - } - ], - parse: [ - { - fileName: 'event', - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['eventID'] }, target: 'eventID' }, - { source: { columns: ['eventDate'] }, target: 'eventDate' }, - { - source: { columns: ['verbatimCoordinatesUTM', 'verbatimCoordinatesLatLong'] }, - target: 'verbatimCoordinates' - } - ] - }, - { - fileName: 'occurrence', - conditionalFields: ['individualCount'], - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['occurrenceID'] }, target: 'occurrenceID' }, - { source: { columns: ['individualCount'] }, target: 'individualCount' }, - { source: { columns: ['vernacularName'] }, target: 'taxonID' }, - { source: { columns: ['lifeStage'] }, target: 'lifeStage' }, - { source: { columns: ['sex'] }, target: 'sex' }, - { source: { value: 'Approved' }, target: 'Status' } - ] - }, - { - fileName: 'taxon', - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['occurrenceID'] }, target: 'occurrenceID' }, - { source: { columns: ['vernacularName'] }, target: 'vernacularName' } - ] - }, - { - fileName: 'resourcerelationship', - conditionalFields: ['resourceID'], - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['resourceID'] }, target: 'resourceID' }, - { source: { columns: ['relatedResourceID'] }, target: 'relatedResourceID' }, - { source: { columns: ['relationshipOfResource'] }, target: 'relationshipOfResource' } - ] - }, - { - fileName: 'measurementorfact', - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['eventID'] }, target: 'measurementID' }, - { source: { value: 'Habitat Description' }, target: 'measurementType' }, - { source: { columns: ['effort_habitat_description'] }, target: 'measurementValue' } - ] - }, - { - fileName: 'measurementorfact', - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['occurrenceID'] }, target: 'measurementID' }, - { source: { value: 'Stratum' }, target: 'measurementType' }, - { source: { columns: ['summary_stratum'] }, target: 'measurementValue' } - ] - }, - { - fileName: 'measurementorfact', - columns: [ - { source: { columns: ['id'] }, target: 'id' }, - { source: { columns: ['occurrenceID'] }, target: 'measurementID' }, - { source: { value: 'Activity' }, target: 'measurementType' }, - { source: { columns: ['observation_activity'] }, target: 'measurementValue' } - ] - } - ] - }; - - const ajv = new Ajv(); - - it('validates against submissionTransformationSchema', () => { - expect(ajv.validate(submissionTransformationSchema, exampleSchema), JSON.stringify(ajv.errors)).to.be.true; - }); -}); diff --git a/api/src/json-schema/transformation-schema.ts b/api/src/json-schema/transformation-schema.ts deleted file mode 100644 index ec8ab8d67..000000000 --- a/api/src/json-schema/transformation-schema.ts +++ /dev/null @@ -1,221 +0,0 @@ -export const submissionTransformationSchema = { - description: 'Occurrence Submission Transformation Schema', - type: 'object', - required: ['flatten', 'transform', 'parse'], - properties: { - name: { - description: 'The name of the submission file', - type: 'string' - }, - description: { - description: 'The description of the submission file', - type: 'string' - }, - flatten: { - type: 'array', - items: { - type: 'object', - required: ['fileName', 'uniqueId'], - properties: { - fileName: { - type: 'string' - }, - uniqueId: { - type: 'array', - items: { - type: 'string' - } - }, - parent: { - type: 'object', - properties: { - fileName: { - type: 'string' - }, - uniqueId: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - } - }, - transform: { - type: 'array', - items: { - type: 'object', - required: ['transformations'], - properties: { - condition: { - type: 'object', - properties: { - if: { - type: 'object', - required: ['columns'], - properties: { - columns: { - type: 'array', - items: { - type: 'string' - } - }, - not: { - type: 'boolean' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - transformations: { - type: 'array', - items: { - type: 'object', - required: ['fields'], - properties: { - fields: { - type: 'object', - patternProperties: { - '^.*$': { - type: 'object', - oneOf: [ - { - type: 'object', - required: ['columns'], - properties: { - columns: { - type: 'array', - items: { - type: 'string' - } - }, - separator: { - type: 'string' - }, - unique: { - type: 'string' - } - }, - additionalProperties: false - }, - { - type: 'object', - required: ['value'], - properties: { - value: { - type: ['string', 'number'] - } - }, - additionalProperties: false - } - ] - } - }, - additionalProperties: false - } - }, - additionalProperties: false - } - }, - postTransformations: { - type: 'array', - items: { - anyOf: [ - { - type: 'object', - properties: { - relationship: { - type: 'object', - properties: { - spreadColumn: { - type: 'string' - }, - uniqueIdColumn: { - type: 'string' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - } - ] - } - } - }, - additionalProperties: false - } - }, - parse: { - type: 'array', - items: { - type: 'object', - required: ['fileName', 'columns'], - properties: { - fileName: { - type: 'string' - }, - columns: { - type: 'array', - items: { - type: 'object', - properties: { - source: { - oneOf: [ - { - type: 'object', - required: ['columns'], - properties: { - columns: { - type: 'array', - items: { - type: 'string' - } - }, - separator: { - type: 'string' - }, - unique: { - type: 'string' - } - }, - additionalProperties: false - }, - { - type: 'object', - required: ['value'], - properties: { - value: { - type: ['string', 'number'] - } - }, - additionalProperties: false - } - ] - }, - target: { - type: 'string' - } - }, - additionalProperties: false - } - }, - conditionalFields: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - } - }, - additionalProperties: false -}; diff --git a/api/src/json-schema/validation-schema.test.ts b/api/src/json-schema/validation-schema.test.ts deleted file mode 100644 index 907335ac7..000000000 --- a/api/src/json-schema/validation-schema.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -import Ajv from 'ajv'; -import { expect } from 'chai'; -import { describe } from 'mocha'; -import { submissionValidationSchema } from './validation-schema'; - -// Useful online json-schema validator: https://www.jsonschemavalidator.net/ - -describe('submissionValidationSchema', () => { - const ajv = new Ajv(); - - it('is valid schema', () => { - expect(ajv.validateSchema(submissionValidationSchema)).to.be.true; - }); -}); - -describe('example submission validation schema', () => { - const exampleSchema2 = { - name: 'Example Submission Validation Schema', - description: 'An example submission validation schema.', - files: [ - { - name: 'event', - description: 'The Event file for this submission. Should contain information about events.', - columns: [ - { - name: 'eventID', - validations: [] - }, - { - name: 'parentEventID', - validations: [] - }, - { - name: 'samplingProtocol', - validations: [] - }, - { - name: 'geodeticDatum', - validations: [] - }, - { - name: 'verbatimCoordinates', - validations: [ - { - column_format_validator: { - reg_exp: '^9N \\d{6} \\d{7}$', - expected_format: '9N 12345 123456' - } - } - ] - }, - { - name: 'verbatimElevation', - validations: [] - }, - { - name: 'coordinateUncertaintyInMeters', - validations: [] - }, - { - name: 'coordinatePrecision', - validations: [] - }, - { - name: 'verbatimLocality', - validations: [] - }, - { - name: 'locationRemarks', - validations: [] - } - ], - validations: [ - { - file_required_columns_validator: { - required_columns: [ - 'eventID', - 'parentEventID', - 'eventDate', - 'samplingProtocol', - 'geodeticDatum', - 'verbatimCoordinates', - 'verbatimElevation', - 'coordinateUncertaintyInMeters', - 'coordinatePrecision', - 'verbatimLocality', - 'locationRemarks' - ] - } - } - ] - }, - { - name: 'occurrence', - description: 'The Occurrence file for this submission. Should contain information about occurrences.', - columns: [ - { - name: 'sex', - desciption: 'The sex of the observed taxon.', - validations: [ - { - column_code_validator: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - allowed_code_values: [ - { - name: 'male', - description: 'A male' - }, - { - name: 'female', - description: 'A female' - }, - { - name: 'unclassified', - description: 'An unclassified sex' - } - ] - } - } - ] - }, - { - name: 'lifeStage', - desciption: 'The life stage of the observed taxon.', - validations: [ - { - column_code_validator: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - allowed_code_values: [ - { - name: 'adult', - description: 'An adult' - }, - { - name: 'juvenile', - description: 'A child' - } - ] - } - } - ] - }, - { - name: 'individualCount', - desciption: 'The occurrence count.', - validations: [ - { - column_format_validator: { - name: '', - description: '', - reg_exp: '^\\d.*$', - expected_format: 'A positive integer' - } - } - ] - } - ], - validations: [ - { - file_required_columns_validator: { - name: '', - description: '', - required_columns: [ - 'eventID', - 'occurrenceID', - 'basisOfRecord', - 'type', - 'taxonID', - 'sex', - 'lifeStage', - 'individualCount', - 'identifiedBy', - 'organismQuantity', - 'organismQuantityType' - ] - } - }, - { - file_recommended_columns_validator: { - name: '', - description: '', - recommended_columns: ['occurrenceRemarks'] - } - } - ] - }, - { - name: 'measurementorfact', - description: - 'The Measurement Or Fact file for this submission. Should contain information about measurements and facts.', - columns: [], - validations: [] - }, - { - name: 'resourcerelationship', - description: - 'The Resource Relationship file for this submission. Should contain information about resource relationships.', - columns: [], - validations: [] - }, - { - name: 'taxon', - description: 'The Taxon file for this submission. Should contain information about taxonomy.', - columns: [], - validations: [] - } - ], - validations: [ - { - submission_required_files_validator: { - name: '', - description: '', - required_files: ['event', 'occurrence', 'taxon'] - } - }, - { - mimetype_validator: { - name: '', - description: '', - reg_exps: ['application/zip', 'application/x-zip-compressed', 'application/x-rar-compressed'] - } - } - ] - }; - - const ajv = new Ajv(); - - it('validates against submissionValidationSchema', () => { - expect(ajv.validate(submissionValidationSchema, exampleSchema2), JSON.stringify(ajv.errors)).to.be.true; - }); -}); diff --git a/api/src/json-schema/validation-schema.ts b/api/src/json-schema/validation-schema.ts deleted file mode 100644 index 895621467..000000000 --- a/api/src/json-schema/validation-schema.ts +++ /dev/null @@ -1,457 +0,0 @@ -export const submissionValidationSchema = { - description: 'Occurrence Submission Validation Schema', - type: 'object', - properties: { - name: { - description: 'The name of the submission file', - type: 'string' - }, - description: { - description: 'The description of the submission file', - type: 'string' - }, - files: { - description: 'An array of files/sheets within the submission file', - type: 'array', - items: [ - { - $ref: '#/$defs/file' - } - ] - }, - defaultFile: { - description: 'A fall-back file definition to use when no files/sheets match items in the files array', - $ref: '#/$defs/file' - }, - validations: { - description: 'An array of validations to apply against the submission file', - type: 'array', - items: { - $ref: '#/$defs/submission_validation' - } - } - }, - $defs: { - file: { - description: 'A single file/sheet within a submission file', - type: 'object', - required: ['name', 'columns'], - properties: { - name: { - description: 'The name of the file/sheet', - type: 'string' - }, - description: { - description: 'The description of the file/sheet', - type: 'string' - }, - columns: { - description: 'An array of columns within the file', - type: 'array', - items: { - $ref: '#/$defs/column' - } - }, - validations: { - description: 'An array of validations to apply against the file/sheet', - type: 'array', - items: { - $ref: '#/$defs/file_validation' - } - } - }, - additionalProperties: false - }, - column: { - description: 'An single column within a file/sheet', - type: 'object', - required: ['name'], - properties: { - name: { - description: 'The name of the column', - type: 'string' - }, - description: { - description: 'The description of the column', - type: 'string' - }, - validations: { - description: 'An array of validations to apply against the column', - type: 'array', - items: { - $ref: '#/$defs/column_validation' - } - } - }, - additionalProperties: false - }, - submission_validation: { - title: 'Submission File Validation', - description: 'The validators that can be applied against a submission file.', - anyOf: [ - { - $ref: '#/$defs/submission_required_files_validator' - }, - { - $ref: '#/$defs/mimetype_validator' - } - ] - }, - file_validation: { - title: 'File/Sheet Validation', - description: 'The validators that can be applied against a file/sheet within a submission file.', - anyOf: [ - { - $ref: '#/$defs/file_required_columns_validator' - }, - { - $ref: '#/$defs/file_recommended_columns_validator' - }, - { - $ref: '#/$defs/file_duplicate_columns_validator' - }, - { - $ref: '#/$defs/file_valid_columns_validator' - } - ] - }, - column_validation: { - title: 'Column Validation', - description: 'The validators that can be applied against a column within a file/sheet.', - anyOf: [ - { - $ref: '#/$defs/column_format_validator' - }, - { - $ref: '#/$defs/column_code_validator' - }, - { - $ref: '#/$defs/column_range_validator' - }, - { - $ref: '#/$defs/column_unique_validator' - }, - { - $ref: '#/$defs/column_key_validator' - }, - { - $ref: '#/$defs/column_numeric_validator' - } - ] - }, - submission_required_files_validator: { - description: 'Validates that this submission file contains required files/sheets', - type: 'object', - properties: { - submission_required_files_validator: { - type: 'object', - required: ['required_files'], - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - required_files: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - mimetype_validator: { - description: 'Validates that the mimetype of this submission/file is in an allowed set of values', - type: 'object', - properties: { - mimetype_validator: { - type: 'object', - required: ['reg_exps'], - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - reg_exps: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - file_required_columns_validator: { - description: 'Validates that this file/sheet contains required columns', - type: 'object', - properties: { - file_required_columns_validator: { - type: 'object', - required: ['required_columns'], - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - required_columns: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - file_recommended_columns_validator: { - description: 'Validates that this file/sheet contains recommended columns', - type: 'object', - properties: { - file_recommended_columns_validator: { - type: 'object', - required: ['recommended_columns'], - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - recommended_columns: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - file_duplicate_columns_validator: { - description: 'Validates that this file/sheet contains recommended columns', - type: 'object', - properties: { - file_duplicate_columns_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - file_valid_columns_validator: { - description: 'Validates that this file/sheet contains only valid (known) columns', - type: 'object', - properties: { - file_valid_columns_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - valid_columns: { - type: 'array', - items: { - type: 'string' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - column_format_validator: { - description: 'Validates that this column value matches a regex', - type: 'object', - properties: { - column_format_validator: { - type: 'object', - required: ['reg_exp', 'expected_format'], - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - reg_exp: { - type: 'string' - }, - reg_exp_flags: { - type: 'string' - }, - expected_format: { - type: 'string' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - - column_numeric_validator: { - description: 'Validates that this column is a number', - type: 'object', - properties: { - column_numeric_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - column_range_validator: { - description: 'Validates that this column value matches a number range', - type: 'object', - properties: { - column_range_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - min_value: { - type: 'number' - }, - max_value: { - type: 'number' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - column_code_validator: { - description: 'Validates that this column value is in an allowed set of values', - type: 'object', - properties: { - column_code_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - allowed_code_values: { - type: 'array', - items: { - $ref: '#/$defs/code_value' - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - column_unique_validator: { - description: 'Validates that this column value is unique within this column', - type: 'object', - properties: { - column_unique_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - is_unique: { - type: 'boolean' - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - column_key_validator: { - description: 'Validates that this column value has a matching counterpart in the target `file` and `column`', - type: 'object', - properties: { - column_key_validator: { - type: 'object', - properties: { - name: { - type: 'string' - }, - description: { - type: 'string' - }, - parent_key: { - type: 'object', - properties: { - file: { - type: 'string' - }, - column: { - type: 'string' - } - } - } - }, - additionalProperties: false - } - }, - additionalProperties: false - }, - code_value: { - description: 'Validates that this column value has a matching counterpart in the target `file` and `column`', - type: 'object', - required: ['name'], - properties: { - name: { - type: ['string', 'number'] - }, - description: { - type: 'string' - } - }, - additionalProperties: false - } - }, - additionalProperties: false -}; From 7593715827a4f7d14d0110c4142c87d68e54773f Mon Sep 17 00:00:00 2001 From: Kjartan Einarsson Date: Mon, 25 Mar 2024 16:41:50 -0700 Subject: [PATCH 2/7] remove unused endpoints --- .../access-request/count.test.ts | 82 ------ .../administrative/access-request/count.ts | 81 ------ .../access-request/update.test.ts | 109 -------- .../administrative/access-request/update.ts | 150 ----------- .../administrative/activity/create.test.ts | 62 ----- .../paths/administrative/activity/create.ts | 84 ------ .../administrative/activity/list.test.ts | 102 ------- api/src/paths/administrative/activity/list.ts | 153 ----------- .../administrative/activity/update.test.ts | 79 ------ .../paths/administrative/activity/update.ts | 98 ------- .../administrative/security/categories.ts | 136 ---------- .../{submissionId}/messages.test.ts | 79 ------ .../submission/{submissionId}/messages.ts | 252 ------------------ 13 files changed, 1467 deletions(-) delete mode 100644 api/src/paths/administrative/access-request/count.test.ts delete mode 100644 api/src/paths/administrative/access-request/count.ts delete mode 100644 api/src/paths/administrative/access-request/update.test.ts delete mode 100644 api/src/paths/administrative/access-request/update.ts delete mode 100644 api/src/paths/administrative/activity/create.test.ts delete mode 100644 api/src/paths/administrative/activity/create.ts delete mode 100644 api/src/paths/administrative/activity/list.test.ts delete mode 100644 api/src/paths/administrative/activity/list.ts delete mode 100644 api/src/paths/administrative/activity/update.test.ts delete mode 100644 api/src/paths/administrative/activity/update.ts delete mode 100644 api/src/paths/administrative/security/categories.ts delete mode 100644 api/src/paths/administrative/submission/{submissionId}/messages.test.ts delete mode 100644 api/src/paths/administrative/submission/{submissionId}/messages.ts diff --git a/api/src/paths/administrative/access-request/count.test.ts b/api/src/paths/administrative/access-request/count.test.ts deleted file mode 100644 index 68ba76731..000000000 --- a/api/src/paths/administrative/access-request/count.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { HTTP400 } from '../../../errors/http-error'; -import { AdministrativeService } from '../../../services/administrative-service'; -import * as userIdentifier from '../../../utils/keycloak-utils'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as count from './count'; - -chai.use(sinonChai); - -describe('count', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw a 400 error when no user identifier', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - sinon.stub(userIdentifier, 'getUserIdentifier').returns(null); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const requestHandler = count.getPendingAccessRequestsCount(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTP400).message).to.equal('Missing required userIdentifier'); - } - }); - - it('should return 0 on success (no rowCount)', async () => { - const dbConnectionObj = getMockDBConnection({ systemUserId: () => 1 }); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - sinon.stub(userIdentifier, 'getUserIdentifier').returns('string'); - - const getPendingAccessRequestCountStub = sinon - .stub(AdministrativeService.prototype, 'getPendingAccessRequestCount') - .resolves(0); - - const requestHandler = count.getPendingAccessRequestsCount(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(getPendingAccessRequestCountStub).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql(0); - }); - - it('should return rowCount on success', async () => { - const dbConnectionObj = getMockDBConnection({ systemUserId: () => 1 }); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - sinon.stub(userIdentifier, 'getUserIdentifier').returns('string'); - - const getPendingAccessRequestCountStub = sinon - .stub(AdministrativeService.prototype, 'getPendingAccessRequestCount') - .resolves(2); - - const requestHandler = count.getPendingAccessRequestsCount(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(getPendingAccessRequestCountStub).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql(2); - }); -}); diff --git a/api/src/paths/administrative/access-request/count.ts b/api/src/paths/administrative/access-request/count.ts deleted file mode 100644 index 7991f4346..000000000 --- a/api/src/paths/administrative/access-request/count.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection } from '../../../database/db'; -import { HTTP400 } from '../../../errors/http-error'; -import { hasPendingAdministrativeActivitiesResponseObject } from '../../../openapi/schemas/administrative-activity'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { getUserIdentifier } from '../../../utils/keycloak-utils'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/access-request/count'); - -export const GET: Operation = [getPendingAccessRequestsCount()]; - -GET.apiDoc = { - description: 'Has one or more pending Administrative Activities.', - tags: ['access request'], - security: [ - { - Bearer: [] - } - ], - requestBody: { - description: 'Administrative Activity get request object.', - content: { - 'application/json': { - schema: { - title: 'Administrative Activity request object', - type: 'object', - properties: {} - } - } - } - }, - responses: { - 200: { - description: '`Has Pending Administrative Activities` get response object.', - content: { - 'application/json': { - schema: { - ...(hasPendingAdministrativeActivitiesResponseObject as object) - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Get all projects. - * - * @returns {RequestHandler} - */ -export function getPendingAccessRequestsCount(): RequestHandler { - return async (req, res) => { - const connection = getAPIUserDBConnection(); - const administrativeService = new AdministrativeService(connection); - - try { - const userIdentifier = getUserIdentifier(req['keycloak_token']); - - if (!userIdentifier) { - throw new HTTP400('Missing required userIdentifier'); - } - - await connection.open(); - - const result = await administrativeService.getPendingAccessRequestCount(userIdentifier); - await connection.commit(); - - return res.status(200).json(result); - } catch (error) { - defaultLog.error({ label: 'getPendingAccessRequestsCount', message: 'error', error }); - - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/access-request/update.test.ts b/api/src/paths/administrative/access-request/update.test.ts deleted file mode 100644 index f51460f7e..000000000 --- a/api/src/paths/administrative/access-request/update.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { HTTPError } from '../../../errors/http-error'; -import { SystemUserExtended } from '../../../repositories/user-repository'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { GCNotifyService } from '../../../services/gcnotify-service'; -import { UserService } from '../../../services/user-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as update from './update'; - -chai.use(sinonChai); - -describe('updateAccessRequest', () => { - afterEach(() => { - sinon.restore(); - }); - - it('re-throws any error that is thrown', async () => { - const mockDBConnection = getMockDBConnection({ - open: () => { - throw new Error('test error'); - } - }); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.body = { - userIdentifier: 1, - identitySource: 'identitySource', - requestId: 1, - requestStatusTypeId: 1, - roleIds: [1, 3] - }; - - const requestHandler = update.updateAccessRequest(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('test error'); - } - }); - - it('adds new system roles and updates administrative activity', async () => { - const mockDBConnection = getMockDBConnection(); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const requestId = 1; - const requestStatusTypeId = 2; - const roleIdsToAdd = [1, 3]; - - mockReq.body = { - userIdentifier: 'username', - identitySource: 'identitySource', - requestId: requestId, - requestStatusTypeId: requestStatusTypeId, - roleIds: roleIdsToAdd - }; - - const systemUserId = 4; - const existingRoleIds = [1, 2]; - const mockSystemUser: SystemUserExtended = { - system_user_id: systemUserId, - user_identity_source_id: 2, - user_identifier: 'username', - user_guid: '', - record_end_date: '', - record_effective_date: '2023-12-08', - create_date: '2023-12-08 14:37:41.315999-08', - create_user: 1, - update_date: null, - update_user: null, - revision_count: 0, - identity_source: 'identitySource', - role_ids: existingRoleIds, - role_names: [] - }; - const ensureSystemUserStub = sinon.stub(UserService.prototype, 'ensureSystemUser').resolves(mockSystemUser); - - const addSystemRolesStub = sinon.stub(UserService.prototype, 'addUserSystemRoles'); - - const updateAdministrativeActivityStub = sinon.stub( - AdministrativeService.prototype, - 'updateAdministrativeActivity' - ); - - const sendApprovalEmailStub = sinon.stub(GCNotifyService.prototype, 'sendApprovalEmail'); - - const requestHandler = update.updateAccessRequest(); - - await requestHandler(mockReq, mockRes, mockNext); - - const expectedRoleIdsToAdd = [3]; - - expect(ensureSystemUserStub).to.have.been.calledOnce; - expect(addSystemRolesStub).to.have.been.calledWith(systemUserId, expectedRoleIdsToAdd); - expect(updateAdministrativeActivityStub).to.have.been.calledWith(requestId, requestStatusTypeId); - expect(sendApprovalEmailStub).to.have.been.calledWith(requestStatusTypeId, 'username', 'identitySource'); - }); -}); diff --git a/api/src/paths/administrative/access-request/update.ts b/api/src/paths/administrative/access-request/update.ts deleted file mode 100644 index d2f9c6945..000000000 --- a/api/src/paths/administrative/access-request/update.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { SYSTEM_IDENTITY_SOURCE } from '../../../constants/database'; -import { SYSTEM_ROLE } from '../../../constants/roles'; -import { getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { GCNotifyService } from '../../../services/gcnotify-service'; -import { UserService } from '../../../services/user-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/access-request/update'); - -export const PUT: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN], - discriminator: 'SystemRole' - } - ] - }; - }), - updateAccessRequest() -]; - -PUT.apiDoc = { - description: "Update a user's system access request and add any specified system roles to the user.", - tags: ['user'], - security: [ - { - Bearer: [] - } - ], - requestBody: { - content: { - 'application/json': { - schema: { - type: 'object', - required: ['userGuid', 'userIdentifier', 'identitySource', 'requestId', 'requestStatusTypeId'], - properties: { - userGuid: { - type: 'string', - description: 'The GUID for the user.' - }, - userIdentifier: { - type: 'string', - description: 'The user identifier for the user.' - }, - identitySource: { - type: 'string', - enum: [ - SYSTEM_IDENTITY_SOURCE.IDIR, - SYSTEM_IDENTITY_SOURCE.BCEID_BASIC, - SYSTEM_IDENTITY_SOURCE.BCEID_BUSINESS - ] - }, - requestId: { - type: 'number', - description: 'The id of the access request to update.' - }, - requestStatusTypeId: { - type: 'number', - description: 'The status type id to set for the access request.' - }, - roleIds: { - type: 'array', - items: { - type: 'number' - }, - description: - 'An array of role ids to add, if the access-request was approved. Ignored if the access-request was denied.' - } - } - } - } - } - }, - responses: { - 200: { - description: 'Add system user roles to user OK.' - }, - ...defaultErrorResponses - } -}; - -/** - * Updates an access request. - * - * key steps performed: - * - Get the user by their user identifier - * - If user is not found, add them - * - Determine if there are any new roles to add, and add them if there are - * - Update the administrative activity record status - * - * @return {*} {RequestHandler} - */ -export function updateAccessRequest(): RequestHandler { - return async (req, res) => { - defaultLog.debug({ label: 'updateAccessRequest', message: 'params', req_body: req.body }); - - const userGuid = req.body?.userGuid; - const userIdentifier = req.body?.userIdentifier; - const identitySource = req.body?.identitySource; - const administrativeActivityId = Number(req.body?.requestId); - const administrativeActivityStatusTypeId = Number(req.body?.requestStatusTypeId); - const roleIds: number[] = req.body?.roleIds || []; - - const connection = getDBConnection(req['keycloak_token']); - const gcnotifyService = new GCNotifyService(connection); - const administrativeService = new AdministrativeService(connection); - - try { - await connection.open(); - - const userService = new UserService(connection); - - // Get the system user (adding or activating them if they already existed). - const systemUserObject = await userService.ensureSystemUser(userGuid, userIdentifier, identitySource); - - // Filter out any system roles that have already been added to the user - const rolesIdsToAdd = roleIds.filter((roleId) => !systemUserObject.role_ids.includes(roleId)); - - if (rolesIdsToAdd?.length) { - // Add any missing roles (if any) - await userService.addUserSystemRoles(systemUserObject.system_user_id, rolesIdsToAdd); - } - - // Update the access request record status - await administrativeService.updateAdministrativeActivity( - administrativeActivityId, - administrativeActivityStatusTypeId - ); - - //if the access request is an approval send Approval email - await gcnotifyService.sendApprovalEmail(administrativeActivityStatusTypeId, userIdentifier, identitySource); - - await connection.commit(); - - return res.status(200).send(); - } catch (error) { - defaultLog.error({ label: 'updateAccessRequest', message: 'error', error }); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/activity/create.test.ts b/api/src/paths/administrative/activity/create.test.ts deleted file mode 100644 index 93a95e18d..000000000 --- a/api/src/paths/administrative/activity/create.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { HTTP500 } from '../../../errors/http-error'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { GCNotifyService } from '../../../services/gcnotify-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as create from './create'; - -chai.use(sinonChai); - -describe('create', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw a 400 error when no system user id', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub(), - systemUserId: () => 0 - }); - - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const requestHandler = create.createAdministrativeActivity(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTP500).message).to.equal('Failed to identify system user ID'); - } - }); - - it('should return 200 when create completes', async () => { - const dbConnectionObj = getMockDBConnection({ systemUserId: () => 1 }); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const createAdministrativeActivityStub = sinon - .stub(AdministrativeService.prototype, 'createAdministrativeActivity') - .resolves({ id: 1, create_date: 'date' }); - - sinon.stub(GCNotifyService.prototype, 'sendAccessRequestReceivedEmail').resolves(); - - const requestHandler = create.createAdministrativeActivity(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(createAdministrativeActivityStub).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql({ id: 1, date: 'date' }); - }); -}); diff --git a/api/src/paths/administrative/activity/create.ts b/api/src/paths/administrative/activity/create.ts deleted file mode 100644 index 82c617294..000000000 --- a/api/src/paths/administrative/activity/create.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection } from '../../../database/db'; -import { HTTP500 } from '../../../errors/http-error'; -import { administrativeActivityResponseObject } from '../../../openapi/schemas/administrative-activity'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { GCNotifyService } from '../../../services/gcnotify-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/activity/list'); - -export const POST: Operation = [createAdministrativeActivity()]; - -POST.apiDoc = { - description: 'Create a new Administrative Activity.', - tags: ['admin'], - security: [ - { - Bearer: [] - } - ], - requestBody: { - description: 'Administrative Activity post request object.', - content: { - 'application/json': { - schema: { - title: 'Administrative Activity request object', - type: 'object', - properties: {} - } - } - } - }, - responses: { - 200: { - description: 'Administrative activity post response object.', - content: { - 'application/json': { - schema: { - ...(administrativeActivityResponseObject as object) - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Creates a new access request record. - * - * @returns {RequestHandler} - */ -export function createAdministrativeActivity(): RequestHandler { - return async (req, res) => { - const connection = getAPIUserDBConnection(); - const administrativeService = new AdministrativeService(connection); - const gcNotifyService = new GCNotifyService(connection); - try { - await connection.open(); - - const systemUserId = connection.systemUserId(); - - if (!systemUserId) { - throw new HTTP500('Failed to identify system user ID'); - } - - const response = await administrativeService.createAdministrativeActivity(systemUserId, req?.body); - - await connection.commit(); - - await gcNotifyService.sendAccessRequestReceivedEmail(); - - return res.status(200).json({ id: response.id, date: response.create_date }); - } catch (error) { - defaultLog.error({ label: 'administrativeActivity', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/activity/list.test.ts b/api/src/paths/administrative/activity/list.test.ts deleted file mode 100644 index f490f42ee..000000000 --- a/api/src/paths/administrative/activity/list.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { HTTPError } from '../../../errors/http-error'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as list from './list'; - -chai.use(sinonChai); - -describe('getAdministrativeActivities', () => { - afterEach(() => { - sinon.restore(); - }); - - it('re-throws any error that is thrown', async () => { - const mockDBConnection = getMockDBConnection({ - open: () => { - throw new Error('test error'); - } - }); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const requestHandler = list.getAdministrativeActivities(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('test error'); - } - }); - - it('should return the rows on success (empty)', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { type: 'string' }; - - const mockGetAdministrativeActivities = sinon - .stub(AdministrativeService.prototype, 'getAdministrativeActivities') - .resolves([]); - - const requestHandler = list.getAdministrativeActivities(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockGetAdministrativeActivities).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql([]); - }); - - it('should return the rows on success (not empty)', async () => { - const data = { - id: 1, - type: 2, - type_name: 'type name', - status: 3, - status_name: 'status name', - description: 'description', - data: 'data', - notes: 'notes', - create_date: '2020/04/04' - }; - - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { type: 'string' }; - - const mockGetAdministrativeActivities = sinon - .stub(AdministrativeService.prototype, 'getAdministrativeActivities') - .resolves([data]); - - const requestHandler = list.getAdministrativeActivities(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockGetAdministrativeActivities).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql([data]); - }); -}); diff --git a/api/src/paths/administrative/activity/list.ts b/api/src/paths/administrative/activity/list.ts deleted file mode 100644 index 4e3d1584d..000000000 --- a/api/src/paths/administrative/activity/list.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { SYSTEM_ROLE } from '../../../constants/roles'; -import { getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/activity/list'); - -export const GET: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN], - discriminator: 'SystemRole' - } - ] - }; - }), - getAdministrativeActivities() -]; - -export enum ADMINISTRATIVE_ACTIVITY_STATUS_TYPE { - PENDING = 'Pending', - ACTIONED = 'Actioned', - REJECTED = 'Rejected' -} - -export const getAllAdministrativeActivityStatusTypes = (): string[] => - Object.values(ADMINISTRATIVE_ACTIVITY_STATUS_TYPE); - -GET.apiDoc = { - description: 'Get a list of administrative activities based on the provided criteria.', - tags: ['admin'], - security: [ - { - Bearer: [] - } - ], - parameters: [ - { - in: 'query', - name: 'type', - schema: { - type: 'string', - enum: ['System Access'] - } - }, - { - in: 'query', - name: 'status', - schema: { - type: 'array', - items: { - type: 'string', - enum: getAllAdministrativeActivityStatusTypes() - } - } - } - ], - responses: { - 200: { - description: 'List of administrative activities.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - id: { - type: 'number', - description: 'Administrative activity row ID' - }, - type: { - type: 'number', - description: 'Administrative activity type ID' - }, - type_name: { - type: 'string', - description: 'Administrative activity type name' - }, - status: { - type: 'number', - description: 'Administrative activity status type ID' - }, - status_name: { - type: 'string', - description: 'Administrative activity status type name' - }, - description: { - type: 'string' - }, - notes: { - type: 'string' - }, - data: { - type: 'object', - description: 'JSON data blob containing additional information about the activity record', - properties: { - // Don't specify as this is a JSON blob column - } - }, - create_date: { - type: 'string' - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Get all administrative activities for the specified type, or all if no type is provided. - * - * @returns {RequestHandler} - */ -export function getAdministrativeActivities(): RequestHandler { - return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); - const administrativeService = new AdministrativeService(connection); - - try { - const administrativeActivityTypeName = (req.query?.type as string) || ''; - - const administrativeActivityStatusTypes: string[] = - (req.query?.status as string[]) || getAllAdministrativeActivityStatusTypes(); - - await connection.open(); - - const response = await administrativeService.getAdministrativeActivities( - administrativeActivityTypeName, - administrativeActivityStatusTypes - ); - - await connection.commit(); - - return res.status(200).json(response); - } catch (error) { - defaultLog.error({ label: 'getAdministrativeActivities', message: 'error', error }); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/activity/update.test.ts b/api/src/paths/administrative/activity/update.test.ts deleted file mode 100644 index d4e928d40..000000000 --- a/api/src/paths/administrative/activity/update.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { HTTPError } from '../../../errors/http-error'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as update from './update'; - -chai.use(sinonChai); - -describe('update', () => { - afterEach(() => { - sinon.restore(); - }); - - const sampleReq = { - keycloak_token: {}, - body: { - id: 1, - status: 2 - } - } as any; - - it('re-throws any error that is thrown', async () => { - const mockDBConnection = getMockDBConnection({ - open: () => { - throw new Error('test error'); - } - }); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.body = { - userIdentifier: 1, - identitySource: 'identitySource', - requestId: 1, - requestStatusTypeId: 1, - roleIds: [1, 3] - }; - - const requestHandler = update.getUpdateAdministrativeActivityHandler(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('test error'); - } - }); - - it('should return 200 after update is completed', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.body = sampleReq.body; - - const mockUpdateAdministrativeActivity = sinon - .stub(AdministrativeService.prototype, 'updateAdministrativeActivity') - .resolves(); - - const requestHandler = update.getUpdateAdministrativeActivityHandler(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockUpdateAdministrativeActivity).to.have.been.calledOnce; - expect(mockRes.statusValue).to.equal(200); - }); -}); diff --git a/api/src/paths/administrative/activity/update.ts b/api/src/paths/administrative/activity/update.ts deleted file mode 100644 index 2c796ceaf..000000000 --- a/api/src/paths/administrative/activity/update.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { SYSTEM_ROLE } from '../../../constants/roles'; -import { getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { AdministrativeService } from '../../../services/administrative-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/activity/update'); - -export const PUT: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN], - discriminator: 'SystemRole' - } - ] - }; - }), - getUpdateAdministrativeActivityHandler() -]; - -PUT.apiDoc = { - description: 'Update an existing administrative activity.', - tags: ['admin'], - security: [ - { - Bearer: [] - } - ], - requestBody: { - description: 'Administrative activity request object.', - content: { - 'application/json': { - schema: { - title: 'Administrative activity put object', - type: 'object', - required: ['id', 'status'], - properties: { - id: { - title: 'administrative activity record ID', - type: 'number' - }, - status: { - title: 'administrative activity status type code ID', - type: 'number' - } - } - } - } - } - }, - responses: { - ...defaultErrorResponses - } -}; - -/** - * Get a request handler to update an existing administrative activity. - * - * @returns {RequestHandler} - */ -export function getUpdateAdministrativeActivityHandler(): RequestHandler { - return async (req, res) => { - defaultLog.debug({ - label: 'getUpdateAdministrativeActivityHandler', - message: 'params', - req_body: req.body - }); - - const administrativeActivityId = Number(req.body?.id); - const administrativeActivityStatusTypeId = Number(req.body?.status); - - const connection = getDBConnection(req['keycloak_token']); - const administrativeService = new AdministrativeService(connection); - - try { - await connection.open(); - - await administrativeService.updateAdministrativeActivity( - administrativeActivityId, - administrativeActivityStatusTypeId - ); - - await connection.commit(); - - return res.status(200).send(); - } catch (error) { - defaultLog.error({ label: 'getUpdateAdministrativeActivityHandler', message: 'error', error }); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/security/categories.ts b/api/src/paths/administrative/security/categories.ts deleted file mode 100644 index d984b50fe..000000000 --- a/api/src/paths/administrative/security/categories.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { SYSTEM_ROLE } from '../../../constants/roles'; -import { getDBConnection } from '../../../database/db'; -import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { SecurityService } from '../../../services/security-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/security/categories'); - -export const GET: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN], - discriminator: 'SystemRole' - } - ] - }; - }), - getActiveSecurityCategories() -]; - -GET.apiDoc = { - description: - 'Get all active security rules with their associated categories. A security category is active if it has not been end-dated.', - tags: ['security'], - security: [ - { - Bearer: [] - } - ], - responses: { - 200: { - description: 'Security Categories.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - required: [ - 'security_category_id', - 'name', - 'description', - 'record_effective_date', - 'record_end_date', - 'create_date', - 'create_user', - 'update_date', - 'update_user', - 'revision_count' - ], - properties: { - security_category_id: { - type: 'integer' - }, - name: { - type: 'string' - }, - description: { - type: 'string' - }, - record_effective_date: { - type: 'string' - }, - record_end_date: { - type: 'string', - nullable: true - }, - create_date: { - type: 'string' - }, - create_user: { - type: 'integer' - }, - update_date: { - type: 'string', - nullable: true - }, - update_user: { - type: 'integer', - minimum: 1, - nullable: true - }, - revision_count: { - type: 'integer' - } - }, - additionalProperties: false - } - } - } - } - }, - 400: { - $ref: '#/components/responses/400' - }, - 401: { - $ref: '#/components/responses/401' - }, - 403: { - $ref: '#/components/responses/403' - }, - 500: { - $ref: '#/components/responses/500' - }, - default: { - $ref: '#/components/responses/default' - } - } -}; - -export function getActiveSecurityCategories(): RequestHandler { - return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); - const service = new SecurityService(connection); - - try { - await connection.open(); - - const data = await service.getActiveSecurityCategories(); - - await connection.commit(); - - return res.status(200).json(data); - } catch (error) { - defaultLog.error({ label: 'getActiveSecurityCategories', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/administrative/submission/{submissionId}/messages.test.ts b/api/src/paths/administrative/submission/{submissionId}/messages.test.ts deleted file mode 100644 index 414907209..000000000 --- a/api/src/paths/administrative/submission/{submissionId}/messages.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../../database/db'; -import { HTTPError } from '../../../../errors/http-error'; -import { SubmissionService } from '../../../../services/submission-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; -import { createSubmissionMessages } from './messages'; - -chai.use(sinonChai); - -describe('createSubmissionMessages', () => { - afterEach(() => { - sinon.restore(); - }); - - it('re-throws any error that is thrown', async () => { - const mockDBConnection = getMockDBConnection({ - open: () => { - throw new Error('test error'); - } - }); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const requestHandler = createSubmissionMessages(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('test error'); - } - }); - - it('should return an array of Reviewed submission objects', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const submissionId = 1; - const messages = [ - { - submission_message_type_id: 2, - label: 'label', - message: 'message', - data: { - dataField: 'dataField' - } - } - ]; - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - submissionId: String(submissionId) - }; - mockReq.body = { - messages - }; - - const createMessagesStub = sinon.stub(SubmissionService.prototype, 'createMessages').resolves(undefined); - - const requestHandler = createSubmissionMessages(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(createMessagesStub).to.have.been.calledOnceWith(submissionId, messages); - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.be.undefined; - }); -}); diff --git a/api/src/paths/administrative/submission/{submissionId}/messages.ts b/api/src/paths/administrative/submission/{submissionId}/messages.ts deleted file mode 100644 index 7df95de2d..000000000 --- a/api/src/paths/administrative/submission/{submissionId}/messages.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { SYSTEM_ROLE } from '../../../../constants/roles'; -import { getDBConnection } from '../../../../database/db'; -import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses'; -import { SubmissionMessageRecord } from '../../../../repositories/submission-repository'; -import { authorizeRequestHandler } from '../../../../request-handlers/security/authorization'; -import { SubmissionService } from '../../../../services/submission-service'; -import { getLogger } from '../../../../utils/logger'; - -const defaultLog = getLogger('paths/administrative/submission/reviewed'); - -export const GET: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - discriminator: 'SystemRole' - } - ] - }; - }), - getSubmissionMessages() -]; - -GET.apiDoc = { - description: 'Get messages for a submission.', - tags: ['admin'], - security: [ - { - Bearer: [] - } - ], - parameters: [ - { - description: 'Submission ID', - in: 'path', - name: 'submissionId', - schema: { - type: 'integer', - minimum: 1 - }, - required: true - } - ], - responses: { - 200: { - description: 'Array of messages for a submission.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - submission_message_id: { - type: 'integer', - minimum: 1 - }, - submission_message_type_id: { - type: 'integer', - minimum: 1 - }, - submission_feature_id: { - type: 'integer', - minimum: 1 - }, - label: { - type: 'string', - maxLength: 250 - }, - message: { - type: 'string', - maxLength: 500 - }, - data: { - type: 'object', - properties: {} - }, - create_date: { - type: 'string' - }, - create_user: { - type: 'integer', - minimum: 1 - }, - update_date: { - type: 'string' - }, - update_user: { - type: 'integer', - minimum: 1 - }, - revision_count: { - type: 'integer', - maximum: 0 - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Get messages for a submission. - * - * @returns {RequestHandler} - */ -export function getSubmissionMessages(): RequestHandler { - return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); - - const submissionId = Number(req.params.submissionId); - - try { - await connection.open(); - - const service = new SubmissionService(connection); - const response = await service.getMessages(submissionId); - - await connection.commit(); - - return res.status(200).json(response); - } catch (error) { - defaultLog.error({ label: 'getSubmissionMessages', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} - -export const POST: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - discriminator: 'SystemRole' - } - ] - }; - }), - getSubmissionMessages() -]; - -POST.apiDoc = { - description: 'Creates new messages for a submission.', - tags: ['admin'], - security: [ - { - Bearer: [] - } - ], - requestBody: { - content: { - 'application/json': { - schema: { - type: 'object', - required: ['messages'], - properties: { - messages: { - type: 'array', - items: { - type: 'object', - required: ['submission_message_type_id', 'label', 'message'], - properties: { - submission_message_type_id: { - type: 'integer', - minimum: 1 - }, - label: { - type: 'string', - maxLength: 250 - }, - message: { - type: 'string', - maxLength: 500 - }, - data: { - type: 'object', - properties: {} - } - } - }, - minItems: 1 - } - } - } - } - } - }, - parameters: [ - { - description: 'Submission ID', - in: 'path', - name: 'submissionId', - schema: { - type: 'integer', - minimum: 1 - }, - required: true - } - ], - responses: { - 200: { - description: 'Create submission messages OK' - }, - ...defaultErrorResponses - } -}; - -/** - * Create submission messages for a submission. - * - * @returns {RequestHandler} - */ -export function createSubmissionMessages(): RequestHandler { - return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); - - const submissionId = Number(req.params.submissionId); - - const messages = req.body.messages as Pick< - SubmissionMessageRecord, - 'submission_message_type_id' | 'label' | 'message' | 'data' - >[]; - - try { - await connection.open(); - - const service = new SubmissionService(connection); - const response = await service.createMessages(submissionId, messages); - - await connection.commit(); - - return res.status(200).json(response); - } catch (error) { - defaultLog.error({ label: 'getSubmissionMessages', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} From 707e32921a7994d17aaaf74f4620665cb559bf16 Mon Sep 17 00:00:00 2001 From: Kjartan Einarsson Date: Wed, 27 Mar 2024 14:36:06 -0700 Subject: [PATCH 3/7] remove unused endpoints/services/repos/utils --- api/package-lock.json | 121 +- api/src/errors/api-error.test.ts | 6 +- api/src/errors/api-error.ts | 13 - .../schemas/administrative-activity.test.ts | 12 - .../schemas/administrative-activity.ts | 25 - .../artifact/{artifactId}/getSignedUrl.ts | 2 +- api/src/paths/dwc/eml/search.test.ts | 185 -- api/src/paths/dwc/eml/search.ts | 100 -- .../paths/dwc/submission/{datasetId}/get.ts | 2 +- api/src/paths/gcnotify/send.ts | 2 +- api/src/queue/queue-registry.ts | 35 +- .../administrative-repository.test.ts | 199 --- .../repositories/administrative-repository.ts | 224 --- .../templates/SIMS-handlebar-template.ts | 280 --- .../services/administrative-service.test.ts | 95 -- api/src/services/administrative-service.ts | 75 - api/src/services/artifact-service.ts | 11 - api/src/services/dwc-service.test.ts | 1500 ----------------- api/src/services/dwc-service.ts | 779 --------- api/src/services/eml-service.test.ts | 509 ------ api/src/services/eml-service.ts | 317 ---- api/src/services/es-service.test.ts | 29 - api/src/services/es-service.ts | 84 - api/src/services/gcnotify-service.ts | 81 +- api/src/services/spatial-service.test.ts | 159 -- api/src/services/spatial-service.ts | 69 - api/src/services/validation-service.ts | 29 - app/src/AppRouter.tsx | 7 - app/src/features/search/SearchPage.tsx | 256 --- app/src/features/search/SearchRouter.tsx | 26 - app/src/hooks/api/useDatasetApi.test.ts | 18 - app/src/hooks/api/useDatasetApi.ts | 13 - app/src/hooks/api/useSearchApi.test.ts | 17 - app/src/hooks/api/useSearchApi.ts | 27 +- 34 files changed, 101 insertions(+), 5206 deletions(-) delete mode 100644 api/src/openapi/schemas/administrative-activity.test.ts delete mode 100644 api/src/openapi/schemas/administrative-activity.ts delete mode 100644 api/src/paths/dwc/eml/search.test.ts delete mode 100644 api/src/paths/dwc/eml/search.ts delete mode 100644 api/src/repositories/administrative-repository.test.ts delete mode 100644 api/src/repositories/administrative-repository.ts delete mode 100644 api/src/repositories/templates/SIMS-handlebar-template.ts delete mode 100644 api/src/services/administrative-service.test.ts delete mode 100644 api/src/services/administrative-service.ts delete mode 100644 api/src/services/dwc-service.test.ts delete mode 100644 api/src/services/dwc-service.ts delete mode 100644 api/src/services/eml-service.test.ts delete mode 100644 api/src/services/eml-service.ts delete mode 100644 api/src/services/es-service.test.ts delete mode 100644 api/src/services/es-service.ts delete mode 100644 app/src/features/search/SearchPage.tsx delete mode 100644 app/src/features/search/SearchRouter.tsx diff --git a/api/package-lock.json b/api/package-lock.json index 9fda741dc..8f3989254 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -678,6 +678,36 @@ "strip-ansi": "^7.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -686,6 +716,21 @@ "ansi-regex": "^6.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + } + } + }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -695,6 +740,54 @@ "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } } } }, @@ -8968,16 +9061,6 @@ "strip-ansi": "^6.0.1" } }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, "string.prototype.padend": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", @@ -9035,14 +9118,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -10110,16 +10185,6 @@ "strip-ansi": "^6.0.0" } }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/api/src/errors/api-error.test.ts b/api/src/errors/api-error.test.ts index d8a6da002..4817989ae 100644 --- a/api/src/errors/api-error.test.ts +++ b/api/src/errors/api-error.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { describe } from 'mocha'; -import { ApiErrorType, ApiExecuteSQLError, ApiGeneralError, ApiUnknownError } from './api-error'; +import { ApiErrorType, ApiExecuteSQLError, ApiGeneralError } from './api-error'; describe('ApiError', () => { describe('No error value provided', () => { @@ -14,10 +14,6 @@ describe('ApiError', () => { expect(new ApiGeneralError(message).name).to.equal(ApiErrorType.GENERAL); }); - it('Creates Api Unknown error', function () { - expect(new ApiUnknownError(message).name).to.equal(ApiErrorType.UNKNOWN); - }); - it('Creates Api execute SQL error', function () { expect(new ApiExecuteSQLError(message).name).to.equal(ApiErrorType.EXECUTE_SQL); }); diff --git a/api/src/errors/api-error.ts b/api/src/errors/api-error.ts index 701581391..4ee0edab6 100644 --- a/api/src/errors/api-error.ts +++ b/api/src/errors/api-error.ts @@ -38,19 +38,6 @@ export class ApiGeneralError extends ApiError { } } -/** - * API encountered an unknown/unexpected error. - * - * @export - * @class ApiUnknownError - * @extends {ApiError} - */ -export class ApiUnknownError extends ApiError { - constructor(message: string, errors?: (string | Record)[]) { - super(ApiErrorType.UNKNOWN, message, errors); - } -} - /** * API executed a query against the database, but the response was missing data, or indicated the query failed. * diff --git a/api/src/openapi/schemas/administrative-activity.test.ts b/api/src/openapi/schemas/administrative-activity.test.ts deleted file mode 100644 index 33a6edc94..000000000 --- a/api/src/openapi/schemas/administrative-activity.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Ajv from 'ajv'; -import { expect } from 'chai'; -import { describe } from 'mocha'; -import { administrativeActivityResponseObject } from './administrative-activity'; - -describe('administrativeActivityResponseObject', () => { - const ajv = new Ajv(); - - it('is valid openapi v3 schema', () => { - expect(ajv.validateSchema(administrativeActivityResponseObject)).to.be.true; - }); -}); diff --git a/api/src/openapi/schemas/administrative-activity.ts b/api/src/openapi/schemas/administrative-activity.ts deleted file mode 100644 index bf87fc9b4..000000000 --- a/api/src/openapi/schemas/administrative-activity.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Response object for creating an access request - */ -export const administrativeActivityResponseObject = { - title: 'Administrative Activity Response Object', - type: 'object', - required: ['id', 'date'], - properties: { - id: { - type: 'number' - }, - date: { - type: 'string', - description: 'The date this administrative activity was made' - } - } -}; - -/** - * Response object for GET 'has pending administrative activities' operation - */ -export const hasPendingAdministrativeActivitiesResponseObject = { - title: '`Has Pending Administrative Activities` Response Object', - type: 'number' -}; diff --git a/api/src/paths/artifact/{artifactId}/getSignedUrl.ts b/api/src/paths/artifact/{artifactId}/getSignedUrl.ts index d56e712f5..5c4304a47 100644 --- a/api/src/paths/artifact/{artifactId}/getSignedUrl.ts +++ b/api/src/paths/artifact/{artifactId}/getSignedUrl.ts @@ -5,7 +5,7 @@ import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; import { SecurityService } from '../../../services/security-service'; import { getLogger } from '../../../utils/logger'; -const defaultLog = getLogger('paths/dwc/eml/get'); +const defaultLog = getLogger('paths/artifact/{artifactId}/getSignedUrl'); export const GET: Operation = [getArtifactSignedUrl()]; diff --git a/api/src/paths/dwc/eml/search.test.ts b/api/src/paths/dwc/eml/search.test.ts deleted file mode 100644 index aee667026..000000000 --- a/api/src/paths/dwc/eml/search.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { ESService } from '../../../services/es-service'; -import { SubmissionService } from '../../../services/submission-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as search from './search'; -import { GET } from './search'; - -chai.use(sinonChai); - -describe('search', () => { - describe('openApiSchema', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - const basicRequest = { - headers: { - 'content-type': 'application/json' - }, - body: {}, - params: {}, - query: {} - }; - - describe('should throw an error when', () => { - it('has invalid type', async () => { - const request = { ...basicRequest, query: { terms: false } }; - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].message).to.equal('must be string'); - }); - }); - - describe('should succeed when', () => { - it('has valid no value', async () => { - const request = { ...basicRequest, query: { terms: '' } }; - const response = requestValidator.validateRequest(request); - - expect(response).to.equal(undefined); - }); - - it('has valid single keyword value', async () => { - const request = { ...basicRequest, query: { terms: 'test' } }; - const response = requestValidator.validateRequest(request); - - expect(response).to.equal(undefined); - }); - - it('has valid single search value', async () => { - const request = { ...basicRequest, query: { terms: 'test' } }; - const response = requestValidator.validateRequest(request); - - expect(response).to.equal(undefined); - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - - describe('should throw an error when', () => { - it('has null value', async () => { - const apiResponse = null; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('has array with invalid key value: id', async () => { - const apiResponse = [{ id1: 1 }]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'id'"); - }); - - it('has array with invalid value: id', async () => { - const apiResponse = [{ id: 14, source: {}, fields: {} }]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'observation_count'"); - }); - it('has array with invalid value: source', async () => { - const apiResponse = [{ id: 'test', source: 1, fields: {} }]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'observation_count'"); - }); - }); - - describe('should succeed when', () => { - it('has empty valid values', async () => { - const apiResponse: never[] = []; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - - it('has valid values', async () => { - const apiResponse = [ - { id: 'test1', source: {}, observation_count: 1 }, - { id: 'test2', source: {}, observation_count: 2 } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - }); - }); - }); - - describe('searchInElasticSearch', () => { - afterEach(() => { - sinon.restore(); - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { - terms: 'search-term' - }; - - sinon.stub(ESService.prototype, 'keywordSearchEml').throws(new Error('test error')); - - const requestHandler = search.searchInElasticSearch(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as Error).message).to.equal('test error'); - expect(dbConnectionObj.release).to.have.been.calledOnce; - expect(dbConnectionObj.rollback).to.have.been.calledOnce; - } - }); - - it('returns search results when Elastic Search service succeeds with valid data', async () => { - const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { - terms: 'search-term' - }; - - sinon - .stub(SubmissionService.prototype, 'findSubmissionRecordsWithSpatialCount') - .onCall(0) - .resolves([ - { id: 'test_uuid1', source: {}, observation_count: 14 }, - { id: 'test_uuid2', source: {}, observation_count: 23 } - ]); - - const keywordSearchEmlStub = sinon.stub(ESService.prototype, 'keywordSearchEml').resolves([ - { _id: '123', _source: {}, fields: {} }, - { _id: '456', _source: {}, fields: {} } - ] as unknown as SearchHit[]); - - const requestHandler = search.searchInElasticSearch(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(keywordSearchEmlStub).to.have.been.calledOnceWith('search-term'); - expect(mockRes.jsonValue).eql([ - { id: 'test_uuid1', source: {}, observation_count: 14 }, - { id: 'test_uuid2', source: {}, observation_count: 23 } - ]); - }); - }); -}); diff --git a/api/src/paths/dwc/eml/search.ts b/api/src/paths/dwc/eml/search.ts deleted file mode 100644 index a8eacab8f..000000000 --- a/api/src/paths/dwc/eml/search.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { ESService } from '../../../services/es-service'; -import { SubmissionService } from '../../../services/submission-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/eml/search'); - -export const GET: Operation = [searchInElasticSearch()]; - -GET.apiDoc = { - description: 'searches submission files with elastic search', - tags: ['eml', 'search'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'submission search parameters.', - in: 'query', - name: 'terms', - required: false, - schema: { - type: 'string' - } - } - ], - responses: { - 200: { - description: 'Submission search response object.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - required: ['id', 'source', 'observation_count'], - properties: { - id: { - type: 'string' - }, - source: { - type: 'object' - }, - observation_count: { - type: 'number' - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Search for meta data in Elastic Search. - * - * @returns {RequestHandler} - */ -export function searchInElasticSearch(): RequestHandler { - return async (req, res) => { - const queryString = String(req.query.terms) || '*'; - - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - try { - await connection.open(); - - const elasticService = new ESService(); - - const submissionService = new SubmissionService(connection); - - const responseFromES = await elasticService.keywordSearchEml(queryString); - - const datasetIdsFromES = responseFromES.map((item) => item._id); - - const result = await submissionService.findSubmissionRecordsWithSpatialCount(datasetIdsFromES); - - // Remove null items, which indicates the provided elastic search dataset id had no matching database record. - const filteredResult = result.filter((item: any) => !!item); - - await connection.commit(); - - res.status(200).json(filteredResult); - } catch (error) { - defaultLog.error({ label: 'keywordSearchEml', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/{datasetId}/get.ts b/api/src/paths/dwc/submission/{datasetId}/get.ts index 3df0d4eec..5b853cbe8 100644 --- a/api/src/paths/dwc/submission/{datasetId}/get.ts +++ b/api/src/paths/dwc/submission/{datasetId}/get.ts @@ -5,7 +5,7 @@ import { defaultErrorResponses } from '../../../../openapi/schemas/http-response import { SubmissionService } from '../../../../services/submission-service'; import { getLogger } from '../../../../utils/logger'; -const defaultLog = getLogger('paths/dwc/eml/get'); +const defaultLog = getLogger('paths/dwc/submission/{datasetId}/get'); export const GET: Operation = [getMetadataByDatasetId()]; diff --git a/api/src/paths/gcnotify/send.ts b/api/src/paths/gcnotify/send.ts index 17f1cd6c0..cd156d3da 100644 --- a/api/src/paths/gcnotify/send.ts +++ b/api/src/paths/gcnotify/send.ts @@ -8,7 +8,7 @@ import { authorizeRequestHandler } from '../../request-handlers/security/authori import { GCNotifyService } from '../../services/gcnotify-service'; import { getLogger } from '../../utils/logger'; -const defaultLog = getLogger('paths/gcnotify'); +const defaultLog = getLogger('paths/gcnotify/send'); export const POST: Operation = [ authorizeRequestHandler(() => { diff --git a/api/src/queue/queue-registry.ts b/api/src/queue/queue-registry.ts index 0a78bdbfa..cb9620cf0 100644 --- a/api/src/queue/queue-registry.ts +++ b/api/src/queue/queue-registry.ts @@ -1,6 +1,5 @@ import { getAPIUserDBConnection } from '../database/db'; import { ISubmissionJobQueueRecord } from '../repositories/submission-job-queue-repository'; -import { DarwinCoreService } from '../services/dwc-service'; import { SubmissionJobQueueService } from '../services/submission-job-queue-service'; import { getLogger } from '../utils/logger'; @@ -13,13 +12,8 @@ export const DWC_DATASET_SUBMISSION_JOB = 'dwc_dataset_submission_job'; /** * Translates a string to a queue-job compatible function. */ -export const QueueJobRegistry = { - registry: [ - { - name: DWC_DATASET_SUBMISSION_JOB, - generator: jobQueueAttemptsWrapper(dwcDatasetSubmissionJob) - } - ], +export const QueueJobRegistry: { registry: any[]; findMatchingJob: (name: string) => QueueJob | undefined } = { + registry: [], /** * Find a job in the registry. * @@ -59,28 +53,3 @@ export function jobQueueAttemptsWrapper(queueJob: QueueJob): QueueJob { return queueJob(jobQueueRecord); }; } - -/** - * Function that runs the Darwin Core intake job. - * - * @export - * @param {ISubmissionJobQueueRecord} jobQueueRecord - */ -export async function dwcDatasetSubmissionJob(jobQueueRecord: ISubmissionJobQueueRecord) { - const connection = getAPIUserDBConnection(); - - try { - await connection.open(); - - const darwinCoreService = new DarwinCoreService(connection); - // Run darwin core intake job - - await darwinCoreService.intakeJob(jobQueueRecord); - } catch (error) { - defaultLog.error({ label: 'dwcDatasetSubmissionJob', message: 'error', error }); - throw error; - } finally { - await connection.commit(); - connection.release(); - } -} diff --git a/api/src/repositories/administrative-repository.test.ts b/api/src/repositories/administrative-repository.test.ts deleted file mode 100644 index 2066305e2..000000000 --- a/api/src/repositories/administrative-repository.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import { QueryResult } from 'pg'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { ApiExecuteSQLError } from '../errors/api-error'; -import { getMockDBConnection } from '../__mocks__/db'; -import { AdministrativeRepository, IAdministrativeActivity } from './administrative-repository'; - -chai.use(sinonChai); - -describe('AdministrativeRepository', () => { - describe('getAdministrativeActivities', async () => { - afterEach(() => { - sinon.restore(); - }); - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - try { - await administrativeRepository.getAdministrativeActivities('string', ['string']); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiExecuteSQLError).message).to.equal('Failed to get Administrative activities'); - } - }); - - it('should return rows if succeeds', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [{ id: 1 } as unknown as IAdministrativeActivity] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.getAdministrativeActivities('string', ['string']); - - expect(result).to.be.eql([{ id: 1 }]); - }); - }); - - describe('checkIfAccessRequestIsApproval', async () => { - afterEach(() => { - sinon.restore(); - }); - it('should return false if request is not Approved', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.checkIfAccessRequestIsApproval(1); - - expect(result).to.eql(false); - }); - - it('should return true if request is Approved', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [{ name: 'Actioned' }] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.checkIfAccessRequestIsApproval(1); - - expect(result).to.be.eql(true); - }); - }); - - describe('createAdministrativeActivity', async () => { - afterEach(() => { - sinon.restore(); - }); - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - try { - await administrativeRepository.createAdministrativeActivity(1, ['string']); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiExecuteSQLError).message).to.equal('Failed to submit administrative activity'); - } - }); - - it('should return rows if succeeds', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [{ id: 1 } as unknown as IAdministrativeActivity] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.createAdministrativeActivity(1, ['string']); - - expect(result).to.be.eql({ id: 1 }); - }); - }); - - describe('getPendingAccessRequestCount', async () => { - afterEach(() => { - sinon.restore(); - }); - it('should return 0 if no requests found', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.getPendingAccessRequestCount('string'); - expect(result).to.be.eql(0); - }); - - it('should return number of requests found', async () => { - const mockQueryResponse = { - rowCount: 2 - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.getPendingAccessRequestCount('string'); - - expect(result).to.be.eql(2); - }); - }); - - describe('updateAdministrativeActivity', async () => { - afterEach(() => { - sinon.restore(); - }); - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - try { - await administrativeRepository.updateAdministrativeActivity(1, 1); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiExecuteSQLError).message).to.equal('Failed to update administrative activity'); - } - }); - - it('should return rows if succeeds', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [{ id: 1 } as unknown as IAdministrativeActivity] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const administrativeRepository = new AdministrativeRepository(mockDBConnection); - - const result = await administrativeRepository.updateAdministrativeActivity(1, 1); - - expect(result).to.be.eql({ id: 1 }); - }); - }); -}); diff --git a/api/src/repositories/administrative-repository.ts b/api/src/repositories/administrative-repository.ts deleted file mode 100644 index 1817144b7..000000000 --- a/api/src/repositories/administrative-repository.ts +++ /dev/null @@ -1,224 +0,0 @@ -import SQL from 'sql-template-strings'; -import { ApiExecuteSQLError } from '../errors/api-error'; -import { BaseRepository } from './base-repository'; - -export interface IAdministrativeActivity { - id: number; - type: number; - type_name: string; - status: number; - status_name: string; - description: string; - notes: string; - data: string; - create_date: string; -} - -export interface ICreateAdministrativeActivity { - id: number; - create_date: string; -} - -export class AdministrativeRepository extends BaseRepository { - /** - * Get all Administrative Activities - * - * @param {string} administrativeActivityTypeName - * @param {string[]} administrativeActivityStatusTypes - * @return {*} {Promise} - * @memberof AdministrativeRepository - */ - async getAdministrativeActivities( - administrativeActivityTypeName: string, - administrativeActivityStatusTypes: string[] - ): Promise { - //TODO: turn sql to knex query - const sqlStatement = SQL` - SELECT - aa.administrative_activity_id as id, - aat.administrative_activity_type_id as type, - aat.name as type_name, - aast.administrative_activity_status_type_id as status, - aast.name as status_name, - aa.description, - aa.data, - aa.notes, - aa.create_date - FROM - administrative_activity aa - LEFT OUTER JOIN - administrative_activity_status_type aast - ON - aa.administrative_activity_status_type_id = aast.administrative_activity_status_type_id - LEFT OUTER JOIN - administrative_activity_type aat - ON - aa.administrative_activity_type_id = aat.administrative_activity_type_id - WHERE - 1 = 1 - `; - - if (administrativeActivityTypeName) { - sqlStatement.append(SQL` - AND - aat.name = ${administrativeActivityTypeName} - `); - } - - if (administrativeActivityStatusTypes?.length) { - sqlStatement.append(SQL` - AND - aast.name IN ( - `); - - // Add first element - sqlStatement.append(SQL`${administrativeActivityStatusTypes[0]}`); - - for (let idx = 1; idx < administrativeActivityStatusTypes.length; idx++) { - // Add subsequent elements, which get a comma prefix - sqlStatement.append(SQL`, ${administrativeActivityStatusTypes[idx]}`); - } - - sqlStatement.append(SQL`)`); - } - - sqlStatement.append(`;`); - - const response = await this.connection.sql(sqlStatement); - - if (response.rowCount <= 0) { - throw new ApiExecuteSQLError('Failed to get Administrative activities', [ - 'AdministrativeRepository->getAdministrativeActivities', - 'rowCount was null or undefined, expected rowCount >= 1' - ]); - } - - const result = (response && response.rowCount && response.rows) || []; - - return result; - } - - /** - * Check access request if actioned - * - * @param {number} adminActivityTypeId - * @return {*} {Promise} - * @memberof AdministrativeRepository - */ - async checkIfAccessRequestIsApproval(adminActivityTypeId: number): Promise { - const adminActivityStatusTypeSQLStatment = SQL` - SELECT - * - FROM - administrative_activity_status_type - WHERE - administrative_activity_status_type_id = ${adminActivityTypeId}; - `; - - const response = await this.connection.sql(adminActivityStatusTypeSQLStatment); - - if (response.rows?.[0]?.name === 'Actioned') { - return true; - } - return false; - } - - /** - * Create Administrative Activity - * - * @param {number} systemUserId - * @param {*} data - * @return {*} {Promise} - * @memberof AdministrativeRepository - */ - async createAdministrativeActivity(systemUserId: number, data: any): Promise { - const postAdministrativeActivitySQLStatement = SQL` - INSERT INTO administrative_activity ( - reported_system_user_id, - administrative_activity_type_id, - administrative_activity_status_type_id, - data - ) VALUES ( - ${systemUserId}, - 1, - 1, - ${data} - ) - RETURNING - administrative_activity_id as id, - create_date::timestamptz; - `; - - const response = await this.connection.sql(postAdministrativeActivitySQLStatement); - - const result = (response && response.rows && response.rows[0]) || null; - - if (!result || !result.id) { - throw new ApiExecuteSQLError('Failed to submit administrative activity'); - } - - return result; - } - - /** - * Get count of pending access requests - * - * @param {string} userIdentifier - * @return {*} {Promise} - * @memberof AdministrativeRepository - */ - async getPendingAccessRequestCount(userIdentifier: string): Promise { - const sqlStatement = SQL` - SELECT - * - FROM - administrative_activity aa - LEFT OUTER JOIN - administrative_activity_status_type aast - ON - aa.administrative_activity_status_type_id = aast.administrative_activity_status_type_id - WHERE - (aa.data -> 'username')::text = '"' || ${userIdentifier} || '"' - AND aast.name = 'Pending'; - `; - - const response = await this.connection.sql(sqlStatement); - - const result = (response && response.rowCount) || 0; - - return result; - } - - /** - * Update an existing administrative activity. - * - * @param {number} administrativeActivityId - * @param {number} administrativeActivityStatusTypeId - * @param {IDBConnection} connection - */ - async updateAdministrativeActivity( - administrativeActivityId: number, - administrativeActivityStatusTypeId: number - ): Promise<{ id: number }> { - const sqlStatement = SQL` - UPDATE - administrative_activity - SET - administrative_activity_status_type_id = ${administrativeActivityStatusTypeId} - WHERE - administrative_activity_id = ${administrativeActivityId} - RETURNING - administrative_activity_id as id; - `; - - const response = await this.connection.sql<{ id: number }>(sqlStatement); - - const result = (response && response.rowCount && response.rows[0]) || null; - - if (!result) { - throw new ApiExecuteSQLError('Failed to update administrative activity'); - } - - return result; - } -} diff --git a/api/src/repositories/templates/SIMS-handlebar-template.ts b/api/src/repositories/templates/SIMS-handlebar-template.ts deleted file mode 100644 index b0620da08..000000000 --- a/api/src/repositories/templates/SIMS-handlebar-template.ts +++ /dev/null @@ -1,280 +0,0 @@ -export const simsHandlebarsTemplate_HEADER = ` - - {{#if eml:eml.dataset.title}} -
-

{{eml:eml.dataset.title}}

- {{#each eml:eml.additionalMetadata as | amd |}} - {{#with (lookup amd.metadata "types") as | projectType | ~}} - {{#ifCond amd.describes '===' @root.eml:eml.dataset.[@_id]}} -
Inventory {{projectType.type}}
- {{/ifCond}} - {{/with}} - {{/each}} -
- {{/if}} -`; - -export const simsHandlebarsTemplate_DETAILS = ` -
- - -
- -
- - -
- {{#each eml:eml.additionalMetadata as | amd |}} - {{#with (lookup amd.metadata "projectAttachments") as | attachments | ~}} - -
-
- Documents -
-
- -
- - {{#each attachments.projectAttachment as | a |}} -
- {{a.file_name}} - {{#if a.is_secure}} - (secured) - {{else}} - (public) - {{/if}} -
- {{/each}} - - {{#if attachments.projectAttachment.file_name}} - {{attachments.projectAttachment.file_name}} - {{#if attachments.projectAttachment.is_secure}} - (secured) - {{else}} - (public) - {{/if}} - {{/if}} -
- - {{/with}} - {{/each}} -
- - -
- {{#each eml:eml.additionalMetadata as | amd |}} - - {{#with (lookup amd.metadata "projectReportAttachments") as | attachments | ~}} -
-
- Reports -
-
-
- - {{#each attachments.projectReportAttachment as | a |}} - - {{#if a.file_name}} -
- {{a.file_name}} - {{#if a.is_secure}} - (secured) - {{else}} - (public) - {{/if}} -
- {{/if}} - {{/each}} - - - {{#if attachments.projectReportAttachment.file_name}} - {{attachments.projectReportAttachment.file_name}} - {{#if attachments.projectReportAttachment.is_secure}} - (secured) - {{else}} - (public) - {{/if}} - {{/if}} -
- {{/with}} - {{/each}} -
-
-`; diff --git a/api/src/services/administrative-service.test.ts b/api/src/services/administrative-service.test.ts deleted file mode 100644 index 1fc9abb4f..000000000 --- a/api/src/services/administrative-service.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { - AdministrativeRepository, - IAdministrativeActivity, - ICreateAdministrativeActivity -} from '../repositories/administrative-repository'; -import { getMockDBConnection } from '../__mocks__/db'; -import { AdministrativeService } from './administrative-service'; - -chai.use(sinonChai); - -describe('administrativeService', () => { - describe('updateAdministrativeActivity', () => { - afterEach(() => { - sinon.restore(); - }); - - const dbConnectionObj = getMockDBConnection(); - const administrativeService = new AdministrativeService(dbConnectionObj); - - it('should return id value', async () => { - const AdministrativeRepositoryStub = sinon - .stub(AdministrativeRepository.prototype, 'updateAdministrativeActivity') - .resolves({ id: 1 }); - - const result = await administrativeService.updateAdministrativeActivity(1, 2); - - expect(result).to.eql({ id: 1 }); - expect(AdministrativeRepositoryStub).to.have.been.calledOnce; - }); - }); - - describe('getPendingAccessRequestCount', () => { - afterEach(() => { - sinon.restore(); - }); - - const dbConnectionObj = getMockDBConnection(); - const administrativeService = new AdministrativeService(dbConnectionObj); - - it('should return id value', async () => { - const AdministrativeRepositoryStub = sinon - .stub(AdministrativeRepository.prototype, 'getPendingAccessRequestCount') - .resolves(1); - - const result = await administrativeService.getPendingAccessRequestCount('string'); - - expect(result).to.eql(1); - expect(AdministrativeRepositoryStub).to.have.been.calledOnce; - }); - }); - - describe('createAdministrativeActivity', () => { - afterEach(() => { - sinon.restore(); - }); - - const dbConnectionObj = getMockDBConnection(); - const administrativeService = new AdministrativeService(dbConnectionObj); - - it('should return id value', async () => { - const AdministrativeRepositoryStub = sinon - .stub(AdministrativeRepository.prototype, 'createAdministrativeActivity') - .resolves({ id: 1 } as unknown as ICreateAdministrativeActivity); - - const result = await administrativeService.createAdministrativeActivity(1, 'string'); - - expect(result).to.eql({ id: 1 }); - expect(AdministrativeRepositoryStub).to.have.been.calledOnce; - }); - }); - - describe('getAdministrativeActivities', () => { - afterEach(() => { - sinon.restore(); - }); - - const dbConnectionObj = getMockDBConnection(); - const administrativeService = new AdministrativeService(dbConnectionObj); - - it('should return id value', async () => { - const AdministrativeRepositoryStub = sinon - .stub(AdministrativeRepository.prototype, 'getAdministrativeActivities') - .resolves([{ id: 1 } as unknown as IAdministrativeActivity]); - - const result = await administrativeService.getAdministrativeActivities('string', ['string']); - - expect(result).to.eql([{ id: 1 }]); - expect(AdministrativeRepositoryStub).to.have.been.calledOnce; - }); - }); -}); diff --git a/api/src/services/administrative-service.ts b/api/src/services/administrative-service.ts deleted file mode 100644 index 7664da35f..000000000 --- a/api/src/services/administrative-service.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { IDBConnection } from '../database/db'; -import { - AdministrativeRepository, - IAdministrativeActivity, - ICreateAdministrativeActivity -} from '../repositories/administrative-repository'; -import { DBService } from './db-service'; - -export class AdministrativeService extends DBService { - administrativeRepository: AdministrativeRepository; - - constructor(connection: IDBConnection) { - super(connection); - - this.administrativeRepository = new AdministrativeRepository(connection); - } - - /** - * Get all Administrative Activities - * - * @param {string} administrativeActivityTypeName - * @param {string[]} administrativeActivityStatusTypes - * @return {*} {Promise} - * @memberof AdministrativeService - */ - async getAdministrativeActivities( - administrativeActivityTypeName: string, - administrativeActivityStatusTypes: string[] - ): Promise { - return this.administrativeRepository.getAdministrativeActivities( - administrativeActivityTypeName, - administrativeActivityStatusTypes - ); - } - - /** - * Create Administrative Activity - * - * @param {number} systemUserId - * @param {*} data - * @return {*} {Promise} - * @memberof AdministrativeService - */ - async createAdministrativeActivity(systemUserId: number, data: any): Promise { - return this.administrativeRepository.createAdministrativeActivity(systemUserId, data); - } - - /** - * Get count of pending access requests - * - * @param {string} userIdentifier - * @return {*} {Promise} - * @memberof AdministrativeService - */ - async getPendingAccessRequestCount(userIdentifier: string): Promise { - return this.administrativeRepository.getPendingAccessRequestCount(userIdentifier); - } - - /** - * Update an existing administrative activity. - * - * @param {number} administrativeActivityId - * @param {number} administrativeActivityStatusTypeId - * @param {IDBConnection} connection - */ - async updateAdministrativeActivity( - administrativeActivityId: number, - administrativeActivityStatusTypeId: number - ): Promise<{ id: number }> { - return this.administrativeRepository.updateAdministrativeActivity( - administrativeActivityId, - administrativeActivityStatusTypeId - ); - } -} diff --git a/api/src/services/artifact-service.ts b/api/src/services/artifact-service.ts index 2c9c17247..aa7d43cc0 100644 --- a/api/src/services/artifact-service.ts +++ b/api/src/services/artifact-service.ts @@ -29,17 +29,6 @@ export class ArtifactService extends DBService { this.submissionService = new SubmissionService(connection); } - /** - * Retrieves an array of of new primary keys for an artifact record. - * - * @param {number} [count=1] The number of artifact primary keys to generate (by default, only 1). - * @returns {*} {Promise} The array of artifact primary keys - * @memberof ArtifactRepository - */ - async getNextArtifactIds(count = 1): Promise { - return this.artifactRepository.getNextArtifactIds(count); - } - /** * Inserts a new artifact record * diff --git a/api/src/services/dwc-service.test.ts b/api/src/services/dwc-service.test.ts deleted file mode 100644 index 6589634e3..000000000 --- a/api/src/services/dwc-service.test.ts +++ /dev/null @@ -1,1500 +0,0 @@ -import { Client } from '@elastic/elasticsearch'; -import { WriteResponseBase } from '@elastic/elasticsearch/lib/api/types'; -import { S3 } from 'aws-sdk'; -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import { QueryResult } from 'pg'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { ApiExecuteSQLError, ApiGeneralError } from '../errors/api-error'; -import { - ISourceTransformModel, - ISubmissionJobQueueRecord, - ISubmissionModel, - SUBMISSION_STATUS_TYPE -} from '../repositories/submission-repository'; -import * as fileUtils from '../utils/file-utils'; -import { CSVWorksheet } from '../utils/media/csv/csv-file'; -import * as dwcUtils from '../utils/media/dwc/dwc-archive-file'; -import { DWCArchive } from '../utils/media/dwc/dwc-archive-file'; -import { ArchiveFile, MediaFile } from '../utils/media/media-file'; -import * as mediaUtils from '../utils/media/media-utils'; -import { getMockDBConnection } from '../__mocks__/db'; -import { DarwinCoreService } from './dwc-service'; -import { EMLService } from './eml-service'; -import { ElasticSearchIndices, ESService } from './es-service'; -import { SpatialService } from './spatial-service'; -import { SubmissionService } from './submission-service'; - -chai.use(sinonChai); - -describe.skip('DarwinCoreService', () => { - afterEach(() => { - sinon.restore(); - }); - - describe.skip('intakeJob', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - - const mediaFileStub = sinon.createStubInstance(MediaFile); - const bufferStub = sinon.createStubInstance(Buffer); - bufferStub.toString.returns( - '' - ); - mediaFileStub.buffer = bufferStub as unknown as Buffer; - - const mockDWCAFile = { - submission_id: 1, - eml: { - emlFile: mediaFileStub - }, - worksheets: {} - } as unknown as DWCArchive; - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const step1 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_1').resolves({ submission_metadata_id: 1 }); - const step2 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_2').resolves(mockDWCAFile); - const step3 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_3').resolves(); - const step4 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_4').resolves(); - const step5 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_5').resolves(); - const step6 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_6').resolves(); - const step7 = sinon - .stub(DarwinCoreService.prototype, 'intakeJob_step_7') - .resolves({ submission_observation_id: 1 }); - const step8 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_8').resolves(); - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - submission_id: 1, - //source_transform_id: 3, - uuid: '123-456-789' - }); - const step9 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_9').resolves({}); - const step10 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_10').resolves(); - const step11 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_11').resolves(); - const step12 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_12').resolves(); - const step13 = sinon.stub(DarwinCoreService.prototype, 'intakeJob_step_13').resolves(); - const submissionStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - - await service.intakeJob(mockJobQueue); - - expect(step1).to.be.calledOnce; - expect(step2).to.be.calledOnce; - expect(step3).to.be.calledOnce; - expect(step4).to.be.calledOnce; - expect(step5).to.be.calledOnce; - expect(step6).to.be.calledOnce; - expect(step7).to.be.calledOnce; - expect(step8).to.be.calledOnce; - expect(step9).to.be.calledOnce; - expect(step10).to.be.calledOnce; - expect(step11).to.be.calledOnce; - expect(step12).to.be.calledOnce; - expect(step13).to.be.calledOnce; - expect(submissionStatus).to.be.calledOnce; - }); - }); - - describe.skip('intakeJob_step_1', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should insert metadata record', async () => { - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return { rowCount: 1, rows: [{ submission_metadata_id: 1 }] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - - const response = await service.intakeJob_step_1(1); - expect(response).to.be.eql({ submission_metadata_id: 1 }); - }); - - it('should fail with `Inserting new Metadata record` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - sinon.stub(SubmissionService.prototype, 'insertSubmissionMetadataRecord').throws(); - const errorInsert = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_1(1); - } catch (error) { - expect(errorInsert).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Inserting new Metadata record'); - } - }); - }); - - describe.skip('intakeJob_step_2', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should return DWCArchive', async () => { - const mockDBConnection = getMockDBConnection(); - const dwcArchive = { - eml: { - buffer: Buffer.from('test') - }, - worksheets: { - test1: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet, - test2: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet - } - } as unknown as DWCArchive; - - const service = new DarwinCoreService(mockDBConnection); - - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - submission_id: 1, - //source_transform_id: 3, - uuid: 'uuid' - }); - const metadata = sinon - .stub(SubmissionService.prototype, 'updateSubmissionMetadataEMLSource') - .resolves({ submission_metadata_id: 1 }); - - sinon.stub(DarwinCoreService.prototype, 'getAndPrepFileFromS3').resolves(dwcArchive); - const response = await service.intakeJob_step_2( - { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: null, - job_end_timestamp: null, - key: 's3 path' - } as unknown as ISubmissionJobQueueRecord, - 1 - ); - - expect(response).to.be.eql(dwcArchive); - expect(metadata).to.be.calledOnce; - }); - - it('should throw `File eml is empty` error', async () => { - const mockDBConnection = getMockDBConnection(); - const dwcArchive = { - eml: undefined, - worksheets: { - test1: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet, - test2: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet - } - } as unknown as DWCArchive; - - const service = new DarwinCoreService(mockDBConnection); - - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - submission_id: 1, - //source_transform_id: 3, - uuid: 'uuid' - }); - const metadata = sinon - .stub(SubmissionService.prototype, 'updateSubmissionMetadataEMLSource') - .resolves({ submission_metadata_id: 1 }); - sinon.stub(DarwinCoreService.prototype, 'getAndPrepFileFromS3').resolves(dwcArchive); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_2( - { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: null, - job_end_timestamp: null, - key: 's3 key' - } as unknown as ISubmissionJobQueueRecord, - 1 - ); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect(metadata).not.to.be.called; - expect((error as ApiGeneralError).errors).to.equal('Accessing S3 File, file eml is empty'); - } - }); - - it('should throw `No S3 Key` error', async () => { - const mockDBConnection = getMockDBConnection(); - const dwcArchive = { - eml: undefined, - worksheets: { - test1: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet, - test2: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet - } - } as unknown as DWCArchive; - - const service = new DarwinCoreService(mockDBConnection); - - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - submission_id: 1, - //source_transform_id: 3, - uuid: 'uuid' - }); - const metadata = sinon - .stub(SubmissionService.prototype, 'updateSubmissionMetadataEMLSource') - .resolves({ submission_metadata_id: 1 }); - sinon.stub(DarwinCoreService.prototype, 'getAndPrepFileFromS3').resolves(dwcArchive); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_2( - { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: null, - job_end_timestamp: null, - key: '' - } as unknown as ISubmissionJobQueueRecord, - 1 - ); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect(metadata).not.to.be.called; - expect((error as ApiGeneralError).errors).to.equal('No S3 Key given'); - } - }); - - it('should throw `Accessing S3 File` error', async () => { - const mockDBConnection = getMockDBConnection(); - const dwcArchive = { - eml: { - buffer: Buffer.from('test') - }, - worksheets: { - test1: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet, - test2: { - getRowObjects: () => { - return [ - { id: 1, name: 'test' }, - { id: 2, name: 'test' } - ]; - } - } as unknown as CSVWorksheet - } - } as unknown as DWCArchive; - - const service = new DarwinCoreService(mockDBConnection); - - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').throws(); - const metadata = sinon.stub(SubmissionService.prototype, 'updateSubmissionMetadataEMLSource').resolves(); - sinon.stub(DarwinCoreService.prototype, 'getAndPrepFileFromS3').resolves(dwcArchive); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_2( - { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: null, - job_end_timestamp: null - } as unknown as ISubmissionJobQueueRecord, - 1 - ); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect(metadata).not.to.be.called; - expect((error as ApiGeneralError).message).to.equal('Accessing S3 File and Updating new Metadata record'); - // checking for another error in the flow - expect((error as ApiGeneralError).errors).not.to.equal('Accessing S3 File, file eml is empty'); - } - }); - }); - - describe.skip('intakeJob_step_3', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - - const emlXMLString = - ''; - - const mockDWCAFile = { - submission_id: 1, - eml: { - emlFile: { - buffer: { - toString: () => emlXMLString - } - } - }, - worksheets: {} - } as unknown as DWCArchive; - - const response = await service.intakeJob_step_3(1, mockDWCAFile); - const result: Record = { - '?xml': { '@_version': '1.0', '@_encoding': 'UTF-8' }, - 'eml:eml': { '@_packageId': 'urn:uuid:0cf8169f-b159-4ef9-bd43-93348bdc1e9f' } - }; - - expect(response).to.deep.equal(result); - }); - - it('should throw `file eml is empty` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockDWCAFile = { - submission_id: 1, - eml: undefined, - worksheets: {} - } as unknown as DWCArchive; - - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_3(1, mockDWCAFile); - expect.fail(); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect((error as ApiGeneralError).errors).to.equal('file eml is empty'); - } - }); - - it('should throw `Converting EML to JSON` error', async () => { - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return { rowCount: 0, rows: [] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - const mediaFileStub = sinon.createStubInstance(MediaFile); - const bufferStub = sinon.createStubInstance(Buffer); - bufferStub.toString.returns( - '' - ); - - mediaFileStub.buffer = bufferStub as unknown as Buffer; - - const mockDWCAFile = { - submission_id: 1, - eml: { - emlFile: mediaFileStub - }, - worksheets: {} - } as unknown as DWCArchive; - sinon.stub(EMLService.prototype, 'convertXMLStringToJSObject').throws(); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_3(1, mockDWCAFile); - expect.fail(); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Converting EML to JSON'); - } - }); - }); - - describe.skip('intakeJob_step_4', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const update = sinon.stub(SubmissionService.prototype, 'updateSubmissionRecordEMLJSONSource').resolves(); - - await service.intakeJob_step_4(1, 1, {}); - - expect(update).to.be.calledOnce; - }); - - it('should throw a `Storing eml JSON data`', async () => { - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return { rowCount: 0, rows: [] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_4(1, 1, {}); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Storing eml JSON data'); - } - }); - }); - - describe.skip('intakeJob_step_5', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const mediaFileStub = sinon.createStubInstance(MediaFile); - const bufferStub = sinon.createStubInstance(Buffer); - - bufferStub.toString.returns( - '' - ); - - mediaFileStub.buffer = bufferStub as unknown as Buffer; - - const mockDWCAFile = { - submission_id: 1, - eml: { - emlFile: mediaFileStub - }, - worksheets: {}, - normalize: () => 'normalized data' - } as unknown as DWCArchive; - const service = new DarwinCoreService(mockDBConnection); - const response = await service.intakeJob_step_5(mockDWCAFile); - expect(response).to.be.eql('normalized data'); - }); - }); - - describe.skip('intakeJob_step_6', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const updateEndDate = sinon.stub(SubmissionService.prototype, 'updateSubmissionMetadataRecordEndDate').resolves(); - const updateEffectiveDate = sinon - .stub(SubmissionService.prototype, 'updateSubmissionMetadataRecordEffectiveDate') - .resolves(); - const updateObservationEndDate = sinon - .stub(DarwinCoreService.prototype, 'updateSubmissionObservationEndTimestamp') - .resolves(); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.intakeJob_step_6(mockJobQueue); - - expect(updateEndDate).to.be.called; - expect(updateEffectiveDate).to.be.called; - expect(updateObservationEndDate).to.be.called; - expect(insertStatus).to.not.be.called; - }); - - it('should throw `Update Submission` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const updateEndDate = sinon.stub(SubmissionService.prototype, 'updateSubmissionMetadataRecordEndDate').rejects(); - const updateEffectiveDate = sinon - .stub(SubmissionService.prototype, 'updateSubmissionMetadataRecordEffectiveDate') - .resolves(); - const updateObservationEndDate = sinon - .stub(DarwinCoreService.prototype, 'updateSubmissionObservationEndTimestamp') - .resolves(); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_6(mockJobQueue); - expect.fail(); - } catch (error: any) { - expect(updateEndDate).to.be.called; - expect(updateEffectiveDate).to.not.be.called; - expect(updateObservationEndDate).to.not.be.called; - expect(insertStatus).to.be.called; - - expect((error as ApiGeneralError).message).to.equal('Update Submission dates'); - } - }); - }); - - describe.skip('intakeJob_step_7', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const insert = sinon - .stub(DarwinCoreService.prototype, 'insertSubmissionObservationRecord') - .resolves({ submission_observation_id: 1 }); - - const response = await service.intakeJob_step_7(mockJobQueue, 'normalized json'); - expect(insert).to.be.calledOnce; - expect(response.submission_observation_id).to.eq(1); - }); - - it('should throw `Inserting Observation JSON`', async () => { - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return { rowCount: 0, rows: [] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const insert = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_7(mockJobQueue, 'normalized json'); - expect.fail(); - } catch (error: any) { - expect(insert).to.be.calledTwice; - expect(error.message).to.eq('Inserting Observation JSON'); - } - }); - }); - - describe.skip('intakeJob_step_8', async () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - const submissionObservationId = 2; - - const transform = sinon.stub(SpatialService.prototype, 'runSpatialTransforms').resolves(); - const status = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.intakeJob_step_8(mockJobQueue, submissionObservationId); - - expect(transform).to.be.calledOnceWith(mockJobQueue.submission_id, submissionObservationId); - expect(status).to.be.calledOnceWith( - mockJobQueue.submission_id, - SUBMISSION_STATUS_TYPE.SPATIAL_TRANSFORM_UNSECURE - ); - expect(insertErrorStatus).to.not.be.called; - }); - - it('should throw `Spatial transform` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(DarwinCoreService.prototype, 'runSpatialTransforms').throws(); - const status = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_8(mockJobQueue, 1); - expect.fail(); - } catch (error) { - expect(transform).to.be.calledOnce; - expect(status).to.not.be.called; - expect(insertErrorStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Spatial transform'); - } - }); - }); - - describe.skip('intakeJob_step_9', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const decorate = sinon.stub(EMLService.prototype, 'decorateEML').resolves({ - '?xml': { - '@_encoding': 'UTF-8', - '@_version': '1.0' - }, - 'eml:eml': { - '@_packageId': 'urn:uuid:0cf8169f-b159-4ef9-bd43-93348bdc1e9f' - } - }); - - await service.intakeJob_step_9(1, '123-456-789', {}); - expect(decorate).to.be.calledOnce; - }); - - it('should throw `EML Decoration` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - sinon.stub(EMLService.prototype, 'decorateEML').throws(); - // const insertError = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_9(1, '123-456-789', {}); - expect.fail(); - } catch (error: any) { - // expect(insertError).to.be.calledOnce; - expect(error.message).to.eql('EML Decoration'); - } - }); - }); - - describe.skip('intakeJob_step_10', async () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const update = sinon.stub(SubmissionService.prototype, 'updateSubmissionRecordEMLJSONSource').resolves(); - - await service.intakeJob_step_10(1, 1, {}); - - expect(update).to.be.calledOnce; - }); - - it('should throw a `Updating Record EML` error', async () => { - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return { rowCount: 0, rows: [] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_10(1, 1, {}); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Updating Record EML'); - } - }); - }); - - describe.skip('intakeJob_step_11', () => { - afterEach(() => { - sinon.restore(); - }); - - // it('should run without issue', async () => { - // const mockDBConnection = getMockDBConnection(); - // const service = new DarwinCoreService(mockDBConnection); - // const mockJobQueue = { - // submission_job_queue_id: 1, - // submission_id: 1, - // job_start_timestamp: '', - // job_end_timestamp: '' - // } as ISubmissionJobQueueRecord; - // //const transform = sinon.stub(DarwinCoreService.prototype, 'transformAndUploadMetaData').resolves(); - // sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - // - // await service.intakeJob_step_11(mockJobQueue); - // - // //expect(transform).to.be.calledOnce; - // }); - - it('should throw `Transforming and uploading metadata` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - //sinon.stub(DarwinCoreService.prototype, 'transformAndUploadMetaData').throws(); - sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - const insertError = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_11(mockJobQueue); - expect.fail(); - } catch (error: any) { - expect(insertError).to.be.calledOnce; - expect(error.message).to.be.eq('Transforming and uploading metadata'); - } - }); - }); - - describe.skip('intakeJob_step_12', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - const transform = sinon.stub(DarwinCoreService.prototype, 'runSecurityTransforms').resolves(); - sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - await service.intakeJob_step_12(mockJobQueue); - - expect(transform).to.be.calledOnce; - }); - - it('should throw `Secure spatial transforms` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - sinon.stub(DarwinCoreService.prototype, 'runSecurityTransforms').throws(); - sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - const insertError = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_12(mockJobQueue); - expect.fail(); - } catch (error: any) { - expect(insertError).to.be.calledOnce; - expect(error.message).to.be.eq('Secure spatial transforms'); - } - }); - }); - - describe.skip('intakeJob_step_13', () => { - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const update = sinon.stub(DarwinCoreService.prototype, 'updateS3FileLocation').resolves(); - - await service.intakeJob_step_13(mockJobQueue); - - expect(update).to.be.calledOnce; - }); - - it('should throw `Transforming and uploading` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const updateS3 = sinon.stub(DarwinCoreService.prototype, 'updateS3FileLocation').throws(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.intakeJob_step_13(mockJobQueue); - expect.fail(); - } catch (error) { - expect(updateS3).to.be.called; - expect(insertErrorStatus).to.be.called; - } - }); - }); - - describe.skip('updateSubmissionObservationEndTimestamp', () => { - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - sinon.stub(SubmissionService.prototype, 'updateSubmissionObservationRecordEndDate').resolves(); - const submissionIssue = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.updateSubmissionObservationEndTimestamp(mockJobQueue); - expect(submissionIssue).to.not.be.called; - }); - - it('should throw `Updating Submission Observation` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - sinon.stub(SubmissionService.prototype, 'updateSubmissionObservationRecordEndDate').throws(); - const insertStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.updateSubmissionObservationEndTimestamp(mockJobQueue); - expect.fail(); - } catch (error) { - expect(insertStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal( - 'Updating Submission Observation Record End and Effective Date' - ); - } - }); - }); - - describe.skip('runTransformsOnObservations', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(DarwinCoreService.prototype, 'runSpatialTransforms').resolves(); - const security = sinon.stub(DarwinCoreService.prototype, 'runSecurityTransforms').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.runTransformsOnObservations(mockJobQueue, 1); - - expect(transform).to.be.calledOnceWith(mockJobQueue, 1); - expect(security).to.be.calledOnceWith(mockJobQueue); - expect(insertErrorStatus).to.not.be.called; - }); - - it('should throw `Running Transform` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(DarwinCoreService.prototype, 'runSpatialTransforms').throws(); - const security = sinon.stub(DarwinCoreService.prototype, 'runSecurityTransforms').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.runTransformsOnObservations(mockJobQueue, 1); - expect.fail(); - } catch (error) { - expect(transform).to.be.calledOnceWith(mockJobQueue, 1); - expect(security).to.not.be.called; - expect(insertErrorStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Running Transforms on Observation Data'); - } - }); - }); - - describe.skip('runSpatialTransforms', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - const submissionObservationId = 2; - - const transform = sinon.stub(SpatialService.prototype, 'runSpatialTransforms').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.runSpatialTransforms(mockJobQueue, submissionObservationId); - - expect(transform).to.be.calledOnceWith(mockJobQueue.submission_id, submissionObservationId); - expect(insertErrorStatus).to.not.be.called; - }); - - it('should throw `Transforming and uploading` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(SpatialService.prototype, 'runSpatialTransforms').throws(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.runSpatialTransforms(mockJobQueue, 1); - expect.fail(); - } catch (error) { - expect(transform).to.be.calledOnce; - expect(insertErrorStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Transforming and uploading spatial transforms'); - } - }); - }); - - describe.skip('runSecurityTransforms', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(SpatialService.prototype, 'runSecurityTransforms').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.runSecurityTransforms(mockJobQueue); - - expect(transform).to.be.calledOnce; - expect(insertErrorStatus).to.not.be.called; - }); - - it('should throw `Transforming and uploading` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const transform = sinon.stub(SpatialService.prototype, 'runSecurityTransforms').throws(); - const status = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatus').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.runSecurityTransforms(mockJobQueue); - expect.fail(); - } catch (error) { - expect(transform).to.be.calledOnce; - expect(status).to.not.be.called; - expect(insertErrorStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Transforming and uploading secure spatial transforms'); - } - }); - }); - - describe.skip('insertSubmissionObservationRecord', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const insertObservation = sinon.stub(SubmissionService.prototype, 'insertSubmissionObservationRecord').resolves(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - await service.insertSubmissionObservationRecord(mockJobQueue, 'dwcaJSON'); - - expect(insertObservation).to.be.calledOnce; - expect(insertErrorStatus).to.not.be.called; - }); - - it('should throw `Inserting Submission Observation` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const insertObservation = sinon.stub(SubmissionService.prototype, 'insertSubmissionObservationRecord').throws(); - const insertErrorStatus = sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - try { - await service.insertSubmissionObservationRecord(mockJobQueue, 'dwcaJSON'); - expect.fail(); - } catch (error) { - expect(insertObservation).to.be.calledOnce; - expect(insertErrorStatus).to.be.calledOnce; - expect((error as ApiGeneralError).message).to.equal('Inserting Submission Observation Record'); - } - }); - }); - - describe.skip('updateS3FileLocation', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - key: 'Key', - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const mockSubmission = { - submission_id: 1, - source_transform_id: 3, - uuid: 'uuid', - create_date: '', - create_user: 1, - update_date: null, - update_user: null, - revision_count: 0 - } as ISubmissionModel; - - const submission = sinon - .stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId') - .resolves(mockSubmission); - const moveS3 = sinon.stub(fileUtils, 'copyFileInS3').resolves(); - const deleteS3 = sinon.stub(fileUtils, 'deleteFileFromS3').resolves(); - - await service.updateS3FileLocation(mockJobQueue); - - expect(submission).to.be.calledOnce; - expect(deleteS3).to.be.calledOnce; - expect(moveS3).to.be.calledOnce; - }); - - it('should do nothing', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const mockSubmission = { - submission_id: 1, - source_transform_id: 3, - uuid: 'uuid', - create_date: '', - create_user: 1, - update_date: null, - update_user: null, - revision_count: 0 - } as ISubmissionModel; - - const submission = sinon - .stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId') - .resolves(mockSubmission); - const moveS3 = sinon.stub(fileUtils, 'copyFileInS3').resolves(); - const deleteS3 = sinon.stub(fileUtils, 'deleteFileFromS3').resolves(); - - await service.updateS3FileLocation(mockJobQueue); - - expect(submission).to.not.be.called; - expect(deleteS3).to.not.be.called; - expect(moveS3).to.not.be.called; - }); - - it('should throw error', async () => { - const mockDBConnection = getMockDBConnection({ - sql: () => { - return { rowCount: 0, rows: [] } as any as Promise>; - } - }); - const service = new DarwinCoreService(mockDBConnection); - const mockJobQueue = { - submission_job_queue_id: 1, - submission_id: 1, - key: 'Key', - job_start_timestamp: '', - job_end_timestamp: '' - } as ISubmissionJobQueueRecord; - - const moveS3 = sinon.stub(fileUtils, 'copyFileInS3').resolves(); - const deleteS3 = sinon.stub(fileUtils, 'deleteFileFromS3').resolves(); - - try { - await service.updateS3FileLocation(mockJobQueue); - expect.fail(); - } catch (error) { - expect((error as ApiExecuteSQLError).message).to.equal('Failed to get submission record'); - expect(deleteS3).to.not.be.called; - expect(moveS3).to.not.be.called; - } - }); - }); - - describe.skip('getAndPrepFileFromS3', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should run without issue', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - const dwc = sinon.createStubInstance(DWCArchive); - - sinon.stub(DarwinCoreService.prototype, 'prepDWCArchive').returns(dwc); - - const getFileFromS3Stub = sinon.stub(fileUtils, 'getFileFromS3').resolves({ Body: 'valid' }); - - const response = await service.getAndPrepFileFromS3('file-key'); - - expect(dwc).to.be.eql(response); - expect(getFileFromS3Stub).to.be.calledOnce; - }); - - it('should throw `The source file is not available` error', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new DarwinCoreService(mockDBConnection); - - const getFileFromS3Stub = sinon.stub(fileUtils, 'getFileFromS3').resolves(null as any as S3.GetObjectOutput); - - try { - await service.getAndPrepFileFromS3('file-key'); - expect.fail(); - } catch (error) { - expect((error as ApiGeneralError).message).to.equal('The source file is not available'); - expect(getFileFromS3Stub).to.be.calledOnce; - } - }); - }); - - describe.skip('ingestNewDwCADataPackage', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - const mockArchiveFile = { - rawFile: { - fileName: 'test' - }, - eml: { - buffer: Buffer.from('test') - } - }; - - const prepDWCArchiveStub = sinon - .stub(DarwinCoreService.prototype, 'prepDWCArchive') - .returns(mockArchiveFile as unknown as DWCArchive); - const insertSubmissionRecordStub = sinon - .stub(SubmissionService.prototype, 'insertSubmissionRecord') - .resolves({ submission_id: 1 }); - const getSourceTransformRecordBySystemUserIdStub = sinon - .stub(SubmissionService.prototype, 'getSourceTransformRecordBySystemUserId') - .resolves({ source_transform_id: 1 } as unknown as ISourceTransformModel); - - const response = await darwinCoreService.ingestNewDwCADataPackage( - { originalname: 'name' } as unknown as Express.Multer.File, - 'string' - ); - - expect(response).to.eql({ dataPackageId: 'string', submissionId: 1 }); - expect(prepDWCArchiveStub).to.be.calledOnce; - expect(insertSubmissionRecordStub).to.be.calledOnce; - expect(getSourceTransformRecordBySystemUserIdStub).to.be.calledOnce; - }); - }); - - describe.skip('prepDWCArchive', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when media is invalid or empty', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - sinon.stub(mediaUtils, 'parseUnknownMedia').returns(null); - - try { - await darwinCoreService.prepDWCArchive('test' as unknown as mediaUtils.UnknownMedia); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to parse submission'); - } - }); - - it('should throw an error when media is not a valid DwC Archive File', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - sinon.stub(mediaUtils, 'parseUnknownMedia').returns('test' as unknown as MediaFile); - - try { - await darwinCoreService.prepDWCArchive('test' as unknown as mediaUtils.UnknownMedia); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to parse submission'); - } - }); - - it('should succeed', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - const archiveStub = sinon.createStubInstance(ArchiveFile); - const dwcStub = sinon.createStubInstance(DWCArchive); - - sinon.stub(mediaUtils, 'parseUnknownMedia').returns(archiveStub); - const dwcAStub = sinon.stub(dwcUtils, 'DWCArchive').returns(dwcStub); - - const response = await darwinCoreService.prepDWCArchive('test' as unknown as mediaUtils.UnknownMedia); - - expect(response).to.equal(dwcStub); - expect(dwcAStub).to.be.calledOnce; - }); - }); - - describe.skip('transformAndUploadMetaData', () => { - afterEach(() => { - sinon.restore(); - }); - - // it('throws an error if there is no source_transform_id in the submission record', async () => { - // const mockDBConnection = getMockDBConnection(); - // const darwinCoreService = new DarwinCoreService(mockDBConnection); - // - // sinon - // .stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId') - // .resolves({ id: 1 } as unknown as ISubmissionModel); - // - // try { - // await darwinCoreService.transformAndUploadMetaData(1); - // expect.fail(); - // } catch (actualError) { - // expect((actualError as Error).message).to.equal('The source_transform_id is not available'); - // } - // }); - - // it('throws an error if there is no metadata_transform in the source transform record', async () => { - // const mockDBConnection = getMockDBConnection(); - // const darwinCoreService = new DarwinCoreService(mockDBConnection); - // - // sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - // submission_id: 1, - // source_transform_id: 2, - // eml_source: 'some eml source' - // } as unknown as ISubmissionModel); - // - // sinon - // .stub(SubmissionService.prototype, 'getSourceTransformRecordBySourceTransformId') - // .resolves({ source_transform_id: 2 } as unknown as ISourceTransformModel); - // - // try { - // await darwinCoreService.transformAndUploadMetaData(1); - // expect.fail(); - // } catch (actualError) { - // expect((actualError as Error).message).to.equal('The source metadata transform is not available'); - // } - // }); - - // it('throws an error if the transformed metadata is null or empty', async () => { - // const mockDBConnection = getMockDBConnection(); - // const darwinCoreService = new DarwinCoreService(mockDBConnection); - // - // sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - // submission_id: 1, - // source_transform_id: 2, - // eml_source: 'some eml source' - // } as unknown as ISubmissionModel); - // - // sinon - // .stub(SubmissionService.prototype, 'getSourceTransformRecordBySourceTransformId') - // .resolves({ source_transform_id: 2, metadata_transform: 'some transform' } as unknown as ISourceTransformModel); - // - // sinon.stub(SubmissionService.prototype, 'getSubmissionMetadataJson').resolves(''); - // - // try { - // await darwinCoreService.transformAndUploadMetaData(1); - // expect.fail(); - // } catch (actualError) { - // expect((actualError as Error).message).to.equal('The source metadata json is not available'); - // } - // }); - - // it('successfully inserts a record into elastic search', async () => { - // const mockDBConnection = getMockDBConnection(); - // const darwinCoreService = new DarwinCoreService(mockDBConnection); - // - // sinon.stub(SubmissionService.prototype, 'getSubmissionRecordBySubmissionId').resolves({ - // submission_id: 1, - // source_transform_id: 2, - // eml_source: 'some eml source', - // uuid: 'uuid' - // } as unknown as ISubmissionModel); - // - // sinon - // .stub(SubmissionService.prototype, 'getSourceTransformRecordBySourceTransformId') - // .resolves({ source_transform_id: 2, metadata_transform: 'some transform' } as unknown as ISourceTransformModel); - // - // sinon.stub(SubmissionService.prototype, 'getSubmissionMetadataJson').resolves('transformed metadata'); - // sinon.stub(SubmissionService.prototype, 'updateSubmissionMetadataWithSearchKeys').resolves(1); - // - // const uploadToElasticSearchStub = sinon - // .stub(DarwinCoreService.prototype, 'uploadToElasticSearch') - // .resolves('success response' as unknown as WriteResponseBase); - // - // await darwinCoreService.transformAndUploadMetaData(1); - // - // expect(uploadToElasticSearchStub).to.be.calledOnceWith('uuid', 'transformed metadata'); - // }); - }); - - describe.skip('uploadToElasticSearch', () => { - afterEach(() => { - sinon.restore(); - }); - - it('succeeds with valid values', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - const indexStub = sinon.stub().returns('es response'); - - sinon.stub(ESService.prototype, 'getEsClient').resolves({ - index: indexStub - } as unknown as Client); - - const response = await darwinCoreService.uploadToElasticSearch('dataPackageId', 'convertedEML'); - - expect(indexStub).to.be.calledOnceWith({ - id: 'dataPackageId', - index: ElasticSearchIndices.EML, - document: 'convertedEML' - }); - expect(response).equals('es response'); - }); - }); - - describe.skip('deleteEmlFromElasticSearchByDataPackageId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed and delete old es file', async () => { - const mockDBConnection = getMockDBConnection(); - const darwinCoreService = new DarwinCoreService(mockDBConnection); - - const esClientStub = sinon.createStubInstance(Client); - - esClientStub.delete.resolves('dataPackageId eml' as unknown as WriteResponseBase); - - const getEsClientStub = sinon - .stub(ESService.prototype, 'getEsClient') - .resolves(esClientStub as unknown as Client); - - const response = await darwinCoreService.deleteEmlFromElasticSearchByDataPackageId('dataPackageId'); - - expect(getEsClientStub).to.be.calledOnce; - expect(response).to.equal('dataPackageId eml'); - }); - }); -}); diff --git a/api/src/services/dwc-service.ts b/api/src/services/dwc-service.ts deleted file mode 100644 index 08e0dd251..000000000 --- a/api/src/services/dwc-service.ts +++ /dev/null @@ -1,779 +0,0 @@ -import { IDBConnection } from '../database/db'; -import { ApiGeneralError } from '../errors/api-error'; -import { - ISubmissionJobQueueRecord, - ISubmissionMetadataRecord, - ISubmissionObservationRecord, - SUBMISSION_MESSAGE_TYPE, - SUBMISSION_STATUS_TYPE -} from '../repositories/submission-repository'; -import { copyFileInS3, deleteFileFromS3, generateDatasetS3FileKey, getFileFromS3 } from '../utils/file-utils'; -import { getLogger } from '../utils/logger'; -import { DWCArchive } from '../utils/media/dwc/dwc-archive-file'; -import { ArchiveFile } from '../utils/media/media-file'; -import { parseUnknownMedia, UnknownMedia } from '../utils/media/media-utils'; -import { DBService } from './db-service'; -import { EMLService } from './eml-service'; -import { ElasticSearchIndices, ESService } from './es-service'; -import { SpatialService } from './spatial-service'; -import { SubmissionService } from './submission-service'; - -const defaultLog = getLogger('services/dwc-service'); - -export class DarwinCoreService extends DBService { - submissionService: SubmissionService; - spatialService: SpatialService; - emlService: EMLService; - esService: ESService; - - /** - * Creates an instance of DarwinCoreService. - * - * @param {IDBConnection} connection - * @memberof DarwinCoreService - */ - constructor(connection: IDBConnection) { - super(connection); - - this.spatialService = new SpatialService(this.connection); - this.submissionService = new SubmissionService(this.connection); - this.emlService = new EMLService(this.connection); - this.esService = new ESService(); - } - - // Look and see if the decorate needs to be run on a fresh record - async intakeJob(jobQueueRecord: ISubmissionJobQueueRecord): Promise { - // Step 1: Insert submission metadata record - const submissionMetadataId = await this.intakeJob_step_1(jobQueueRecord.submission_id); - - // Step 2: Prep file and set submission metadata eml source column - const dwcaFile = await this.intakeJob_step_2(jobQueueRecord, submissionMetadataId.submission_metadata_id); - - // Step 3: Convert eml to JSON object - const emlJSON = await this.intakeJob_step_3(jobQueueRecord.submission_id, dwcaFile); - - // Step 4: Update submission JSON column - await this.intakeJob_step_4(jobQueueRecord.submission_id, submissionMetadataId.submission_metadata_id, emlJSON); - - // Step 5: Normalize data - const normalizedJSON = await this.intakeJob_step_5(dwcaFile); - - // Step 6: End date old records - await this.intakeJob_step_6(jobQueueRecord); - - // Step 7: Insert submission observation record - const submissionObservation = await this.intakeJob_step_7(jobQueueRecord, normalizedJSON); - - // Step 8: Run spatial transforms - await this.intakeJob_step_8(jobQueueRecord, submissionObservation.submission_observation_id); - - const submissionRecord = await this.submissionService.getSubmissionRecordBySubmissionId( - jobQueueRecord.submission_id - ); - - // Step 9: Decorate EML - const decoratedEML = await this.intakeJob_step_9(jobQueueRecord.submission_id, submissionRecord.uuid, emlJSON); - - // Step 10: Update submission json column with decorated eml - await this.intakeJob_step_10( - jobQueueRecord.submission_id, - submissionMetadataId.submission_metadata_id, - decoratedEML - ); - - // Step 11: Run source transforms on decorated data - await this.intakeJob_step_11(jobQueueRecord); - - // Step 12: Run security transforms - await this.intakeJob_step_12(jobQueueRecord); - - // Step 13: Update S3 location - await this.intakeJob_step_13(jobQueueRecord); - - await this.submissionService.insertSubmissionStatus(jobQueueRecord.submission_id, SUBMISSION_STATUS_TYPE.INGESTED); - } - - /** - * Step 1 - * - Insert submission metadata record - * - * @param {number} submissionId - * @return {*} {Promise<{ - * submission_metadata_id: number; - * }>} - * @memberof DarwinCoreService - */ - async intakeJob_step_1(submissionId: number): Promise<{ - submission_metadata_id: number; - }> { - try { - const submissionMetadata: ISubmissionMetadataRecord = { - submission_id: submissionId, - eml_source: '', - eml_json_source: null - }; - - return await this.submissionService.insertSubmissionMetadataRecord(submissionMetadata); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_1', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - submissionId, - SUBMISSION_STATUS_TYPE.FAILED_INGESTION, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Inserting new Metadata record', error.message); - } - } - - /** - * Step 2 - * - Download DwCA file form S3 - * - Update submission metadata record - set EML source column - * - * @param {number} submissionJobQueueId - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async intakeJob_step_2(jobQueueRecord: ISubmissionJobQueueRecord, submissionMetadataId: number): Promise { - try { - if (!jobQueueRecord.key) { - throw new ApiGeneralError('No S3 Key given'); - } - - const file = await this.getAndPrepFileFromS3(jobQueueRecord.key); - - if (!file.eml) { - throw new ApiGeneralError('Accessing S3 File, file eml is empty'); - } - - await this.submissionService.updateSubmissionMetadataEMLSource( - jobQueueRecord.submission_id, - submissionMetadataId, - file.eml - ); - - return file; - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_2', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_EML_INGESTION, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Accessing S3 File and Updating new Metadata record', error.message); - } - } - - /** - * Step 3 - * - Convert EML to JSON - * - * @param {number} submissionId - * @param {DWCArchive} file - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async intakeJob_step_3(submissionId: number, file: DWCArchive): Promise> { - try { - if (!file.eml) { - throw new ApiGeneralError('file eml is empty'); - } - - // Convert the EML data from XML to JSON - return this.emlService.convertXMLStringToJSObject(file.eml.emlFile.buffer.toString()); - } catch (error: any) { - // TODO: does it make sense to throw/ catch here? is it even possible - defaultLog.debug({ label: 'intakeJob_step_3', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - submissionId, - SUBMISSION_STATUS_TYPE.FAILED_EML_TO_JSON, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Converting EML to JSON', error.message); - } - } - - /** - * Step 4 - * Update submission record json column - * - * @param submissionId - * @param submissionMetadataId - * @param emlJSON - */ - async intakeJob_step_4(submissionId: number, submissionMetadataId: number, emlJSON: any): Promise { - try { - await this.submissionService.updateSubmissionRecordEMLJSONSource( - submissionId, - submissionMetadataId, - JSON.stringify(emlJSON) - ); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_4', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - submissionId, - SUBMISSION_STATUS_TYPE.FAILED_EML_TO_JSON, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Storing eml JSON data', error.message); - } - } - - /** - * Step 5 - * Normalize DWCArchive file - * - * @param dwcaFile - * @returns {*} Promise - */ - async intakeJob_step_5(dwcaFile: DWCArchive): Promise { - try { - return dwcaFile.normalize(); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_5', message: 'error', error }); - // TODO: does this try catch make sense? - throw new ApiGeneralError('Normalizing DWC Archive', error.message); - } - } - - /** - * Step 6 - * End date old submission records - * - * @param jobQueueRecord - */ - async intakeJob_step_6(jobQueueRecord: ISubmissionJobQueueRecord) { - try { - // END DATE OLD METADATA - await this.submissionService.updateSubmissionMetadataRecordEndDate(jobQueueRecord.submission_id); - await this.submissionService.updateSubmissionMetadataRecordEffectiveDate(jobQueueRecord.submission_id); - - // END DATE OLF OBSERVATION RECORD - await this.updateSubmissionObservationEndTimestamp(jobQueueRecord); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_6', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.OUT_DATED_RECORD, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Update Submission dates', error.message); - } - } - - /** - * Step 7 - * Create submission observation record with normalized JSON - * - * @param jobQueueRecord - * @param normalizedJSON - * @returns - */ - async intakeJob_step_7( - jobQueueRecord: ISubmissionJobQueueRecord, - normalizedJSON: string - ): Promise<{ submission_observation_id: number }> { - try { - return await this.insertSubmissionObservationRecord(jobQueueRecord, normalizedJSON); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_7', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - throw new ApiGeneralError('Inserting Observation JSON', error.message); - } - } - - /** - * Step 8 - * Run spatial transformations - * - * @param jobQueueRecord - * @param submissionObservationId - */ - async intakeJob_step_8(jobQueueRecord: ISubmissionJobQueueRecord, submissionObservationId: number): Promise { - try { - await this.runSpatialTransforms(jobQueueRecord, submissionObservationId); - await this.submissionService.insertSubmissionStatus( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.SPATIAL_TRANSFORM_UNSECURE - ); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_8', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - throw new ApiGeneralError('Spatial transform', error.message); - } - } - - /** - * Step 9 - * Decorate EML - * - * @param {number} submissionId - * @param {string} datasetId - * @param {Record} emlJSON - * @return {*} {Promise>} Decorated JSON object - * @memberof DarwinCoreService - */ - async intakeJob_step_9( - submissionId: number, - datasetId: string, - emlJSON: Record - ): Promise> { - try { - return await this.emlService.decorateEML(submissionId, datasetId, emlJSON); - } catch (error: any) { - // TODO: does this trycatch here make sense? - defaultLog.debug({ label: 'intakeJob_step_9', message: 'error', error }); - - throw new ApiGeneralError('EML Decoration', error.message); - } - } - - /** - * Step 10 - * Update submission record with decorated EML - * - * @param submissionId number - * @param submissionMetadataId number - * @param emlJSON Record - */ - async intakeJob_step_10(submissionId: number, submissionMetadataId: number, emlJSON: Record) { - try { - await this.submissionService.updateSubmissionRecordEMLJSONSource( - submissionId, - submissionMetadataId, - JSON.stringify(emlJSON) - ); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_10', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - submissionId, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Updating Record EML', error.message); - } - } - - /** - * Step 11 - * Run source transform on decorated JSON - * @param jobQueueRecord - * @param submissionObservationId - */ - async intakeJob_step_11(jobQueueRecord: ISubmissionJobQueueRecord) { - try { - //TODO: is this deprecated? - //await this.transformAndUploadMetaData(jobQueueRecord.submission_id); - await this.submissionService.insertSubmissionStatus( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.METADATA_TO_ES - ); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_11', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_METADATA_TO_ES, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Transforming and uploading metadata', error.message); - } - } - - /** - * Step 12 - * Run security transforms on decorated JSON - * @param jobQueueRecord - */ - async intakeJob_step_12(jobQueueRecord: ISubmissionJobQueueRecord) { - try { - await this.runSecurityTransforms(jobQueueRecord); - await this.submissionService.insertSubmissionStatus( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.SPATIAL_TRANSFORM_SECURE - ); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_12', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_SECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Secure spatial transforms', error.message); - } - } - - /** - * Step 13 - * Updated S3 file location - * @param jobQueueRecord - */ - async intakeJob_step_13(jobQueueRecord: ISubmissionJobQueueRecord) { - try { - await this.updateS3FileLocation(jobQueueRecord); - } catch (error: any) { - defaultLog.debug({ label: 'intakeJob_step_13', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_UPLOAD, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('S3 location update', error.message); - } - } - - /** - * Update and set all matching submission record end timestamps. - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async updateSubmissionObservationEndTimestamp(jobQueueRecord: ISubmissionJobQueueRecord): Promise { - try { - await this.submissionService.updateSubmissionObservationRecordEndDate(jobQueueRecord.submission_id); - } catch (error: any) { - defaultLog.debug({ label: 'updateSubmissionObservationEndTimestamp', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_INGESTION, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Updating Submission Observation Record End and Effective Date', error.message); - } - } - - /** - * Run both spatial and Security Transform on Observation - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @param {number} submissionObservationId - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async runTransformsOnObservations( - jobQueueRecord: ISubmissionJobQueueRecord, - submissionObservationId: number - ): Promise { - try { - await this.runSpatialTransforms(jobQueueRecord, submissionObservationId); - - await this.runSecurityTransforms(jobQueueRecord); - } catch (error: any) { - defaultLog.debug({ label: 'runTransformsOnObservations', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Running Transforms on Observation Data', error.message); - } - } - - /** - * Insert new Submission Observation Record - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @param {string} dwcaJson - * @return {*} {Promise<{ - * submission_observation_id: number; - * }>} - * @memberof DarwinCoreService - */ - async insertSubmissionObservationRecord( - jobQueueRecord: ISubmissionJobQueueRecord, - dwcaJson: string - ): Promise<{ - submission_observation_id: number; - }> { - try { - const submissionObservationData: ISubmissionObservationRecord = { - submission_id: jobQueueRecord.submission_id, - darwin_core_source: dwcaJson, - submission_security_request: jobQueueRecord.security_request - }; - - return await this.submissionService.insertSubmissionObservationRecord(submissionObservationData); - } catch (error: any) { - defaultLog.debug({ label: 'insertSubmissionObservationRecord', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_UPLOAD, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Inserting Submission Observation Record', error.message); - } - } - - /** - * Run Spatial Transform on Submission Observation Record - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @param {number} submissionObservationId - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async runSpatialTransforms( - jobQueueRecord: ISubmissionJobQueueRecord, - submissionObservationId: number - ): Promise { - try { - //run transform on observation data - await this.spatialService.runSpatialTransforms(jobQueueRecord.submission_id, submissionObservationId); - } catch (error: any) { - defaultLog.debug({ label: 'runSpatialTransforms', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Transforming and uploading spatial transforms', error.message); - } - } - - /** - * Run Security Transform on Submission Observation Record - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - async runSecurityTransforms(jobQueueRecord: ISubmissionJobQueueRecord): Promise { - try { - //run transform on observation data - await this.spatialService.runSecurityTransforms(jobQueueRecord.submission_id); - } catch (error: any) { - defaultLog.debug({ label: 'runSecurityTransforms', message: 'error', error }); - - await this.submissionService.insertSubmissionStatusAndMessage( - jobQueueRecord.submission_id, - SUBMISSION_STATUS_TYPE.FAILED_SPATIAL_TRANSFORM_UNSECURE, - SUBMISSION_MESSAGE_TYPE.ERROR, - error.message - ); - - throw new ApiGeneralError('Transforming and uploading secure spatial transforms', error.message); - } - } - - /** - * Move S3 file to new home directory - * - * @param {ISubmissionJobQueueRecord} jobQueueRecord - * @memberof DarwinCoreService - */ - async updateS3FileLocation(jobQueueRecord: ISubmissionJobQueueRecord) { - const sourceS3Key = jobQueueRecord.key; - - if (!sourceS3Key) { - return; - } - - const submissionRecord = await this.submissionService.getSubmissionRecordBySubmissionId( - jobQueueRecord.submission_id - ); - - const fileName = `${submissionRecord.uuid}.zip`; - - const destinationS3Key = generateDatasetS3FileKey({ datasetUUID: submissionRecord.uuid, fileName: fileName }); - - // Copy object to new location - await copyFileInS3(sourceS3Key, destinationS3Key); - - // Delete original object - await deleteFileFromS3(sourceS3Key); - } - - /** - * Access file from S3 and prep into DWCA File - * - * @param {string} fileKey - * @return {*} - * @memberof DarwinCoreService - */ - async getAndPrepFileFromS3(fileKey: string) { - const s3File = await getFileFromS3(fileKey); - - if (!s3File) { - throw new ApiGeneralError('The source file is not available'); - } - - return this.prepDWCArchive(s3File); - } - - /** - * Ingest a Darwin Core Archive (DwCA) data package. - * - * @param {{ dataPackageId?: string }} [options] - * @return {*} {Promise<{ dataPackageId: string; submissionId: number }>} - * @memberof DarwinCoreService - */ - async ingestNewDwCADataPackage( - file: Express.Multer.File, - dataPackageId: string - ): Promise<{ dataPackageId: string; submissionId: number }> { - this.prepDWCArchive(file); - - // Fetch the source transform record for this submission based on the source system user id - const sourceTransformRecord = await this.submissionService.getSourceTransformRecordBySystemUserId( - this.connection.systemUserId() - ); - - const response = await this.submissionService.insertSubmissionRecord({ - source_transform_id: sourceTransformRecord.source_transform_id, - uuid: dataPackageId - }); - - const submissionId = response.submission_id; - - return { dataPackageId, submissionId }; - } - - /** - * Parse unknown submission record and convert to DWArchive file. - * - * @param {UnknownMedia} unknownMedia - * @return {*} {DWCArchive} - * @memberof DarwinCoreService - */ - prepDWCArchive(unknownMedia: UnknownMedia): DWCArchive { - const parsedMedia = parseUnknownMedia(unknownMedia); - - if (!parsedMedia) { - throw new ApiGeneralError('Failed to parse submission', [ - 'DarwinCoreService->prepDWCArchive', - 'unknown media file was empty or unable to be parsed' - ]); - } - - if (!(parsedMedia instanceof ArchiveFile)) { - throw new ApiGeneralError('Failed to parse submission', [ - 'DarwinCoreService->prepDWCArchive', - 'unknown media file was not a valid Archive file' - ]); - } - - return new DWCArchive(parsedMedia); - } - - /** - * transform submission record eml to metadata json and upload to search engine - * - * @param {number} submissionId - * @param {string} dataPackageId - * @return {*} {Promise} - * @memberof DarwinCoreService - */ - //TODO: is this deprecated? - // async transformAndUploadMetaData(submissionId: number): Promise { - // const submissionRecord = await this.submissionService.getSubmissionRecordBySubmissionId(submissionId); - // - // if (!submissionRecord.source_transform_id) { - // throw new ApiGeneralError('The source_transform_id is not available'); - // } - // - // const sourceTransformRecord = await this.submissionService.getSourceTransformRecordBySourceTransformId( - // submissionRecord.source_transform_id - // ); - // - // if (!sourceTransformRecord.metadata_transform) { - // throw new ApiGeneralError('The source metadata transform is not available'); - // } - // - // const jsonMetadata = await this.submissionService.getSubmissionMetadataJson( - // submissionId, - // sourceTransformRecord.metadata_transform - // ); - // - // if (!jsonMetadata) { - // throw new ApiGeneralError('The source metadata json is not available'); - // } - // - // // call to the ElasticSearch API to create a record with our transformed EML - // await this.uploadToElasticSearch(submissionRecord.uuid, jsonMetadata); - // - // // update submission metadata with a copy of the elastic search object - // await this.submissionService.updateSubmissionMetadataWithSearchKeys(submissionId, jsonMetadata); - // } - - /** - * Upload file to ES - * - * @param {string} dataPackageId - * @param {string} convertedEML - * @return {*} - * @memberof DarwinCoreService - */ - async uploadToElasticSearch(dataPackageId: string, convertedEML: string) { - const esClient = await this.esService.getEsClient(); - - return esClient.index({ - id: dataPackageId, - index: ElasticSearchIndices.EML, - document: convertedEML - }); - } - - /** - * Delete old data from ES - * - * @param {string} dataPackageId - * @return {*} - * @memberof DarwinCoreService - */ - async deleteEmlFromElasticSearchByDataPackageId(dataPackageId: string) { - const esClient = await this.esService.getEsClient(); - - return esClient.delete({ id: dataPackageId, index: ElasticSearchIndices.EML }); - } -} diff --git a/api/src/services/eml-service.test.ts b/api/src/services/eml-service.test.ts deleted file mode 100644 index 35806f7ec..000000000 --- a/api/src/services/eml-service.test.ts +++ /dev/null @@ -1,509 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { ISystemConstant } from '../repositories/system-constant-repository'; -import { getMockDBConnection } from '../__mocks__/db'; -import { BcgwLayerService } from './bcgw-layer-service'; -import { EMLService } from './eml-service'; -import { Srid3005 } from './geo-service'; -import { SpatialService } from './spatial-service'; -import { SystemConstantService } from './system-constant-service'; - -chai.use(sinonChai); - -describe('EMLService', () => { - afterEach(() => { - sinon.restore(); - }); - - describe('convertXMLStringToJSObject', () => { - afterEach(() => { - sinon.restore(); - }); - - it('transforms an XML string to a JS Object', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const emlXMLString = ` - - - - - My Dataset Title - - - - My Project Title - - - - My Related Project - - - - - - M-ALAM - - - - - `; - - const emlJSObject = emlService.convertXMLStringToJSObject(emlXMLString); - - expect(emlJSObject).to.eql({ - '?xml': { - '@_encoding': 'UTF-8', - '@_version': '1.0' - }, - 'eml:eml': { - '@_packageId': 'urn:uuid:0cf8169f-b159-4ef9-bd43-93348bdc1e9f', - dataset: { - '@_system': '', - title: 'My Dataset Title', - project: { - '@_system': 'project_system', - title: 'My Project Title', - relatedProject: [ - { - '@_system': '', - title: 'My Related Project' - } - ] - }, - taxonomicCoverage: [ - { - taxonId: { - '@_provider': '', - '#text': 'M-ALAM' - } - } - ] - } - } - }); - }); - }); - - describe('parseJSObjectToXMLString', () => { - afterEach(() => { - sinon.restore(); - }); - - it('transforms a JS Object to an XML string', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const emlJSObject = { - '?xml': { - '@_encoding': 'UTF-8', - '@_version': '1.0' - }, - 'eml:eml': { - '@_packageId': 'urn:uuid:0cf8169f-b159-4ef9-bd43-93348bdc1e9f', - dataset: { - '@_system': '', - title: 'My Dataset Title', - project: { - '@_system': 'project_system', - title: 'My Project Title', - relatedProject: [ - { - '@_system': '', - title: 'My Related Project' - } - ] - }, - taxonomicCoverage: [ - { - taxonId: { - '@_provider': '', - '#text': 'M-ALAM' - } - } - ] - } - } - }; - - const xmlString = emlService.convertJSObjectToXMLString(emlJSObject); - - expect(xmlString).to.eql( - 'My Dataset TitleMy Project TitleMy Related ProjectM-ALAM' - ); - }); - }); - - describe('getSystemURL', () => { - it('returns the system url', () => { - process.env.APP_HOST = 'http://localhost:7200'; - - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const response = emlService.getSystemURL(); - - expect(response).to.eql('http://localhost:7200'); - }); - }); - - describe('getDatasetSystemURL', () => { - it('returns the dataset system url', () => { - process.env.APP_HOST = 'https://www.biohub.ca'; - - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const response = emlService.getDatasetSystemURL(); - - expect(response).to.eql('https://www.biohub.ca/datasets'); - }); - }); - - describe('getTaxonomicProviderURL', () => { - it('returns url string', () => { - process.env.ELASTICSEARCH_URL = 'www.elastic.com'; - process.env.ELASTICSEARCH_TAXONOMY_INDEX = 'taxonomy_index'; - - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const response = emlService.getTaxonomicProviderURL(); - - expect(response).to.eql('www.elastic.com/taxonomy_index'); - }); - }); - - describe('getMetadataProviderNode', () => { - it('returns a metadataProvider object', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - sinon.stub(SystemConstantService.prototype, 'getSystemConstants').resolves([ - { constant_name: 'ORGANIZATION_NAME_FULL', character_value: 'organization name' }, - { constant_name: 'ORGANIZATION_URL', character_value: 'www.organization-url.com' } - ] as unknown as ISystemConstant[]); - - const response = await emlService.getMetadataProviderNode(); - - expect(response).to.eql({ - organizationName: 'organization name', - onlineUrl: 'www.organization-url.com' - }); - }); - - it('returns an empty metadataProvider object if system constants are not found', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - sinon.stub(SystemConstantService.prototype, 'getSystemConstants').resolves([] as unknown as ISystemConstant[]); - - const response = await emlService.getMetadataProviderNode(); - - expect(response).to.eql({ - organizationName: '', - onlineUrl: '' - }); - }); - }); - - describe('getRegionAdditionalMetadataNode', () => { - it('returns a region additionalMetadata object', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const submissionId = 1; - const datasetId = '123-456-789'; - - const mockBoundaryCentroidWktString = 'POINT(123,456)'; - const mockBoundaryWktString = 'POLYGON(123,456,789)'; - - // Mock `getGeometryAsWktFromBoundarySpatialComponentBySubmissionId` calls - sinon - .stub(SpatialService.prototype, 'getGeometryAsWktFromBoundarySpatialComponentBySubmissionId') - .withArgs(submissionId, SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, Srid3005) - .resolves({ geometry: mockBoundaryCentroidWktString }) - .withArgs(submissionId, SPATIAL_COMPONENT_TYPE.BOUNDARY, Srid3005) - .resolves({ geometry: mockBoundaryWktString }); - - // Mock `getEnvRegionNames` calls - sinon - .stub(BcgwLayerService.prototype, 'getEnvRegionNames') - .withArgs(mockBoundaryCentroidWktString) - .resolves(['EnvRegion1']) - .withArgs(mockBoundaryWktString) - .resolves(['EnvRegion1', 'EnvRegion2', 'EnvRegion3']); - - // Mock `getNrmRegionNames` calls - sinon - .stub(BcgwLayerService.prototype, 'getNrmRegionNames') - .withArgs(mockBoundaryCentroidWktString) - .resolves(['NrmRegion1']) - .withArgs(mockBoundaryWktString) - .resolves(['NrmRegion1']); - - const response = await emlService.getRegionAdditionalMetadataNode(submissionId, datasetId); - - expect(response).to.eql({ - describes: datasetId, - metadata: { - regions: { - env: [ - { name: 'EnvRegion1', from: 'Boundary Centroid' }, - { name: 'EnvRegion2', from: 'Boundary' }, - { name: 'EnvRegion3', from: 'Boundary' } - ], - nrm: [{ name: 'NrmRegion1', from: 'Boundary Centroid' }] - } - } - }); - }); - - it('returns a region additionalMetadata object if no regions are found', async () => { - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const submissionId = 1; - const datasetId = '123-456-789'; - - const mockBoundaryCentroidWktString = 'POINT(123,456)'; - const mockBoundaryWktString = 'POLYGON(123,456,789)'; - - // Mock `getGeometryAsWktFromBoundarySpatialComponentBySubmissionId` calls - sinon - .stub(SpatialService.prototype, 'getGeometryAsWktFromBoundarySpatialComponentBySubmissionId') - .withArgs(submissionId, SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, Srid3005) - .resolves({ geometry: mockBoundaryCentroidWktString }) - .withArgs(submissionId, SPATIAL_COMPONENT_TYPE.BOUNDARY, Srid3005) - .resolves({ geometry: mockBoundaryWktString }); - - // Mock `getEnvRegionNames` calls - sinon - .stub(BcgwLayerService.prototype, 'getEnvRegionNames') - .withArgs(mockBoundaryCentroidWktString) - .resolves([]) - .withArgs(mockBoundaryWktString) - .resolves([]); - - // Mock `getNrmRegionNames` calls - sinon - .stub(BcgwLayerService.prototype, 'getNrmRegionNames') - .withArgs(mockBoundaryCentroidWktString) - .resolves([]) - .withArgs(mockBoundaryWktString) - .resolves([]); - - const response = await emlService.getRegionAdditionalMetadataNode(submissionId, datasetId); - - expect(response).to.eql({ - describes: datasetId, - metadata: { - regions: { - env: [], - nrm: [] - } - } - }); - }); - }); - - describe('decorateEML', () => { - afterEach(() => { - sinon.restore(); - }); - - it('decorates the eml', async () => { - process.env.NODE_ENV = 'dev'; - process.env.APP_HOST = 'www.host.com'; - process.env.ELASTICSEARCH_URL = 'www.elastic.com'; - process.env.ELASTICSEARCH_TAXONOMY_INDEX = 'taxonomy_index'; - - const mockDBConnection = getMockDBConnection(); - const emlService = new EMLService(mockDBConnection); - - const submissionId = 1; - const datasetId = '123-456-789'; - - sinon - .stub(EMLService.prototype, 'getMetadataProviderNode') - .resolves({ organizationName: 'organization name', onlineUrl: 'www.organization-url.com' }); - - sinon.stub(EMLService.prototype, 'getRegionAdditionalMetadataNode').resolves({ - describes: datasetId, - metadata: { - regions: { - env: [ - { from: 'Boundary Centroid', name: 'EnvRegion1' }, - { from: 'Boundary', name: 'EnvRegion1' }, - { from: 'Boundary', name: 'EnvRegion2' } - ], - nrm: [ - { from: 'Boundary Centroid', name: 'NrmRegion1' }, - { from: 'Boundary', name: 'NrmRegion1' } - ] - } - } - }); - - const emlXMLString = ` - - - - - My Dataset Title - - - - My Project Title - - - - My Related Project - - - - - - M-ALAM - - - - Survey Area Name - - -121.904297 - -120.19043 - 51.971346 - 50.930738 - - - - - 50.930738 - -121.904297 - - - 51.971346 - -121.904297 - - - 51.971346 - -120.19043 - - - 50.930738 - -120.19043 - - - 50.930738 - -121.904297 - - - - - - - `; - - // Generate the sample test object using the real xml convert function - const emlObject = emlService.convertXMLStringToJSObject(emlXMLString); - - const decoratedEMLObject = await emlService.decorateEML(submissionId, datasetId, emlObject); - - expect(decoratedEMLObject).to.eql({ - '?xml': { - '@_encoding': 'UTF-8', - '@_version': '1.0' - }, - 'eml:eml': { - '@_packageId': 'urn:uuid:0cf8169f-b159-4ef9-bd43-93348bdc1e9f', - '@_system': 'www.host.com', - dataset: { - '@_system': 'www.host.com/datasets', - metadataProvider: [ - { - onlineUrl: 'www.organization-url.com', - organizationName: 'organization name' - } - ], - title: 'My Dataset Title', - project: { - '@_system': 'www.host.com/datasets', - title: 'My Project Title', - relatedProject: [ - { - '@_system': 'www.host.com/datasets', - title: 'My Related Project' - } - ] - }, - taxonomicCoverage: [ - { - taxonId: { - '@_provider': 'www.elastic.com/taxonomy_index', - '#text': 'M-ALAM' - } - } - ], - geographicCoverage: { - geographicDescription: 'Survey Area Name', - boundingCoordinates: { - westBoundingCoordinate: '-121.904297', - eastBoundingCoordinate: '-120.19043', - northBoundingCoordinate: '51.971346', - southBoundingCoordinate: '50.930738' - }, - datasetGPolygon: { - datasetGPolygonOuterGRing: { - gRingPoint: [ - { - gRingLatitude: '50.930738', - gRingLongitude: '-121.904297' - }, - { - gRingLatitude: '51.971346', - gRingLongitude: '-121.904297' - }, - { - gRingLatitude: '51.971346', - gRingLongitude: '-120.19043' - }, - { - gRingLatitude: '50.930738', - gRingLongitude: '-120.19043' - }, - { - gRingLatitude: '50.930738', - gRingLongitude: '-121.904297' - } - ] - } - } - } - }, - additionalMetadata: [ - { - describes: '123-456-789', - metadata: { - regions: { - env: [ - { from: 'Boundary Centroid', name: 'EnvRegion1' }, - { from: 'Boundary', name: 'EnvRegion1' }, - { from: 'Boundary', name: 'EnvRegion2' } - ], - nrm: [ - { from: 'Boundary Centroid', name: 'NrmRegion1' }, - { from: 'Boundary', name: 'NrmRegion1' } - ] - } - } - } - ] - } - }); - }); - }); -}); diff --git a/api/src/services/eml-service.ts b/api/src/services/eml-service.ts deleted file mode 100644 index 51267bbdb..000000000 --- a/api/src/services/eml-service.ts +++ /dev/null @@ -1,317 +0,0 @@ -import jsonpatch, { Operation } from 'fast-json-patch'; -import { XMLBuilder, XMLParser } from 'fast-xml-parser'; -import { JSONPath } from 'jsonpath-plus'; -import { z } from 'zod'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { IDBConnection } from '../database/db'; -import { getLogger } from '../utils/logger'; -import { BcgwLayerService } from './bcgw-layer-service'; -import { DBService } from './db-service'; -import { Srid3005 } from './geo-service'; -import { SpatialService } from './spatial-service'; -import { SystemConstantService } from './system-constant-service'; - -const defaultLog = getLogger('services/eml-service'); - -export class EMLService extends DBService { - _XMLParserOptions = { - ignoreAttributes: false, - attributeNamePrefix: '@_', - // Passes all values through as strings. This avoids problems where text fields have numbers only but need to be - // interpreted as text. - parseTagValue: false, - isArray: (tagName: string) => { - const tagsArray: Array = [ - 'relatedProject', - 'section', - 'taxonomicCoverage', - 'metadataProvider', - 'additionalMetadata' - ]; - - return tagsArray.includes(tagName); - } - }; - - /** - * Creates an instance of EMLService. - * - * @param {IDBConnection} connection - * @memberof DarwinCoreService - */ - constructor(connection: IDBConnection) { - super(connection); - } - - /** - * Converts an XML string to a JS Object. - * - * @param {string} xmlString - * @return {*} {Record} - * @memberof EMLService - */ - convertXMLStringToJSObject(xmlString: string): Record { - const xmlParser = new XMLParser(this._XMLParserOptions); - - // Zod schema to validate value is type `Record` - const recordSchema = z.record(z.any()); - - return recordSchema.parse(xmlParser.parse(xmlString)); - } - - /** - * Converts a JS Object to an XML string. - * - * @param {Record} jsObject - * @return {*} {string} - * @memberof EMLService - */ - convertJSObjectToXMLString(jsObject: Record): string { - const xmlBuilder = new XMLBuilder(this._XMLParserOptions); - - // Zod schema to validate value is type `string` - const stringSchema = z.string(); - - const x = xmlBuilder.build(jsObject); - - return stringSchema.parse(x); - } - - /** - * Get the BioHub system URL. - * - * @example - * www.biohub.gov.bc.ca - * @return {*} {string} - * @memberof EMLService - */ - getSystemURL(): string { - return process.env.APP_HOST || ''; - } - - /** - * Get the BioHub dataset URL prefix. Used in combination with a dataset uuid to create a URL to a specific dataset. - * - * @example - * www.biohub.gov.bc.ca/datasets - * @return {*} {string} - * @memberof EMLService - */ - getDatasetSystemURL(): string { - return `${this.getSystemURL()}/datasets`; - } - - /** - * Get the BioHub taxonomic system URL. - * - * @example - * www.biohub-elastic-search.gov.bc.ca/taxonomy_index - * @return {*} {string} - * @memberof EMLService - */ - getTaxonomicProviderURL(): string { - return `${process.env.ELASTICSEARCH_URL}/${process.env.ELASTICSEARCH_TAXONOMY_INDEX}`; - } - - /** - * Get a BioHub `metadataProvider` object. - * - * @return {*} {Promise<{ organizationName: string; onlineUrl: string }>} - * @memberof EMLService - */ - async getMetadataProviderNode(): Promise<{ organizationName: string; onlineUrl: string }> { - const systemConstantService = new SystemConstantService(this.connection); - const response = await systemConstantService.getSystemConstants(['ORGANIZATION_NAME_FULL', 'ORGANIZATION_URL']); - - const organizationName = - response.find((item) => item.constant_name === 'ORGANIZATION_NAME_FULL')?.character_value || ''; - const onlineUrl = response.find((item) => item.constant_name === 'ORGANIZATION_URL')?.character_value || ''; - - return { organizationName: organizationName, onlineUrl: onlineUrl }; - } - - /** - * Compares the submission `Boundary` spatial component against BCGW layers, and builds an `additionalMetadata` - * object containing the names of the matching layer features (ie: which regions does the submission boundary cross). - * - * @param {number} submissionId - * @param {string} datasetId - * @return {*} {Promise} - * @memberof EMLService - */ - async getRegionAdditionalMetadataNode(submissionId: number, datasetId: string): Promise { - const spatialService = new SpatialService(this.connection); - - // Fetch the submission `Boundary Centroid` spatial component - const bondaryCentroidSpatialComponent = - await spatialService.getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId, - SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, - Srid3005 - ); - - // Fetch the submission `Boundary` spatial component - const boundarySpatialComponent = await spatialService.getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId, - SPATIAL_COMPONENT_TYPE.BOUNDARY, - Srid3005 - ); - - const datasetRegionService = new BcgwLayerService(); - - // Fetch env `Boundary Centroid` and `Boundary` regions - const envBoundaryCentroidRegionNames = await datasetRegionService.getEnvRegionNames( - bondaryCentroidSpatialComponent.geometry - ); - const envBoundaryRegionNames = await datasetRegionService.getEnvRegionNames(boundarySpatialComponent.geometry); - - // Fetch nrm `Boundary Centroid` and `Boundary` regions - const nrmBoundaryCentroidRegionNames = await datasetRegionService.getNrmRegionNames( - bondaryCentroidSpatialComponent.geometry - ); - const nrmBoundaryRegionNames = await datasetRegionService.getNrmRegionNames(boundarySpatialComponent.geometry); - - // Build additionalMetadata object for region names - return { - describes: datasetId, - metadata: { - regions: { - env: [ - // Add renv egions found based on the boundary centroid - ...envBoundaryCentroidRegionNames.map((name) => ({ - name: name, - from: SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID - })), - // Add env regions found based on the boundary - ...envBoundaryRegionNames - .filter((item) => !envBoundaryCentroidRegionNames.includes(item)) - .map((name) => ({ name: name, from: SPATIAL_COMPONENT_TYPE.BOUNDARY })) - ], - nrm: [ - // Add nrm regions found based on the boundary centroid - ...nrmBoundaryCentroidRegionNames.map((name) => ({ - name: name, - from: SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID - })), - // Add nrm regions found based on the boundary - ...nrmBoundaryRegionNames - .filter((item) => !nrmBoundaryCentroidRegionNames.includes(item)) - .map((name) => ({ name: name, from: SPATIAL_COMPONENT_TYPE.BOUNDARY })) - ] - } - } - }; - } - - /** - * Decorates an EML object, adding additional BioHub metadata to the original EML. - * - * @param {number} submissionId - * @param {string} datasetId - * @param {Record} eml - * @return {*} {Promise>} - * @memberof EMLService - */ - async decorateEML(submissionId: number, datasetId: string, eml: Record): Promise> { - // The config for a list of patch operations to perform on the EML. - // Note: the order of these patches may be important. Below, when patches are being applied, they are applied in - // order, allows iterative patches to be supported. - const patches = [ - { - // Set the `@_system` attribute on the `eml:eml` node. - path: '$..eml:eml', - property: '@_system', - value: this.getSystemURL() - }, - { - // Set the `@_system` attribute on the `dataset` node. - path: '$..eml:eml.dataset', - property: '@_system', - value: this.getDatasetSystemURL() - }, - { - // Set the `@_system` attribute on the `project` node. - path: '$..eml:eml.dataset.project', - property: '@_system', - value: this.getDatasetSystemURL() - }, - { - // Set the `@_system` attribute on all `relatedProject` nodes. - path: '$..eml:eml..relatedProject[*]', - property: '@_system', - value: this.getDatasetSystemURL() - }, - { - // Set the `@_provider` attribute on all `taxonId` nodes that are a descendent of `taxonomicCoverage` - path: '$..eml:eml..taxonomicCoverage..taxonId', - property: '@_provider', - value: this.getTaxonomicProviderURL() - }, - { - // Add a `metadataProvider` node if one does not already exist - path: '$..eml:eml.dataset', - property: 'metadataProvider', - value: [] - }, - { - // Append an object to the `metadataProvider` node. - path: '$..eml:eml.dataset.metadataProvider', - property: '-', // append to the end of the array - value: await this.getMetadataProviderNode() - }, - { - // Add a `additionalMetadata` node if one does not already exist - path: '$..eml:eml', - property: 'additionalMetadata', - value: [] - }, - { - // Append an object to the `additionalMetadata` node. - path: '$..eml:eml.additionalMetadata', - property: '-', // append to the end of the array - value: await this.getRegionAdditionalMetadataNode(submissionId, datasetId) - } - ]; - - // Iterates over `patches` in order from first to last, applying all resulting patch operations one by one. - // Note: subsequent patches will operate on the output of the previous patches. This means iterative patches are - // possible, if needed. - for (const patch of patches) { - const patchOperations: Operation[] = []; - - try { - const paths = JSONPath({ - path: patch.path, - json: eml, - resultType: 'all' - }); - - paths.forEach((path: any) => { - // Get value of target property - const currentPropertyValue = path.value[patch.property]; - - if (currentPropertyValue) { - // Target property exists and its value is truthy: do not modify it - return; - } - - // Patch the value of the target property - const patchOperation: Operation = { - op: 'add', - path: `${path.pointer}/${patch.property}`, - value: patch.value - }; - - patchOperations.push(patchOperation); - }); - } catch (error) { - defaultLog.warn({ label: 'decorateEML', message: 'error', failed_patch: patch, error }); - } - - // Apply patch to eml object. This updated eml object will be used in subsequent patches. - eml = jsonpatch.applyPatch(eml, patchOperations).newDocument; - } - - return eml; - } -} diff --git a/api/src/services/es-service.test.ts b/api/src/services/es-service.test.ts deleted file mode 100644 index 6e8441d79..000000000 --- a/api/src/services/es-service.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Client } from '@elastic/elasticsearch'; -import chai, { expect } from 'chai'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { ESService } from './es-service'; - -chai.use(sinonChai); - -describe('ESService', () => { - describe('getEsClient', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should return new elastic search client', async () => { - const esService = new ESService(); - - const clientStub = sinon.stub().callsFake(() => { - return 'test'; - }); - - Object.setPrototypeOf(Client, clientStub); - - esService.getEsClient(); - - expect(clientStub).to.be.calledOnce; - }); - }); -}); diff --git a/api/src/services/es-service.ts b/api/src/services/es-service.ts deleted file mode 100644 index eb1034944..000000000 --- a/api/src/services/es-service.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Client } from '@elastic/elasticsearch'; -import { AggregationsAggregate, SearchHit, SearchRequest, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; - -export const ElasticSearchIndices = { - EML: process.env.ELASTICSEARCH_EML_INDEX || 'eml', - TAXONOMY: process.env.ELASTICSEARCH_TAXONOMY_INDEX || 'taxonomy_3.0.0' -}; - -/** - * Base class for services that require a elastic search connection. - * - * @export - * @class ESService - */ -export class ESService { - esClient: Client | undefined = undefined; - - /** - * Returns the Elasticsearch Client instance. If `this.esClient` isn't defined, - * a new Elasticsearch Client is instantiated. - * - * @return {*} {Promise} - * @memberof ESService - */ - async getEsClient(): Promise { - if (!this.esClient) { - this.esClient = await new Client({ node: process.env.ELASTICSEARCH_URL }); - } - return this.esClient; - } - - /** - * Performs a search in Elasticsearch. - * @param searchRequest The ES search request - * @returns {Promise>} The results of the search - */ - async _elasticSearch(searchRequest: SearchRequest): Promise[]> { - const { index, ...request } = searchRequest; - const esClient = await this.getEsClient(); - - const response: SearchResponse> = await esClient.search({ - index: String(index).toLowerCase(), - ...request - }); - - return response.hits.hits; - } - - /** - * Searches for projects based on a given species or keyword query. - * @param query The species/keywords to search for - * @returns {Promise>} The results of the search - */ - async keywordSearchEml(query: string): Promise[]> { - return this._elasticSearch({ - index: ElasticSearchIndices.EML, - - query: { - multi_match: { - fields: ['*'], - type: 'phrase_prefix', - query - } - } - }); - } - - /** - * Searches for projects based on a datasetId. - * @param query The species/keywords to search for - * @returns {Promise>} The results of the search - */ - async datasetSearchEml(datasetId: string): Promise[]> { - return this._elasticSearch({ - index: ElasticSearchIndices.EML, - query: { - ids: { - values: [datasetId] - } - }, - fields: ['*'] - }); - } -} diff --git a/api/src/services/gcnotify-service.ts b/api/src/services/gcnotify-service.ts index 065c4250b..3e85c394a 100644 --- a/api/src/services/gcnotify-service.ts +++ b/api/src/services/gcnotify-service.ts @@ -1,9 +1,7 @@ import axios, { AxiosRequestConfig } from 'axios'; -import { ACCESS_REQUEST_ADMIN_EMAIL, ACCESS_REQUEST_APPROVAL_ADMIN_EMAIL } from '../constants/notifications'; import { IDBConnection } from '../database/db'; -import { ApiError, ApiErrorType, ApiGeneralError } from '../errors/api-error'; +import { ApiError, ApiErrorType } from '../errors/api-error'; import { IgcNotifyGenericMessage, IgcNotifyPostReturn } from '../interfaces/gcnotify.interface'; -import { AdministrativeRepository } from '../repositories/administrative-repository'; import { getLogger } from '../utils/logger'; import { formatPhoneNumber, makeLoginUrl } from '../utils/string-utils'; import { ArtifactService } from './artifact-service'; @@ -55,7 +53,6 @@ interface IGcNotifyArtifactRequestAccess { const defaultLog = getLogger('services/gcnotify-service'); export class GCNotifyService extends DBService { - administrativeRepository: AdministrativeRepository; axiosConfig: AxiosRequestConfig; EMAIL_TEMPLATE: string; SECURE_DOCUMENT_REQUEST_TEMPLATE: string; @@ -68,8 +65,6 @@ export class GCNotifyService extends DBService { constructor(connection: IDBConnection) { super(connection); - this.administrativeRepository = new AdministrativeRepository(connection); - this.SECURE_DOCUMENT_REQUEST_TEMPLATE = process.env.GCNOTIFY_REQUEST_ACCESS_SECURE_DOCUMENTS || ''; this.EMAIL_TEMPLATE = process.env.GCNOTIFY_ONBOARDING_REQUEST_EMAIL_TEMPLATE || ''; this.SMS_TEMPLATE = process.env.GCNOTIFY_ONBOARDING_REQUEST_SMS_TEMPLATE || ''; @@ -225,78 +220,4 @@ export class GCNotifyService extends DBService { return result; } - - /** - * Check if access request is approved, then send Approval email - * - * @param {number} adminActivityTypeId - * @param {string} userIdentifier - * @param {string} identitySource - * @memberof GCNotifyService - */ - async sendApprovalEmail(adminActivityTypeId: number, userIdentifier: string, identitySource: string) { - if (await this.administrativeRepository.checkIfAccessRequestIsApproval(adminActivityTypeId)) { - const userEmail = await this.getUserKeycloakEmail(userIdentifier, identitySource); - this.sendAccessRequestApprovalEmail(userEmail); - } - } - - /** - * Get users email from username and identity source - * - * @param {string} userIdentifier - * @param {string} identitySource - * @return {*} {Promise} - * @memberof GCNotifyService - */ - async getUserKeycloakEmail(userIdentifier: string, identitySource: string): Promise { - // const keycloakService = new KeycloakService(); - // const userDetails = await keycloakService.getUserByUsername(`${userIdentifier}@${identitySource}`); - // return userDetails.email; - return Promise.resolve(''); - } - - /** - * Send Approval Email - * - * @param {string} userEmail - * @memberof GCNotifyService - */ - async sendAccessRequestApprovalEmail(userEmail: string) { - const APP_HOST = process.env.APP_HOST; - const NODE_ENV = process.env.NODE_ENV; - - const url = `${APP_HOST}/`; - const hrefUrl = `[click here.](${url})`; - try { - await this.sendEmailGCNotification(userEmail, { - ...ACCESS_REQUEST_APPROVAL_ADMIN_EMAIL, - subject: `${NODE_ENV}: ${ACCESS_REQUEST_APPROVAL_ADMIN_EMAIL.subject}`, - body1: `${ACCESS_REQUEST_APPROVAL_ADMIN_EMAIL.body1} ${hrefUrl}`, - footer: `${APP_HOST}` - }); - } catch (error) { - throw new ApiGeneralError('Failed to send gcNotification approval email', [(error as Error).message]); - } - } - - /** - * Send Email that Access Request has been received - * - * @memberof GCNotifyService - */ - async sendAccessRequestReceivedEmail() { - const ADMIN_EMAIL = process.env.GCNOTIFY_ADMIN_EMAIL || ''; - const APP_HOST = process.env.APP_HOST; - const NODE_ENV = process.env.NODE_ENV; - - const url = `${APP_HOST}/admin/users?authLogin=true`; - const hrefUrl = `[click here.](${url})`; - this.sendEmailGCNotification(ADMIN_EMAIL, { - ...ACCESS_REQUEST_ADMIN_EMAIL, - subject: `${NODE_ENV}: ${ACCESS_REQUEST_ADMIN_EMAIL.subject}`, - body1: `${ACCESS_REQUEST_ADMIN_EMAIL.body1} ${hrefUrl}`, - footer: `${APP_HOST}` - }); - } } diff --git a/api/src/services/spatial-service.test.ts b/api/src/services/spatial-service.test.ts index c46bd69bd..a13876b43 100644 --- a/api/src/services/spatial-service.test.ts +++ b/api/src/services/spatial-service.test.ts @@ -1,5 +1,4 @@ import chai, { expect } from 'chai'; -import { FeatureCollection } from 'geojson'; import { describe } from 'mocha'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; @@ -107,164 +106,6 @@ describe('SpatialService', () => { }); }); - describe('runSpatialTransforms', () => { - it('should return submission_spatial_component_id after running transform and inserting data', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const getSpatialTransformRecordsStub = sinon - .stub(SpatialService.prototype, 'getSpatialTransformRecords') - .resolves([ - { - spatial_transform_id: 1, - name: 'name1', - description: null, - notes: null, - transform: 'transform1' - }, - { - spatial_transform_id: 2, - name: 'name2', - description: null, - notes: null, - transform: 'transform2' - } - ]); - - const runSpatialTransformOnSubmissionIdStub = sinon - .stub(SpatialRepository.prototype, 'runSpatialTransformOnSubmissionId') - .onCall(0) - .resolves([ - { result_data: 'result1' as unknown as FeatureCollection }, - { result_data: 'result2' as unknown as FeatureCollection } - ]) - .onCall(1) - .resolves([ - { result_data: 'result3' as unknown as FeatureCollection }, - { result_data: 'result4' as unknown as FeatureCollection } - ]); - - const insertSubmissionSpatialComponentStub = sinon - .stub(SpatialRepository.prototype, 'insertSubmissionSpatialComponent') - .onCall(0) - .resolves({ submission_spatial_component_id: 3 }) - .onCall(1) - .resolves({ submission_spatial_component_id: 4 }) - .onCall(2) - .resolves({ submission_spatial_component_id: 5 }) - .onCall(3) - .resolves({ submission_spatial_component_id: 6 }); - - const insertSpatialTransformSubmissionRecordStub = sinon - .stub(SpatialRepository.prototype, 'insertSpatialTransformSubmissionRecord') - .resolves(); - - await spatialService.runSpatialTransforms(8, 9); - - expect(getSpatialTransformRecordsStub).to.be.calledOnceWith(); - expect(runSpatialTransformOnSubmissionIdStub).to.be.calledWith(8, 'transform1').calledWith(8, 'transform2'); - expect(insertSubmissionSpatialComponentStub) - .to.be.calledWith(9, 'result1') - .calledWith(9, 'result2') - .calledWith(9, 'result3') - .calledWith(9, 'result4'); - expect(insertSpatialTransformSubmissionRecordStub) - .to.be.calledWith(1, 3) - .calledWith(1, 4) - .calledWith(2, 5) - .calledWith(2, 6); - }); - }); - - describe('runSecurityTransforms', () => { - it('should return submission_security_component_id after running transform and updating data', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const getSecurityTransformRecordsStub = sinon - .stub(SpatialService.prototype, 'getSecurityTransformRecords') - .resolves([ - { - security_transform_id: 1, - name: 'name1', - description: null, - notes: null, - transform: 'transform1' - }, - { - security_transform_id: 2, - name: 'name2', - description: null, - notes: null, - transform: 'transform2' - } - ]); - - const runSecurityTransformOnSubmissionIdStub = sinon - .stub(SpatialRepository.prototype, 'runSecurityTransformOnSubmissionId') - .onCall(0) - .resolves([ - { - spatial_component: { - submission_spatial_component_id: 1, - spatial_data: 'result1' as unknown as FeatureCollection - } - }, - { - spatial_component: { - submission_spatial_component_id: 2, - spatial_data: 'result2' as unknown as FeatureCollection - } - } - ]) - .onCall(1) - .resolves([ - { - spatial_component: { - submission_spatial_component_id: 3, - spatial_data: 'result3' as unknown as FeatureCollection - } - }, - { - spatial_component: { - submission_spatial_component_id: 4, - spatial_data: 'result4' as unknown as FeatureCollection - } - } - ]); - - const updateSubmissionSpatialComponentStub = sinon - .stub(SpatialRepository.prototype, 'updateSubmissionSpatialComponentWithSecurity') - .onCall(0) - .resolves({ submission_spatial_component_id: 3 }) - .onCall(1) - .resolves({ submission_spatial_component_id: 4 }) - .onCall(2) - .resolves({ submission_spatial_component_id: 5 }) - .onCall(3) - .resolves({ submission_spatial_component_id: 6 }); - - const insertSecurityTransformSubmissionRecordStub = sinon - .stub(SpatialRepository.prototype, 'insertSecurityTransformSubmissionRecord') - .resolves(); - - await spatialService.runSecurityTransforms(9); - - expect(getSecurityTransformRecordsStub).to.be.calledOnceWith(); - expect(runSecurityTransformOnSubmissionIdStub).to.be.calledWith(9, 'transform1').calledWith(9, 'transform2'); - expect(updateSubmissionSpatialComponentStub) - .to.be.calledWith(1, 'result1') - .calledWith(2, 'result2') - .calledWith(3, 'result3') - .calledWith(4, 'result4'); - expect(insertSecurityTransformSubmissionRecordStub) - .to.be.calledWith(1, 3) - .calledWith(1, 4) - .calledWith(2, 5) - .calledWith(2, 6); - }); - }); - describe('findSpatialComponentsByCriteria', () => { it('should return spatial component search result rows', async () => { const mockDBConnection = getMockDBConnection(); diff --git a/api/src/services/spatial-service.ts b/api/src/services/spatial-service.ts index 05ee0951a..15eadc16d 100644 --- a/api/src/services/spatial-service.ts +++ b/api/src/services/spatial-service.ts @@ -93,75 +93,6 @@ export class SpatialService extends DBService { ); } - /** - * Collect transforms from db, run transformations on submission id, save result to spatial component table - * - * @param {number} submissionId - * @param {number} submissionObservationId - * @return {*} {Promise} - * @memberof SpatialService - */ - async runSpatialTransforms(submissionId: number, submissionObservationId: number): Promise { - const spatialTransformRecords = await this.getSpatialTransformRecords(); - - const promises1 = spatialTransformRecords.map(async (transformRecord) => { - const transformed = await this.spatialRepository.runSpatialTransformOnSubmissionId( - submissionId, - transformRecord.transform - ); - - const promises2 = transformed.map(async (dataPoint) => { - const submissionSpatialComponentId = await this.spatialRepository.insertSubmissionSpatialComponent( - submissionObservationId, - dataPoint.result_data - ); - - await this.insertSpatialTransformSubmissionRecord( - transformRecord.spatial_transform_id, - submissionSpatialComponentId.submission_spatial_component_id - ); - }); - - await Promise.all(promises2); - }); - - await Promise.all(promises1); - } - - /** - *Collect security transforms from db, run transformations on submission id, update the spatial component table - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SpatialService - */ - async runSecurityTransforms(submissionId: number): Promise { - const spatialTransformRecords = await this.getSecurityTransformRecords(); - - const promises1 = spatialTransformRecords.map(async (transformRecord) => { - const transformed = await this.spatialRepository.runSecurityTransformOnSubmissionId( - submissionId, - transformRecord.transform - ); - - const promises2 = transformed.map(async (dataPoint) => { - const submissionSpatialComponentId = await this.spatialRepository.updateSubmissionSpatialComponentWithSecurity( - dataPoint.spatial_component.submission_spatial_component_id, - dataPoint.spatial_component.spatial_data - ); - - await this.insertSecurityTransformSubmissionRecord( - transformRecord.security_transform_id, - submissionSpatialComponentId.submission_spatial_component_id - ); - }); - - await Promise.all(promises2); - }); - - await Promise.all(promises1); - } - /** * Query builder to find spatial component by given criteria. * diff --git a/api/src/services/validation-service.ts b/api/src/services/validation-service.ts index cffdf31b7..3f67c133b 100644 --- a/api/src/services/validation-service.ts +++ b/api/src/services/validation-service.ts @@ -8,10 +8,6 @@ import { ValidationRepository } from '../repositories/validation-repository'; import { getLogger } from '../utils/logger'; -import { ICsvState } from '../utils/media/csv/csv-file'; -import { DWCArchive } from '../utils/media/dwc/dwc-archive-file'; -import { IMediaState } from '../utils/media/media-file'; -import { ValidationSchemaParser } from '../utils/media/validation/validation-schema-parser'; import { GeoJSONFeatureCollectionZodSchema } from '../zod-schema/geoJsonZodSchema'; import { DBService } from './db-service'; @@ -192,29 +188,4 @@ export class ValidationService extends DBService { async getStyleSchemaByStyleId(styleId: number): Promise { return this.validationRepository.getStyleSchemaByStyleId(styleId); } - - /** - * Validate DWCArchive file with given stylesheet - * - * @param {DWCArchive} dwcArchive - * @param {IStyleModel} styleSchema - * @return {*} {{ validation: boolean; mediaState: IMediaState; csvState?: ICsvState[] }} - * @memberof ValidationService - */ - validateDWCArchiveWithStyleSchema( - dwcArchive: DWCArchive, - styleSchema: IStyleModel - ): { validation: boolean; mediaState: IMediaState; csvState?: ICsvState[] } { - const validationSchemaParser: ValidationSchemaParser = new ValidationSchemaParser(styleSchema); - - const mediaState: IMediaState = dwcArchive.isMediaValid(validationSchemaParser); - - if (!mediaState.isValid) { - return { validation: false, mediaState: mediaState }; - } - - const csvState: ICsvState[] = dwcArchive.isContentValid(validationSchemaParser); - - return { validation: true, mediaState: mediaState, csvState: csvState }; - } } diff --git a/app/src/AppRouter.tsx b/app/src/AppRouter.tsx index 1e6bac1f2..dd344792a 100644 --- a/app/src/AppRouter.tsx +++ b/app/src/AppRouter.tsx @@ -4,7 +4,6 @@ import NotFoundPage from 'features/404/NotFoundPage'; import AdminUsersRouter from 'features/admin/AdminUsersRouter'; import AdminDashboardRouter from 'features/admin/dashboard/AdminDashboardRouter'; import DatasetsRouter from 'features/datasets/DatasetsRouter'; -import SearchRouter from 'features/search/SearchRouter'; import SubmissionsRouter from 'features/submissions/SubmissionsRouter'; import { SystemRoleGuard } from 'guards/Guards'; import { AuthenticatedRouteGuard } from 'guards/RouteGuards'; @@ -26,12 +25,6 @@ const AppRouter: React.FC = () => { - - - - - - diff --git a/app/src/features/search/SearchPage.tsx b/app/src/features/search/SearchPage.tsx deleted file mode 100644 index 62d325e90..000000000 --- a/app/src/features/search/SearchPage.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import { mdiEyeOffOutline, mdiEyeOutline } from '@mdi/js'; -import Icon from '@mdi/react'; -import { Theme } from '@mui/material'; -import Box from '@mui/material/Box'; -import Card from '@mui/material/Card'; -import Container from '@mui/material/Container'; -import Divider from '@mui/material/Divider'; -import Link from '@mui/material/Link'; -import Paper from '@mui/material/Paper'; -import Typography from '@mui/material/Typography'; -import { makeStyles } from '@mui/styles'; -import { IErrorDialogProps } from 'components/dialog/ErrorDialog'; -import { Formik, FormikProps } from 'formik'; -import { APIError } from 'hooks/api/useAxios'; -import { useApi } from 'hooks/useApi'; -import { useDialogContext } from 'hooks/useContext'; -import useDataLoader from 'hooks/useDataLoader'; -import { IAdvancedSearch } from 'interfaces/useSearchApi.interface'; -import { truncate } from 'lodash'; -import qs from 'qs'; -import { useCallback, useRef, useState } from 'react'; -import { useHistory, useLocation } from 'react-router'; -import { pluralize as p } from 'utils/Utils'; -import SearchComponent from './SearchComponent'; - -const useStyles = makeStyles((theme: Theme) => ({ - searchResultTitle: { - fontSize: '1.125rem' - }, - datasetResultContainer: { - display: 'flex', - flexDirection: 'column' - }, - datasetTitle: { - fontSize: '1.25rem' - }, - datasetAbstract: { - display: '-webkit-box', - overflow: 'hidden', - WebkitLineClamp: 2, - WebkitBoxOrient: 'vertical', - maxWidth: '92ch' - }, - bodyContainer: { - paddingTop: theme.spacing(5), - paddingBottom: theme.spacing(5) - } -})); - -const advancedSearchInitialValues: IAdvancedSearch = { - keywords: '' -}; - -const SearchPage = () => { - const classes = useStyles(); - const biohubApi = useApi(); - const history = useHistory(); - const location = useLocation(); - const dialogContext = useDialogContext(); - - const searchDataLoader = useDataLoader((query: string) => { - return biohubApi.search.keywordSearch(query); - }); - - /** - * collection of params from url location.search - */ - const collectFilterParams = useCallback((): IAdvancedSearch => { - if (location.search) { - const urlParams = qs.parse(location.search, { ignoreQueryPrefix: true }); - - return { keywords: urlParams.keywords } as IAdvancedSearch; - } - return advancedSearchInitialValues; - }, [location.search]); - - const [formikValues, setFormikValues] = useState(collectFilterParams); - const formikRef = useRef>(null); - - /** - * Determines if the search parameters are empty - */ - const isDefaultState = (): boolean => { - return ( - JSON.stringify(!formikRef?.current || formikRef.current.values) === JSON.stringify(advancedSearchInitialValues) - ); - }; - - /** - * Updates URL search params to reflect formikRef values - */ - const updateSearchParams = () => { - const urlParams = qs.stringify(formikRef.current?.values); - history.push({ - search: `?${urlParams}` - }); - }; - - const handleResetSearchParams = () => { - history.push({ - search: `` - }); - }; - - const handleReset = async () => { - setFormikValues(advancedSearchInitialValues); - handleResetSearchParams(); - }; - - const handleSubmit = async () => { - if (!formikRef?.current) { - return; - } - - // empty Filters - if (isDefaultState()) { - return; - } - - try { - searchDataLoader.refresh(formikRef.current?.values.keywords); - - updateSearchParams(); - } catch (error) { - const apiError = error as APIError; - showFilterErrorDialog({ - dialogTitle: 'Error Searching Datasets', - dialogError: apiError?.message, - dialogErrorDetails: apiError?.errors - }); - } - }; - - const showFilterErrorDialog = (textDialogProps?: Partial) => { - dialogContext.setErrorDialog({ - onClose: () => { - dialogContext.setErrorDialog({ open: false }); - }, - onOk: () => { - dialogContext.setErrorDialog({ open: false }); - }, - ...textDialogProps, - open: true - }); - }; - - const parseResult = () => { - const newList: any[] = []; - - searchDataLoader.data && - searchDataLoader.data.forEach((dataset) => { - const datasetId = dataset.id; - - const project = dataset.source['eml:eml'].dataset.project; - - const parsedItem = { ...project, datasetId: datasetId, observationCount: dataset.observation_count }; - - newList.push(parsedItem); - }); - - return newList; - }; - - const results = parseResult(); - - searchDataLoader.load(formikRef.current?.values.keywords || formikValues.keywords); - - return ( - - - - - Find BioHub Datasets - - - innerRef={formikRef} - initialValues={formikValues} - onSubmit={handleSubmit} - onReset={handleReset} - enableReinitialize={true}> - - - - - - - {formikRef.current?.values.keywords && ( - - {searchDataLoader.isLoading ? ( - Loading... - ) : ( - <> - {`Found ${results.length} ${p(results.length, 'result')}`} - {` for '${formikRef.current?.values.keywords}'`} - - )} - - )} - - - {results.map((result: any, index: number) => ( - - - - - - {result.title} - - - - {truncate(result.abstract.section[0].para, { length: 200, separator: ' ' })} - - - - - - - {result.observationCount === 0 ? ( - - ) : ( - - )} - - {result.observationCount} observations - - - - - - - ))} - - - - ); -}; - -export default SearchPage; diff --git a/app/src/features/search/SearchRouter.tsx b/app/src/features/search/SearchRouter.tsx deleted file mode 100644 index 6b4a19bb9..000000000 --- a/app/src/features/search/SearchRouter.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Redirect, Route, Switch } from 'react-router'; -import RouteWithTitle from 'utils/RouteWithTitle'; -import { getTitle } from 'utils/Utils'; -import SearchPage from './SearchPage'; - -/** - * Router for all `/search/*` pages. - * - * @return {*} - */ -const SearchRouter: React.FC = () => { - return ( - - - - - - {/* Catch any unknown routes, and re-direct to the not found page */} - - - - - ); -}; - -export default SearchRouter; diff --git a/app/src/hooks/api/useDatasetApi.test.ts b/app/src/hooks/api/useDatasetApi.test.ts index d9b021a92..d97b085f1 100644 --- a/app/src/hooks/api/useDatasetApi.test.ts +++ b/app/src/hooks/api/useDatasetApi.test.ts @@ -13,24 +13,6 @@ describe('useDatasetApi', () => { mock.restore(); }); - it('listAllDatasets works as expected', async () => { - const response = [ - { - id: 'a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf', - fields: { - datasetTitle: ['Coastal Caribou'] - } - } - ]; - - mock.onGet('api/dwc/eml/search').reply(200, response); - - const actualResult = await useDatasetApi(axios).listAllDatasets(); - - expect(actualResult[0].id).toEqual('a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf'); - expect(actualResult[0].fields).toEqual({ datasetTitle: ['Coastal Caribou'] }); - }); - it('getDatasetEML works as expected', async () => { const response = 'response'; diff --git a/app/src/hooks/api/useDatasetApi.ts b/app/src/hooks/api/useDatasetApi.ts index 3e574b6ab..cc764c050 100644 --- a/app/src/hooks/api/useDatasetApi.ts +++ b/app/src/hooks/api/useDatasetApi.ts @@ -1,6 +1,5 @@ import { AxiosInstance } from 'axios'; import { IArtifact, IHandlebarsTemplates, IListRelatedDatasetsResponse } from 'interfaces/useDatasetApi.interface'; -import { IKeywordSearchResponse } from 'interfaces/useSearchApi.interface'; /** * Returns a set of supported api methods for working with datasets. @@ -9,17 +8,6 @@ import { IKeywordSearchResponse } from 'interfaces/useSearchApi.interface'; * @return {*} object whose properties are supported api methods. */ const useDatasetApi = (axios: AxiosInstance) => { - /** - * Fetch all datasets. - * - * @return {*} {Promise>} - */ - const listAllDatasets = async (): Promise => { - const { data } = await axios.get(`api/dwc/eml/search`); - - return data; - }; - /** * Fetch dataset metadata by datasetId. * @@ -92,7 +80,6 @@ const useDatasetApi = (axios: AxiosInstance) => { }; return { - listAllDatasets, getDatasetEML, getDataset, getDatasetArtifacts, diff --git a/app/src/hooks/api/useSearchApi.test.ts b/app/src/hooks/api/useSearchApi.test.ts index 541282dc6..531306964 100644 --- a/app/src/hooks/api/useSearchApi.test.ts +++ b/app/src/hooks/api/useSearchApi.test.ts @@ -14,23 +14,6 @@ describe('useSearchApi', () => { mock.restore(); }); - it('getSearchResults works as expected', async () => { - const res = [ - { - id: '1', - name: 'name', - objectives: 'objectives', - geometry: [] - } - ]; - - mock.onGet('api/search').reply(200, res); - - const result = await useSearchApi(axios).getSearchResults(); - - expect(result[0].id).toEqual('1'); - }); - describe('getSpatialData', () => { it('with criteria `type` works as expected', async () => { const res = [{ type: 'FeatureCollection' } as FeatureCollection]; diff --git a/app/src/hooks/api/useSearchApi.ts b/app/src/hooks/api/useSearchApi.ts index 87cda5332..bb72ed935 100644 --- a/app/src/hooks/api/useSearchApi.ts +++ b/app/src/hooks/api/useSearchApi.ts @@ -1,6 +1,6 @@ import { AxiosInstance } from 'axios'; import { Feature, GeoJsonProperties } from 'geojson'; -import { IGetSearchResultsResponse, IKeywordSearchResponse, ISpatialData } from 'interfaces/useSearchApi.interface'; +import { IGetSearchResultsResponse, ISpatialData } from 'interfaces/useSearchApi.interface'; /** * Returns a set of supported api methods for working with search functionality @@ -9,17 +9,6 @@ import { IGetSearchResultsResponse, IKeywordSearchResponse, ISpatialData } from * @return {*} object whose properties are supported api methods. */ const useSearchApi = (axios: AxiosInstance) => { - /** - * Get search results (spatial) - * - * @return {*} {Promise} - */ - const getSearchResults = async (): Promise => { - const { data } = await axios.get(`/api/search`); - - return data; - }; - const getSpatialData = async (criteria: { boundary: Feature[]; type: string[]; @@ -60,21 +49,7 @@ const useSearchApi = (axios: AxiosInstance) => { return data; }; - /** - * Get keyword search results - * - * @param searchQuery The keywords to search for - * @returns {*} {Promise<>} - */ - const keywordSearch = async (searchQuery: string): Promise => { - const { data } = await axios.get(`api/dwc/eml/search?terms=${searchQuery}`); - - return data; - }; - return { - getSearchResults, - keywordSearch, getSpatialData, getSpatialMetadata, getSpatialDataFile From 59203cdec5a313f671d55862c523b5d9bdc1ffba Mon Sep 17 00:00:00 2001 From: Kjartan Einarsson Date: Thu, 28 Mar 2024 10:41:19 -0700 Subject: [PATCH 4/7] fix tests --- api/src/queue/queue-registry.test.ts | 2 +- api/src/services/artifact-service.test.ts | 26 --------- api/src/services/validation-service.test.ts | 64 --------------------- 3 files changed, 1 insertion(+), 91 deletions(-) diff --git a/api/src/queue/queue-registry.test.ts b/api/src/queue/queue-registry.test.ts index 66464f097..7e6d9aaa7 100644 --- a/api/src/queue/queue-registry.test.ts +++ b/api/src/queue/queue-registry.test.ts @@ -8,7 +8,7 @@ import { getMockDBConnection } from '../__mocks__/db'; import { DWC_DATASET_SUBMISSION_JOB, jobQueueAttemptsWrapper, QueueJobRegistry } from './queue-registry'; describe('QueueJobRegistry', () => { - it('returns a known job function', () => { + it.skip('returns a known job function', () => { const job = QueueJobRegistry.findMatchingJob(DWC_DATASET_SUBMISSION_JOB); expect(job).not.to.be.undefined; diff --git a/api/src/services/artifact-service.test.ts b/api/src/services/artifact-service.test.ts index 02408ce91..42c0978bd 100644 --- a/api/src/services/artifact-service.test.ts +++ b/api/src/services/artifact-service.test.ts @@ -20,32 +20,6 @@ describe('ArtifactService', () => { sinon.restore(); }); - describe('getNextArtifactIds', () => { - it('should retrieve an array of artifact primary keys', async () => { - const mockDBConnection = getMockDBConnection(); - const artifactService = new ArtifactService(mockDBConnection); - - const getNextArtifactIdsStub = sinon.stub(ArtifactRepository.prototype, 'getNextArtifactIds').resolves([1, 2]); - - const result = await artifactService.getNextArtifactIds(2); - - expect(getNextArtifactIdsStub).to.be.calledWith(2); - expect(result).to.eql([1, 2]); - }); - - it('should retrieve one artifact primary key by default', async () => { - const mockDBConnection = getMockDBConnection(); - const artifactService = new ArtifactService(mockDBConnection); - - const getNextArtifactIdsStub = sinon.stub(ArtifactRepository.prototype, 'getNextArtifactIds').resolves([1]); - - const result = await artifactService.getNextArtifactIds(); - - expect(getNextArtifactIdsStub).to.be.calledWith(1); - expect(result).to.eql([1]); - }); - }); - describe('insertArtifactRecord', () => { it('should return artifact_id on insert', async () => { const mockDBConnection = getMockDBConnection(); diff --git a/api/src/services/validation-service.test.ts b/api/src/services/validation-service.test.ts index 41313830f..f5853bcb0 100644 --- a/api/src/services/validation-service.test.ts +++ b/api/src/services/validation-service.test.ts @@ -9,8 +9,6 @@ import { IStyleModel, ValidationRepository } from '../repositories/validation-repository'; -import { DWCArchive } from '../utils/media/dwc/dwc-archive-file'; -import * as validatorParser from '../utils/media/validation/validation-schema-parser'; import { getMockDBConnection } from '../__mocks__/db'; import { ValidationService } from './validation-service'; @@ -51,68 +49,6 @@ describe('ValidationService', () => { }); }); - describe('validateDWCArchiveWithStyleSchema', () => { - it('should return a false validation with error array', async () => { - const mockDBConnection = getMockDBConnection(); - const validationService = new ValidationService(mockDBConnection); - - sinon - .stub(validatorParser, 'ValidationSchemaParser') - .returns({} as unknown as validatorParser.ValidationSchemaParser); - - const mockMediaState = { fileName: 'string', fileErrors: ['error'], isValid: false }; - - const mockDWC = { - isMediaValid: () => { - return mockMediaState; - } - }; - - const response = await validationService.validateDWCArchiveWithStyleSchema( - mockDWC as unknown as DWCArchive, - {} as unknown as IStyleModel - ); - - expect(response.validation).to.eql(false); - expect(response.mediaState.isValid).to.eql(false); - expect(response.mediaState.fileErrors).to.eql(['error']); - }); - - it('should return a true validation with csvState', async () => { - const mockDBConnection = getMockDBConnection(); - const validationService = new ValidationService(mockDBConnection); - - sinon - .stub(validatorParser, 'ValidationSchemaParser') - .returns({} as unknown as validatorParser.ValidationSchemaParser); - - const mockMediaState = { fileName: 'string', fileErrors: [], isValid: true }; - const mockCsvState = [{ headerErrors: [], rowErrors: [] }]; - - const mockDWC = { - isMediaValid: () => { - return mockMediaState; - }, - isContentValid: () => { - return mockCsvState; - } - }; - - const response = await validationService.validateDWCArchiveWithStyleSchema( - mockDWC as unknown as DWCArchive, - {} as unknown as IStyleModel - ); - - expect(response.validation).to.eql(true); - expect(response.mediaState.isValid).to.eql(true); - expect(response.mediaState.fileErrors).to.eql([]); - if (response.csvState) { - expect(response.csvState[0].headerErrors).to.eql([]); - expect(response.csvState[0].rowErrors).to.eql([]); - } - }); - }); - describe('validateSubmissionFeatures', () => { it('should return false if the dataset is invalid', async () => { const mockDBConnection = getMockDBConnection(); From 978fce7a9bd511f419bfd07f863da88d4fc32ab4 Mon Sep 17 00:00:00 2001 From: Nick Phura Date: Wed, 1 May 2024 15:16:35 -0700 Subject: [PATCH 5/7] Remove more unused code --- README/PR Based Deploy Pipeline.md | 21 - README/handlebars.md | 21 - api/.pipeline/config.js | 9 - api/.pipeline/lib/api.deploy.js | 4 - api/.pipeline/lib/queue.deploy.js | 4 - api/.pipeline/queue.config.js | 9 - api/.pipeline/templates/api.dc.yaml | 19 - api/.pipeline/templates/queue.dc.yaml | 19 - api/package-lock.json | 2080 +++--- api/package.json | 1 - api/src/paths/dwc/spatial/download.test.ts | 378 -- api/src/paths/dwc/spatial/download.ts | 116 - api/src/paths/dwc/spatial/metadata.test.ts | 198 - api/src/paths/dwc/spatial/metadata.ts | 84 - api/src/paths/dwc/spatial/search.test.ts | 475 -- api/src/paths/dwc/spatial/search.ts | 182 - api/src/paths/dwc/submission/list.test.ts | 326 - api/src/paths/dwc/submission/list.ts | 136 - .../submission/{datasetId}/artifacts.test.ts | 285 - .../dwc/submission/{datasetId}/artifacts.ts | 176 - .../dwc/submission/{datasetId}/get.test.ts | 62 - .../paths/dwc/submission/{datasetId}/get.ts | 75 - .../submission/{datasetId}/handlebar.test.ts | 34 - .../dwc/submission/{datasetId}/handlebar.ts | 82 - .../submission/{datasetId}/related.test.ts | 348 - .../dwc/submission/{datasetId}/related.ts | 128 - .../repositories/spatial-repository.test.ts | 638 -- api/src/repositories/spatial-repository.ts | 973 --- .../submission-job-queue-repository.ts | 26 - .../submission-repository.test.ts | 414 -- api/src/repositories/submission-repository.ts | 431 -- api/src/services/spatial-service.test.ts | 525 -- api/src/services/spatial-service.ts | 199 - .../submission-job-queue-service.test.ts | 59 - .../services/submission-job-queue-service.ts | 41 +- api/src/services/submission-service.test.ts | 523 +- api/src/services/submission-service.ts | 322 +- api/src/utils/media/csv/csv-file.test.ts | 273 - api/src/utils/media/csv/csv-file.ts | 299 - .../validation/csv-header-validator.test.ts | 308 - .../csv/validation/csv-header-validator.ts | 193 - .../csv/validation/csv-row-validator.test.ts | 607 -- .../media/csv/validation/csv-row-validator.ts | 353 -- .../utils/media/dwc/dwc-archive-file.test.ts | 62 - api/src/utils/media/dwc/dwc-archive-file.ts | 187 - api/src/utils/media/eml/eml-file.ts | 9 - api/src/utils/media/media-file.test.ts | 77 - api/src/utils/media/media-file.ts | 159 - api/src/utils/media/media-utils.test.ts | 203 - api/src/utils/media/media-utils.ts | 119 - .../file-type-and-content-validator.test.ts | 242 - .../file-type-and-content-validator.ts | 122 - .../validation-schema-parser.test.ts | 260 - .../validation/validation-schema-parser.ts | 279 - .../transformation-schema-parser.ts | 108 - .../transformation/xlsx-transformation.ts | 621 -- api/src/utils/media/xlsx/xlsx-file.test.ts | 14 - api/src/utils/media/xlsx/xlsx-file.ts | 88 - api/src/utils/media/xlsx/xlsx-utils.ts | 89 - app/package-lock.json | 5647 +++++++++-------- app/package.json | 1 - app/src/components/map/DatasetPopup.tsx | 127 - app/src/components/map/FeaturePopup.tsx | 272 - app/src/components/map/MapContainer.test.tsx | 97 - app/src/components/map/MapContainer.tsx | 137 - .../components/map/components/BaseLayer.tsx | 16 - .../map/components/BaseLayerControls.tsx | 19 - app/src/components/map/components/Bounds.tsx | 60 - .../map/components/DrawControls.tsx | 226 - .../map/components/EventHandler.tsx | 16 - .../FullScreenScrollingEventHandler.tsx | 64 - .../map/components/MarkerCluster.tsx | 56 - .../map/components/MarkerClusterControls.tsx | 149 - .../map/components/StaticLayers.tsx | 63 - .../map/components/StaticLayersControls.tsx | 91 - .../map/components/UploadAreaControls.tsx | 51 - .../components/security/SecurityRuleForm.tsx | 7 +- app/src/contexts/submissionContext.tsx | 1 + .../features/datasets/DatasetPage.test.tsx | 113 - app/src/features/datasets/DatasetPage.tsx | 193 +- app/src/features/datasets/DatasetsRouter.tsx | 4 - .../components/AttachmentItemMenuButton.tsx | 119 - .../datasets/components/DatasetArtifacts.tsx | 326 - .../datasets/components/DatasetSearchForm.tsx | 144 - .../datasets/components/RelatedDatasets.tsx | 124 - .../components/RenderWithHandlebars.test.tsx | 44 - .../components/RenderWithHandlebars.tsx | 25 - .../security/ApplySecurityDialog.tsx | 292 - .../security/SecurityReasonCategory.tsx | 108 - .../security/SecurityReasonSelector.tsx | 82 - .../security/SelectedDocumentsDataset.tsx | 57 - app/src/features/home/HomePage.tsx | 64 - app/src/features/home/HomeRouter.tsx | 21 - app/src/features/map/MapPage.test.tsx | 75 - app/src/features/map/MapPage.tsx | 210 - app/src/features/map/MapRouter.tsx | 21 - app/src/features/map/SideSearchBar.test.tsx | 58 - app/src/features/map/SideSearchBar.tsx | 210 - app/src/features/search/SearchComponent.tsx | 36 - app/src/hooks/api/useDatasetApi.test.ts | 35 - app/src/hooks/api/useDatasetApi.ts | 43 +- app/src/hooks/api/useSearchApi.test.ts | 89 - app/src/hooks/api/useSearchApi.ts | 82 - app/src/hooks/api/useSubmissionsApi.test.ts | 28 - app/src/hooks/api/useSubmissionsApi.ts | 13 - app/src/hooks/useApi.ts | 9 - app/src/interfaces/useDatasetApi.interface.ts | 4 - app/src/interfaces/useSearchApi.interface.ts | 71 - .../interfaces/useSubmissionsApi.interface.ts | 18 - app/src/styles/handlebar.scss | 62 - app/src/utils/handlebarsUtils.ts | 106 - app/src/utils/spatial-utils.test.tsx | 143 - app/src/utils/spatial-utils.tsx | 230 - database/liquibase/changelog.xml | 0 database/liquibase/docker-compose.yml | 25 - .../liquibase/liquibase.docker.properties | 6 - database/package-lock.json | 858 ++- docker-compose.yml | 6 - env_config/env.docker | 7 - 119 files changed, 4511 insertions(+), 20845 deletions(-) delete mode 100644 README/PR Based Deploy Pipeline.md delete mode 100644 README/handlebars.md delete mode 100644 api/src/paths/dwc/spatial/download.test.ts delete mode 100644 api/src/paths/dwc/spatial/download.ts delete mode 100644 api/src/paths/dwc/spatial/metadata.test.ts delete mode 100644 api/src/paths/dwc/spatial/metadata.ts delete mode 100644 api/src/paths/dwc/spatial/search.test.ts delete mode 100644 api/src/paths/dwc/spatial/search.ts delete mode 100644 api/src/paths/dwc/submission/list.test.ts delete mode 100644 api/src/paths/dwc/submission/list.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/artifacts.test.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/artifacts.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/get.test.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/get.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/handlebar.test.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/handlebar.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/related.test.ts delete mode 100644 api/src/paths/dwc/submission/{datasetId}/related.ts delete mode 100644 api/src/repositories/spatial-repository.test.ts delete mode 100644 api/src/repositories/spatial-repository.ts delete mode 100644 api/src/services/spatial-service.test.ts delete mode 100644 api/src/services/spatial-service.ts delete mode 100644 api/src/utils/media/csv/csv-file.test.ts delete mode 100644 api/src/utils/media/csv/csv-file.ts delete mode 100644 api/src/utils/media/csv/validation/csv-header-validator.test.ts delete mode 100644 api/src/utils/media/csv/validation/csv-header-validator.ts delete mode 100644 api/src/utils/media/csv/validation/csv-row-validator.test.ts delete mode 100644 api/src/utils/media/csv/validation/csv-row-validator.ts delete mode 100644 api/src/utils/media/dwc/dwc-archive-file.test.ts delete mode 100644 api/src/utils/media/dwc/dwc-archive-file.ts delete mode 100644 api/src/utils/media/eml/eml-file.ts delete mode 100644 api/src/utils/media/media-file.test.ts delete mode 100644 api/src/utils/media/media-file.ts delete mode 100644 api/src/utils/media/media-utils.test.ts delete mode 100644 api/src/utils/media/media-utils.ts delete mode 100644 api/src/utils/media/validation/file-type-and-content-validator.test.ts delete mode 100644 api/src/utils/media/validation/file-type-and-content-validator.ts delete mode 100644 api/src/utils/media/validation/validation-schema-parser.test.ts delete mode 100644 api/src/utils/media/validation/validation-schema-parser.ts delete mode 100644 api/src/utils/media/xlsx/transformation/transformation-schema-parser.ts delete mode 100644 api/src/utils/media/xlsx/transformation/xlsx-transformation.ts delete mode 100644 api/src/utils/media/xlsx/xlsx-file.test.ts delete mode 100644 api/src/utils/media/xlsx/xlsx-file.ts delete mode 100644 api/src/utils/media/xlsx/xlsx-utils.ts delete mode 100644 app/src/components/map/DatasetPopup.tsx delete mode 100644 app/src/components/map/FeaturePopup.tsx delete mode 100644 app/src/components/map/MapContainer.test.tsx delete mode 100644 app/src/components/map/MapContainer.tsx delete mode 100644 app/src/components/map/components/BaseLayer.tsx delete mode 100644 app/src/components/map/components/BaseLayerControls.tsx delete mode 100644 app/src/components/map/components/Bounds.tsx delete mode 100644 app/src/components/map/components/DrawControls.tsx delete mode 100644 app/src/components/map/components/EventHandler.tsx delete mode 100644 app/src/components/map/components/FullScreenScrollingEventHandler.tsx delete mode 100644 app/src/components/map/components/MarkerCluster.tsx delete mode 100644 app/src/components/map/components/MarkerClusterControls.tsx delete mode 100644 app/src/components/map/components/StaticLayers.tsx delete mode 100644 app/src/components/map/components/StaticLayersControls.tsx delete mode 100644 app/src/components/map/components/UploadAreaControls.tsx delete mode 100644 app/src/features/datasets/DatasetPage.test.tsx delete mode 100644 app/src/features/datasets/components/AttachmentItemMenuButton.tsx delete mode 100644 app/src/features/datasets/components/DatasetArtifacts.tsx delete mode 100644 app/src/features/datasets/components/DatasetSearchForm.tsx delete mode 100644 app/src/features/datasets/components/RelatedDatasets.tsx delete mode 100644 app/src/features/datasets/components/RenderWithHandlebars.test.tsx delete mode 100644 app/src/features/datasets/components/RenderWithHandlebars.tsx delete mode 100644 app/src/features/datasets/components/security/ApplySecurityDialog.tsx delete mode 100644 app/src/features/datasets/components/security/SecurityReasonCategory.tsx delete mode 100644 app/src/features/datasets/components/security/SecurityReasonSelector.tsx delete mode 100644 app/src/features/datasets/components/security/SelectedDocumentsDataset.tsx delete mode 100644 app/src/features/home/HomePage.tsx delete mode 100644 app/src/features/home/HomeRouter.tsx delete mode 100644 app/src/features/map/MapPage.test.tsx delete mode 100644 app/src/features/map/MapPage.tsx delete mode 100644 app/src/features/map/MapRouter.tsx delete mode 100644 app/src/features/map/SideSearchBar.test.tsx delete mode 100644 app/src/features/map/SideSearchBar.tsx delete mode 100644 app/src/hooks/api/useSearchApi.test.ts delete mode 100644 app/src/hooks/api/useSearchApi.ts delete mode 100644 app/src/interfaces/useSearchApi.interface.ts delete mode 100644 app/src/styles/handlebar.scss delete mode 100644 app/src/utils/handlebarsUtils.ts delete mode 100644 app/src/utils/spatial-utils.test.tsx delete mode 100644 app/src/utils/spatial-utils.tsx delete mode 100644 database/liquibase/changelog.xml delete mode 100644 database/liquibase/docker-compose.yml delete mode 100644 database/liquibase/liquibase.docker.properties diff --git a/README/PR Based Deploy Pipeline.md b/README/PR Based Deploy Pipeline.md deleted file mode 100644 index f658c324e..000000000 --- a/README/PR Based Deploy Pipeline.md +++ /dev/null @@ -1,21 +0,0 @@ -# PR Based Deploy Pipeline - -# Duplicate Action Skipping - -The PR-Based Deployment workflow will attempt to skip jobs if it determines that no new code changes have been made since the last successful run. - -### Example: - -- The PR-Based Deploy workflow runs successfully. -- A new commit is pushed that only changes the APP. -- The PR-Base Deploy workflow runs again. - - The API, Database, and Database Setup jobs are skipped, because they have no new changes. - - The APP job is not skipped, and does a new build and deploy, because there are new changes. - -## Bypass Duplicate Action Skipping - -You may want to bypass the duplicate action skipping logic, and force a full re-run of the PR-Based Deploy workflow. - -In this case, include the keyword `ignore-skip` anywhere in the message of a commit, and when the PR-Based Deploy workflow runs, it will identify the existence of the keyword `ignore-skip` and prevent any jobs from being skipped. - -_Note: this only works for the workflow run kicked off by this specific commit. Subsequent commits that do not include `ignore-skip` will revert back to the normal duplicate action skipping logic._ diff --git a/README/handlebars.md b/README/handlebars.md deleted file mode 100644 index a55f3725e..000000000 --- a/README/handlebars.md +++ /dev/null @@ -1,21 +0,0 @@ -# Working with Handlebars - -Source: https://www.npmjs.com/package/handlebars -Repository: https://github.com/handlebars-lang/handlebars.js -Documentation: https://handlebarsjs.com/ (includes examples and editor) - -We use Handlebars to create templates used to render the metadata based on: - -- datasetId -- Source system (ie SIMS, etc), -- provided template - -To precompile a template: - -1. useHandlebars().applyConditionalChecks(); - -- this is a custom function that allows Handlebars to process the use of conditionals such as '===' - -2. parse: Handlebars.parse(rawTemplate); -3. preCompile" : Handlebars.precompile(parsedHbr); -4. The precompiled template generated above get stored in the DB. diff --git a/api/.pipeline/config.js b/api/.pipeline/config.js index 1e6d09bd5..1c09c7c12 100644 --- a/api/.pipeline/config.js +++ b/api/.pipeline/config.js @@ -80,9 +80,6 @@ const phases = { host: (isStaticDeployment && staticUrlsAPI.dev) || `${name}-${changeId}-a0ec71-dev.apps.silver.devops.gov.bc.ca`, appHost: (isStaticDeployment && staticUrls.dev) || `${appName}-${changeId}-a0ec71-dev.apps.silver.devops.gov.bc.ca`, env: 'dev', - elasticsearchURL: 'http://es01:9200', - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', itisSolrUrl: 'https://services.itis.gov', s3KeyPrefix: (isStaticDeployment && 'biohub') || `local/${deployChangeId}/biohub`, tz: config.timezone.api, @@ -109,9 +106,6 @@ const phases = { host: staticUrlsAPI.test, appHost: staticUrls.test, env: 'test', - elasticsearchURL: 'http://es01.a0ec71-dev:9200', // TODO: Update to test instance (es is not yet deployed to test) - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', itisSolrUrl: 'https://services.itis.gov', s3KeyPrefix: 'biohub', tz: config.timezone.api, @@ -138,9 +132,6 @@ const phases = { host: staticUrlsAPI.prod, appHost: staticUrls.prod, env: 'prod', - elasticsearchURL: 'http://es01:9200', - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', itisSolrUrl: 'https://services.itis.gov', s3KeyPrefix: 'biohub', tz: config.timezone.api, diff --git a/api/.pipeline/lib/api.deploy.js b/api/.pipeline/lib/api.deploy.js index aac11a3d1..36d3f2185 100644 --- a/api/.pipeline/lib/api.deploy.js +++ b/api/.pipeline/lib/api.deploy.js @@ -34,10 +34,6 @@ const apiDeploy = async (settings) => { // Node NODE_ENV: phases[phase].env || 'dev', NODE_OPTIONS: phases[phase].nodeOptions, - // Elastic Search - ELASTICSEARCH_URL: phases[phase].elasticsearchURL, - ELASTICSEARCH_EML_INDEX: phases[phase].elasticsearchEmlIndex, - ELASTICSEARCH_TAXONOMY_INDEX: phases[phase].elasticsearchTaxonomyIndex, // ITIS SOLR ITIS_SOLR_URL: phases[phase].itisSolrUrl, // S3 (Object Store) diff --git a/api/.pipeline/lib/queue.deploy.js b/api/.pipeline/lib/queue.deploy.js index e8021cba4..833abf126 100644 --- a/api/.pipeline/lib/queue.deploy.js +++ b/api/.pipeline/lib/queue.deploy.js @@ -33,10 +33,6 @@ const queueDeploy = async (settings) => { APP_HOST: phases[phase].appHost, // Node NODE_ENV: phases[phase].env || 'dev', - // Elastic Search - ELASTICSEARCH_URL: phases[phase].elasticsearchURL, - ELASTICSEARCH_EML_INDEX: phases[phase].elasticsearchEmlIndex, - ELASTICSEARCH_TAXONOMY_INDEX: phases[phase].elasticsearchTaxonomyIndex, // S3 (Object Store) S3_KEY_PREFIX: phases[phase].s3KeyPrefix, OBJECT_STORE_SECRETS: 'biohubbc-object-store', diff --git a/api/.pipeline/queue.config.js b/api/.pipeline/queue.config.js index 5c1b54f41..8e5a20b62 100644 --- a/api/.pipeline/queue.config.js +++ b/api/.pipeline/queue.config.js @@ -83,9 +83,6 @@ const phases = { host: (isStaticDeployment && staticUrlsAPI.dev) || `${name}-${changeId}-a0ec71-dev.apps.silver.devops.gov.bc.ca`, appHost: (isStaticDeployment && staticUrls.dev) || `${appName}-${changeId}-a0ec71-dev.apps.silver.devops.gov.bc.ca`, env: 'dev', - elasticsearchURL: 'http://es01:9200', - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', s3KeyPrefix: 'biohub', tz: config.timezone.api, logLevel: 'debug', @@ -110,9 +107,6 @@ const phases = { host: staticUrlsAPI.test, appHost: staticUrls.test, env: 'test', - elasticsearchURL: 'http://es01.a0ec71-dev:9200', // TODO: Update to test instance (es is not yet deployed to test) - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', s3KeyPrefix: 'biohub', tz: config.timezone.api, logLevel: 'info', @@ -137,9 +131,6 @@ const phases = { host: staticUrlsAPI.prod, appHost: staticUrls.prod, env: 'prod', - elasticsearchURL: 'http://es01:9200', - elasticsearchEmlIndex: 'eml', - elasticsearchTaxonomyIndex: 'taxonomy_3.0.0', s3KeyPrefix: 'biohub', tz: config.timezone.api, logLevel: 'info', diff --git a/api/.pipeline/templates/api.dc.yaml b/api/.pipeline/templates/api.dc.yaml index 6951951bc..bf84527dc 100644 --- a/api/.pipeline/templates/api.dc.yaml +++ b/api/.pipeline/templates/api.dc.yaml @@ -37,19 +37,6 @@ parameters: required: true value: 'dev' - name: NODE_OPTIONS - # Elastic Search - - name: ELASTICSEARCH_URL - description: Platform Elasticsearch URL - required: true - value: 'http://es01:9200' - - name: ELASTICSEARCH_EML_INDEX - description: Platform Elasticsearch index for EML data - required: true - value: 'eml' - - name: ELASTICSEARCH_TAXONOMY_INDEX - description: Platform Elasticsearch Taxonomy Index - required: true - vale: 'taxonomy_3.0.0' - name: TZ description: Application timezone required: false @@ -262,12 +249,6 @@ objects: value: ${NODE_ENV} - name: NODE_OPTIONS value: ${NODE_OPTIONS} - - name: ELASTICSEARCH_URL - value: ${ELASTICSEARCH_URL} - - name: ELASTICSEARCH_EML_INDEX - value: ${ELASTICSEARCH_EML_INDEX} - - name: ELASTICSEARCH_TAXONOMY_INDEX - value: ${ELASTICSEARCH_TAXONOMY_INDEX} # ITIS SOLR - name: ITIS_SOLR_URL value: ${ITIS_SOLR_URL} diff --git a/api/.pipeline/templates/queue.dc.yaml b/api/.pipeline/templates/queue.dc.yaml index efbb510ad..e2a091a9b 100644 --- a/api/.pipeline/templates/queue.dc.yaml +++ b/api/.pipeline/templates/queue.dc.yaml @@ -30,19 +30,6 @@ parameters: description: Application Environment type variable required: true value: 'dev' - # Elastic Search - - name: ELASTICSEARCH_URL - description: Platform Elasticsearch URL - required: true - value: 'http://es01:9200' - - name: ELASTICSEARCH_EML_INDEX - description: Platform Elasticsearch index for EML data - required: true - value: 'eml' - - name: ELASTICSEARCH_TAXONOMY_INDEX - description: Platform Elasticsearch Taxonomy Index - required: true - vale: 'taxonomy_3.0.0' - name: TZ description: Application timezone required: false @@ -149,12 +136,6 @@ objects: value: ${CHANGE_ID} - name: NODE_ENV value: ${NODE_ENV} - - name: ELASTICSEARCH_URL - value: ${ELASTICSEARCH_URL} - - name: ELASTICSEARCH_EML_INDEX - value: ${ELASTICSEARCH_EML_INDEX} - - name: ELASTICSEARCH_TAXONOMY_INDEX - value: ${ELASTICSEARCH_TAXONOMY_INDEX} - name: S3_KEY_PREFIX value: ${S3_KEY_PREFIX} - name: TZ diff --git a/api/package-lock.json b/api/package-lock.json index 8f3989254..205530d18 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -4,20 +4,14 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "@babel/code-frame": { @@ -30,27 +24,27 @@ } }, "@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "dev": true }, "@babel/core": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", - "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.7", - "@babel/parser": "^7.23.6", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -59,60 +53,34 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "semver": { @@ -124,14 +92,14 @@ } }, "@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dev": true, "requires": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" } }, @@ -197,55 +165,55 @@ } }, "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dev": true, "requires": { - "@babel/types": "^7.22.15" + "@babel/types": "^7.24.0" } }, "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" } }, "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dev": true, "requires": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" } }, "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true }, "@babel/helper-validator-option": { @@ -255,25 +223,26 @@ "dev": true }, "@babel/helpers": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", - "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", "dev": true, "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" } }, "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "dependencies": { "ansi-styles": { @@ -320,160 +289,93 @@ } }, "@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "dev": true }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true } } }, "@babel/traverse": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", - "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", "dev": true, "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", "debug": "^4.3.1", "globals": "^11.1.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "ms": "2.1.2" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, "@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" } }, @@ -507,28 +409,6 @@ "kuler": "^2.0.0" } }, - "@elastic/elasticsearch": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.1.0.tgz", - "integrity": "sha512-IiZ6u77C7oYYbUkx/YFgEJk6ZtP+QDI97VaUWiYD14xIdn/w9WJtmx/Y1sN8ov0nZzrWbqScB2Z7Pb8oxo7vqw==", - "requires": { - "@elastic/transport": "^8.0.2", - "tslib": "^2.3.0" - } - }, - "@elastic/transport": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.4.0.tgz", - "integrity": "sha512-Yb3fDa7yGD0ca3uMbL64M3vM1cE5h5uHmBcTjkdB4VpCasRNKSd09iDpwqX8zX1tbBtxcaKYLceKthWvPeIxTw==", - "requires": { - "debug": "^4.3.4", - "hpagent": "^1.0.0", - "ms": "^2.1.3", - "secure-json-parse": "^2.4.0", - "tslib": "^2.4.0", - "undici": "^5.22.1" - } - }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -568,6 +448,15 @@ "concat-map": "0.0.1" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -589,6 +478,12 @@ "brace-expansion": "^1.1.7" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -597,11 +492,6 @@ } } }, - "@fastify/busboy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", - "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==" - }, "@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -623,6 +513,15 @@ "concat-map": "0.0.1" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -631,6 +530,12 @@ "requires": { "brace-expansion": "^1.1.7" } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, @@ -678,36 +583,6 @@ "strip-ansi": "^7.0.1" } }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, "strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -716,21 +591,6 @@ "ansi-regex": "^6.0.1" } }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - } - } - }, "wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -740,54 +600,6 @@ "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } - }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } - } } } }, @@ -820,26 +632,26 @@ "dev": true }, "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true }, "@jridgewell/sourcemap-codec": { @@ -849,9 +661,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", @@ -931,9 +743,9 @@ "dev": true }, "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "@tsconfig/node12": { @@ -970,6 +782,16 @@ "requires": { "@types/connect": "*", "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } + } } }, "@types/chai": { @@ -984,6 +806,16 @@ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "requires": { "@types/node": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } + } } }, "@types/expect": { @@ -1013,14 +845,24 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "requires": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } + } } }, "@types/express-unless": { @@ -1032,9 +874,9 @@ } }, "@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", "dev": true }, "@types/glob-stream": { @@ -1049,16 +891,22 @@ } }, "@types/gulp": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.11.tgz", - "integrity": "sha512-jy0nfcsjiGqO1prNsYMK/bHkTblIBgG04sL2nxPpnP9MyNicHp1SUblomjOla6JoW1qkR67HjFHqIibpPoShNQ==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.17.tgz", + "integrity": "sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==", "dev": true, "requires": { + "@types/node": "*", "@types/undertaker": ">=1.2.6", "@types/vinyl-fs": "*", "chokidar": "^3.3.1" } }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1082,15 +930,16 @@ } }, "@types/lodash": { - "version": "4.14.195", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", - "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", "dev": true }, "@types/mime": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.3.tgz", - "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==" + "integrity": "sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q==", + "dev": true }, "@types/mocha": { "version": "9.0.0", @@ -1099,9 +948,9 @@ "dev": true }, "@types/multer": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", - "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", + "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", "dev": true, "requires": { "@types/express": "*" @@ -1110,12 +959,13 @@ "@types/node": { "version": "14.14.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", - "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==" + "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==", + "dev": true }, "@types/object-inspect": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.1.tgz", - "integrity": "sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@types/object-inspect/-/object-inspect-1.8.4.tgz", + "integrity": "sha512-2yh72JxmDney1h7LQvkyO8p8FOmNMQXGs8HjuXS3SXvE/dLydLLjBqKCdHqcTUo66CQVHfn7yFR680bvi9jlVw==", "dev": true }, "@types/pg": { @@ -1136,9 +986,9 @@ "dev": true }, "@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" }, "@types/range-parser": { "version": "1.2.7", @@ -1158,31 +1008,50 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "@types/node": { + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } } } }, "@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "requires": { - "@types/mime": "*", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + }, + "dependencies": { + "@types/node": { + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } + } } }, "@types/sinon": { - "version": "10.0.15", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz", - "integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" } }, "@types/sinon-chai": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", - "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.12.tgz", + "integrity": "sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==", "dev": true, "requires": { "@types/chai": "*", @@ -1205,9 +1074,9 @@ } }, "@types/swagger-ui-express": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.3.tgz", - "integrity": "sha512-jqCjGU/tGEaqIplPy3WyQg+Nrp6y80DCFnDEAvVKWkJyv0VivSSDCChkppHRHAablvInZe6pijDFMnavtN0vqA==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/swagger-ui-express/-/swagger-ui-express-4.1.6.tgz", + "integrity": "sha512-UVSiGYXa5IzdJJG3hrc86e8KdZWLYxyEsVoUI4iPXc7CO4VZ3AfNP8d/8+hrDRIqz+HAaSMtZSqAsF3Nq2X/Dg==", "dev": true, "requires": { "@types/express": "*", @@ -1243,9 +1112,9 @@ "dev": true }, "@types/vinyl": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.11.tgz", - "integrity": "sha512-vPXzCLmRp74e9LsP8oltnWKTH+jBwt86WgRUb4Pc9Lf3pkMVGyvIo2gm9bODeGfCay2DBB/hAWDuvf07JcK4rw==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", "dev": true, "requires": { "@types/expect": "^1.20.4", @@ -1264,9 +1133,9 @@ } }, "@types/yamljs": { - "version": "0.2.31", - "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.31.tgz", - "integrity": "sha512-QcJ5ZczaXAqbVD3o8mw/mEBhRvO5UAdTtbvgwL/OgoWubvNBh6/MxLBAigtcgIFaq3shon9m3POIxQaLQt4fxQ==", + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@types/yamljs/-/yamljs-0.2.34.tgz", + "integrity": "sha512-gJvfRlv9ErxdOv7ux7UsJVePtX54NAvQyd8ncoiFqK8G5aeHIfQfGH2fbruvjAQ9657HwAaO54waS+Dsk2QTUQ==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -1285,6 +1154,15 @@ "tsutils": "^3.21.0" }, "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1294,10 +1172,16 @@ "yallist": "^4.0.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -1335,6 +1219,23 @@ "@typescript-eslint/types": "4.33.0", "@typescript-eslint/typescript-estree": "4.33.0", "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "@typescript-eslint/scope-manager": { @@ -1368,6 +1269,15 @@ "tsutils": "^3.21.0" }, "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1377,10 +1287,16 @@ "yallist": "^4.0.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -1453,9 +1369,9 @@ } }, "adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.12.tgz", + "integrity": "sha512-6TVU49mK6KZb4qG6xWaaM4C7sA/sgUMLy/JYMOzkcp3BvVLpW0fXDFQiIzAuxFCt/2+xD7fNIiPFAoLZPhVNLQ==" }, "aggregate-error": { "version": "3.1.0", @@ -1610,13 +1526,13 @@ "dev": true }, "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" } }, "array-each": { @@ -1703,17 +1619,18 @@ "dev": true }, "arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" } }, @@ -1788,9 +1705,12 @@ "dev": true }, "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "requires": { + "possible-typed-array-names": "^1.0.0" + } }, "avj": { "version": "0.0.0", @@ -1798,9 +1718,9 @@ "integrity": "sha512-uYMMuRd+Ux8xH8L1NnAMy+aTsV+UBgQbS3ckRi3ERZWwq5eyXC5D3lC+8w/ZEjHhNpgUUCu61zJktMS8wse+Mg==" }, "aws-sdk": { - "version": "2.1398.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1398.0.tgz", - "integrity": "sha512-jiHAhKPPKDHZdwsbY9FD/WfX9RfQ7+7eKvXyXr7BYf0JIShok4TWan/iLdNWCtU0BhXYl+bfG9W0pG4rwJ9Ivg==", + "version": "2.1611.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1611.0.tgz", + "integrity": "sha512-y3RRmE9opN6qoeeZVRLu85buqufvtdxf+/3YGzVxVQZ+SM0ew9fokVVQ43GeANn5e6vWDAP2raHQvl6fpfZXgw==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -1811,7 +1731,7 @@ "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", - "xml2js": "0.5.0" + "xml2js": "0.6.2" }, "dependencies": { "uuid": { @@ -1901,9 +1821,9 @@ } }, "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, "bluebird": { @@ -1928,19 +1848,6 @@ "type-is": "~1.6.18" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", @@ -1972,13 +1879,13 @@ "dev": true }, "browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" } @@ -2058,12 +1965,15 @@ } }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -2078,9 +1988,9 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caniuse-lite": { - "version": "1.0.30001576", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", - "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "version": "1.0.30001614", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", + "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", "dev": true }, "cfb": { @@ -2100,18 +2010,18 @@ } }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dev": true, "requires": { "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^4.1.2", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.5" + "type-detect": "^4.0.8" } }, "chalk": { @@ -2151,9 +2061,9 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -2531,24 +2441,57 @@ "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==" }, "d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "requires": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + } + }, + "data-view-buffer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" } }, "dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" }, "db-migrate": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/db-migrate/-/db-migrate-0.11.13.tgz", - "integrity": "sha512-OE/bbDo/mQvLmZrui/2jNAiAECJROSURCOU5xs6qKr3FvtUE2O6b0xBUI6WyAAKdili3LJQHFlpqugiYCGTSBA==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/db-migrate/-/db-migrate-0.11.14.tgz", + "integrity": "sha512-8e+/YsIlM3d69hj+cb6qG6WyubR8nwXfDf9gGLWl4AxHpI6mTomcqhRLNfPrs7Le7AZv2eEsgK8hkXDSQqfIvg==", "requires": { "balanced-match": "^1.0.0", "bluebird": "^3.1.1", @@ -2592,18 +2535,11 @@ "integrity": "sha512-65k86bVeHaMxb2L0Gw3y5V+CgZSRwhVQMwDMydmw5MvIpHHwD6SmBciqIwHsZfzJ9yzV/yYhdRefRM6FV5/siw==" }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "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" } }, "decamelize": { @@ -2678,14 +2614,13 @@ "dev": true }, "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" } }, "define-properties": { @@ -2872,9 +2807,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.4.628", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.628.tgz", - "integrity": "sha512-2k7t5PHvLsufpP6Zwk0nof62yLOsCf032wZx7/q0mv8gwlXjhcxI3lz6f0jBr0GrnWKcm3burXzI3t5IrcdUxw==", + "version": "1.4.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz", + "integrity": "sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q==", "dev": true }, "emoji-regex": { @@ -2929,140 +2864,98 @@ } }, "es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.15" }, "dependencies": { - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.11" - } - }, "object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true - }, - "which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } } } }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, "es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "requires": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "dependencies": { - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - } + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" } }, "es-to-primitive": { @@ -3077,13 +2970,14 @@ } }, "es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, "requires": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, @@ -3105,13 +2999,13 @@ } }, "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", "dev": true, "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "d": "^1.0.2", + "ext": "^1.7.0" } }, "es6-weak-map": { @@ -3127,9 +3021,9 @@ } }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" }, "escape-html": { "version": "1.0.3", @@ -3212,6 +3106,15 @@ "concat-map": "0.0.1" } }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -3259,10 +3162,16 @@ "brace-expansion": "^1.1.7" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -3330,6 +3239,18 @@ "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, + "esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "requires": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + } + }, "espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -3405,10 +3326,20 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "events": { "version": "1.1.1", @@ -3435,15 +3366,6 @@ "to-regex": "^3.0.1" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -3461,12 +3383,6 @@ "requires": { "is-extendable": "^0.1.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true } } }, @@ -3516,19 +3432,6 @@ "vary": "~1.1.2" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", @@ -3563,14 +3466,6 @@ "dev": true, "requires": { "type": "^2.7.2" - }, - "dependencies": { - "type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", - "dev": true - } } }, "extend": { @@ -3713,9 +3608,9 @@ } }, "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "requires": { "reusify": "^1.0.4" } @@ -3764,21 +3659,6 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } } }, "find-cache-dir": { @@ -3955,9 +3835,9 @@ } }, "flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "flush-write-stream": { @@ -4008,9 +3888,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-each": { "version": "0.3.3", @@ -4103,9 +3983,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.6", @@ -4149,14 +4029,15 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-package-type": { @@ -4171,13 +4052,14 @@ "dev": true }, "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" } }, "get-value": { @@ -4192,15 +4074,15 @@ "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==" }, "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", + "jackspeak": "^2.3.6", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" } }, "glob-parent": { @@ -4607,12 +4489,13 @@ } }, "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "requires": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" } }, "globby": { @@ -4897,14 +4780,6 @@ "glogg": "^1.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -4915,41 +4790,20 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.2" - }, - "dependencies": { - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - } + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" } }, "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" }, "has-symbols": { "version": "1.0.3", @@ -4957,11 +4811,11 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, "has-value": { @@ -5035,18 +4889,11 @@ } }, "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { "function-bind": "^1.1.2" - }, - "dependencies": { - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - } } }, "he": { @@ -5070,11 +4917,6 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "hpagent": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz", - "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==" - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -5107,9 +4949,9 @@ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "ignore-by-default": { @@ -5174,34 +5016,14 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "requires": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" - }, - "dependencies": { - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - } } }, "interpret": { @@ -5249,14 +5071,13 @@ } }, "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" } }, "is-arrayish": { @@ -5320,6 +5141,15 @@ "hasown": "^2.0.0" } }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "requires": { + "is-typed-array": "^1.1.13" + } + }, "is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -5385,9 +5215,9 @@ "dev": true }, "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, "is-number": { @@ -5452,12 +5282,12 @@ } }, "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" } }, "is-stream": { @@ -5484,15 +5314,11 @@ } }, "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.14" } }, "is-typedarray": { @@ -5654,9 +5480,9 @@ } }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5690,6 +5516,21 @@ "source-map": "^0.6.1" }, "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5699,9 +5540,9 @@ } }, "istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -5723,9 +5564,9 @@ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" }, "jose": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.6.tgz", - "integrity": "sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.7.tgz", + "integrity": "sha512-5hFWIigKqC+e/lRyQhfnirrAqUdIPMB7SJRqflJaO29dW7q5DFvH1XCSTmv6PQ6pb++0k6MJlLRoS0Wv4s38Wg==", "requires": { "@panva/asn1.js": "^1.0.0" } @@ -5781,9 +5622,9 @@ "dev": true }, "jsonpath-plus": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-8.0.0.tgz", - "integrity": "sha512-+AOBHcQvRr8DcWVIkfOCCCLSlYgQuNZ+gFNqwkBrNpdUfdfkcrbO4ml3F587fWUMFOmoy6D9c+5wrghgjN3mbg==" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-8.1.0.tgz", + "integrity": "sha512-qVTiuKztFGw0dGhYi3WNqvddx3/SHtyDT0xJaeyz4uP0d1tkpG+0y5uYQ4OcIo1TLAz3PE/qDOW9F0uDt3+CTw==" }, "jsonwebtoken": { "version": "8.5.1", @@ -5800,6 +5641,13 @@ "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } } }, "just-debounce": { @@ -5834,6 +5682,21 @@ "jose": "^2.0.5", "limiter": "^1.1.5", "lru-memoizer": "^2.1.4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "jws": { @@ -5881,6 +5744,19 @@ "tildify": "2.0.0" }, "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "pg-connection-string": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", @@ -6124,6 +6000,11 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -6137,9 +6018,9 @@ } }, "lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==" + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==" }, "lru-memoizer": { "version": "2.2.0", @@ -6402,9 +6283,9 @@ } }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "requires": { "brace-expansion": "^2.0.1" } @@ -6604,6 +6485,12 @@ "brace-expansion": "^1.1.7" } }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6692,9 +6579,9 @@ "integrity": "sha512-s6BdnqNoEYfViPJgkH85X5Nw5NpzxN8hoflKLweNa7vBxt2V7kaS06d74pAtqDxde8fn4r9h4dNdLiFGoNV0KA==" }, "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "multer": { "version": "1.4.4", @@ -6723,9 +6610,9 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "nan": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", - "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", "optional": true }, "nanoid": { @@ -6777,9 +6664,9 @@ "dev": true }, "nise": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.7.tgz", - "integrity": "sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.9.tgz", + "integrity": "sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.0", @@ -6790,9 +6677,9 @@ }, "dependencies": { "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -6808,9 +6695,9 @@ } }, "path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true } } @@ -6880,6 +6767,12 @@ "requires": { "brace-expansion": "^1.1.7" } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true } } }, @@ -7262,25 +7155,6 @@ "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" - }, - "dependencies": { - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - } } }, "object.defaults": { @@ -7487,17 +7361,17 @@ "integrity": "sha512-/Yvsd2D7miYB4HLJ3hOOS0+vnowQpaT75FsHzr/y5M9P4q9bwa7RcbW2YdH6KZBn8ceLbKGnHxMZ1CHliGHUFw==" }, "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" } }, "ordered-read-streams": { @@ -7576,11 +7450,11 @@ } }, "p-queue": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.3.4.tgz", - "integrity": "sha512-esox8CWt0j9EZECFvkFl2WNPat8LN4t7WWeXq73D9ha0V96qPRufApZi4ZhPwXAln1uVVal429HVVKPa2X0yQg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-7.4.1.tgz", + "integrity": "sha512-vRpMXmIkYF2/1hLBKisKeVYJZ8S2tZ0zEAmIJgdVKP2nq0nh4qCdf8bgw+ZgKrkh71AOCaqzwbJJk1WtdcF3VA==", "requires": { - "eventemitter3": "^4.0.7", + "eventemitter3": "^5.0.1", "p-timeout": "^5.0.2" } }, @@ -7714,11 +7588,11 @@ "dev": true }, "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, @@ -7754,9 +7628,9 @@ } }, "pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" }, "pg-int8": { "version": "1.0.1", @@ -7764,14 +7638,14 @@ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, "pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==" }, "pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "pg-types": { "version": "2.2.0", @@ -7870,6 +7744,11 @@ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", "dev": true }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" + }, "postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -8198,14 +8077,15 @@ } }, "regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" } }, "regexpp": { @@ -8295,11 +8175,11 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -8404,13 +8284,13 @@ } }, "safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -8438,45 +8318,14 @@ } }, "safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "requires": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" - }, - "dependencies": { - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - } } }, "safe-stable-stringify": { @@ -8494,15 +8343,10 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, - "secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" }, "semver-greatest-satisfied-range": { "version": "1.1.0", @@ -8533,25 +8377,15 @@ "statuses": "~1.5.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -8581,26 +8415,28 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" } }, "set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "requires": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" } }, "set-value": { @@ -8651,13 +8487,21 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "dependencies": { + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + } } }, "signal-exit": { @@ -8760,15 +8604,6 @@ "use": "^3.1.0" }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -8786,12 +8621,6 @@ "requires": { "is-extendable": "^0.1.0" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true } } }, @@ -8939,9 +8768,9 @@ } }, "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "spdx-expression-parse": { @@ -8955,9 +8784,9 @@ } }, "spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "split-string": { @@ -9041,9 +8870,9 @@ "dev": true }, "stream-shift": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.2.tgz", - "integrity": "sha512-rV4Bovi9xx0BFzOb/X0B2GqoIjvqPCttZdu0Wgtx2Dxkj7ETyWl9gmqJ4EutWRLvtZWm8dxE+InQZX1IryZn/w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", "dev": true }, "streamsearch": { @@ -9061,48 +8890,60 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.padend": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", - "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string_decoder": { @@ -9118,6 +8959,14 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -9162,9 +9011,9 @@ } }, "swagger-ui-dist": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.0.tgz", - "integrity": "sha512-j0PIATqQSEFGOLmiJOJZj1X1Jt6bFIur3JpY7+ghliUnfZs0fpWDdHEkn9q7QUlBtKbkn6TepvSxTqnE8l3s0A==" + "version": "5.17.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.2.tgz", + "integrity": "sha512-V/NqUw6QoTrjSpctp2oLQvxrl3vW29UsUtZyq7B1CF0v870KOFbYGDQw8rpKaKm0JxTwHpWnW1SN9YuKZdiCyw==" }, "swagger-ui-express": { "version": "4.3.0", @@ -9175,9 +9024,9 @@ } }, "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -9509,9 +9358,10 @@ } }, "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "tsutils": { "version": "3.21.0", @@ -9520,14 +9370,6 @@ "dev": true, "requires": { "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } } }, "tunnel-ssh": { @@ -9538,21 +9380,6 @@ "debug": "2.6.9", "lodash.defaults": "^4.1.0", "ssh2": "1.4.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } } }, "tweetnacl": { @@ -9561,9 +9388,9 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, "type-check": { @@ -9597,50 +9424,55 @@ } }, "typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" } }, "typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" } }, "typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" } }, "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" } }, "typedarray": { @@ -9718,13 +9550,10 @@ "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", "dev": true }, - "undici": { - "version": "5.28.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz", - "integrity": "sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==", - "requires": { - "@fastify/busboy": "^2.0.0" - } + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "union-value": { "version": "1.0.1", @@ -9800,12 +9629,12 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.14.tgz", + "integrity": "sha512-JixKH8GR2pWYshIPUg/NujK3JO7JiqEEUiNArE86NQyrgUuZeTlZQN3xuS/yiV5Kb48ev9K6RqNkaJjXsdg7Jw==", "dev": true, "requires": { - "escalade": "^3.1.1", + "escalade": "^3.1.2", "picocolors": "^1.0.0" } }, @@ -10037,16 +9866,15 @@ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.2" } }, "wide-align": { @@ -10130,9 +9958,9 @@ } }, "winston-transport": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.6.0.tgz", - "integrity": "sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", "requires": { "logform": "^2.3.2", "readable-stream": "^3.6.0", @@ -10169,6 +9997,12 @@ "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, "workerpool": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", @@ -10185,6 +10019,16 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -10226,9 +10070,9 @@ } }, "xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "requires": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -10320,9 +10164,9 @@ "dev": true }, "zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.5.tgz", + "integrity": "sha512-fkwiq0VIQTksNNA131rDOsVJcns0pfVUjHzLrNBiF/O/Xxb5lQyEXkhZWcJ7npWsYlvs+h0jFWXXy4X46Em1JA==" } } } diff --git a/api/package.json b/api/package.json index 7d84b835b..2cf1b8b4c 100644 --- a/api/package.json +++ b/api/package.json @@ -28,7 +28,6 @@ "npm": ">= 6.0.0" }, "dependencies": { - "@elastic/elasticsearch": "~8.1.0", "adm-zip": "~0.5.5", "ajv": "~8.6.3", "avj": "0.0.0", diff --git a/api/src/paths/dwc/spatial/download.test.ts b/api/src/paths/dwc/spatial/download.test.ts deleted file mode 100644 index e109d66fd..000000000 --- a/api/src/paths/dwc/spatial/download.test.ts +++ /dev/null @@ -1,378 +0,0 @@ -import AdmZip from 'adm-zip'; -import chai, { expect } from 'chai'; -import { Feature } from 'geojson'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { ISubmissionSpatialSearchResponseRow } from '../../../repositories/spatial-repository'; -import { SpatialService } from '../../../services/spatial-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as download from './download'; -import { GET } from './download'; - -chai.use(sinonChai); - -describe('download', () => { - describe('openApiScheme', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - describe('should throw an error when', () => { - describe('boundry', () => { - it('is null', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: null, - datasetID: [] - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('boundary'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('is not a array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: 123, - datasetID: [] - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('boundary'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - - describe('type', () => { - it('is not an array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [], - datasetID: [], - type: 'not an array' - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('type'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - - describe('datasetID', () => { - it('is undefined', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [] - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('datasetID'); - expect(response.errors[0].message).to.equal("must have required property 'datasetID'"); - }); - - it('is null', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [], - datasetID: null - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('datasetID'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('is not an array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [], - type: [], - datasetID: 'not an array' - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('datasetID'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - describe('should throw an error when', () => { - it('returns a null response', async () => { - const apiResponse = null; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('response'); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('returns invalide response', () => { - const apiResponse = [{}]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be string'); - }); - }); - - describe('should succeed when', () => { - it('returns a response that can be turned back into object', async () => { - const mockData: ISubmissionSpatialSearchResponseRow[] = [ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Boundary' }, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - } - } - }, - { - taxa_data: [{ submission_spatial_component_id: 2 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { - type: 'Occurrence' - }, - geometry: { type: 'Point', coordinates: [] } - } - ] - } - } - } - ]; - // convert object into valid buffer - const zip = new AdmZip(); - zip.addFile('results.json', Buffer.from(JSON.stringify(mockData)), 'Search Results.'); - - const fileString = zip.toBuffer().toString('hex'); - const response = responseValidator.validateResponse(200, fileString); - - // Convert response back into a file - const fileData = Buffer.from(fileString, 'hex'); - const responseZip = new AdmZip(fileData); - const zipEntries = responseZip.getEntries(); - zipEntries.forEach((item) => { - expect(JSON.parse(item.getData().toString())).to.eql(mockData); - }); - - expect(response).to.equal(undefined); - }); - }); - }); - }); - - describe('downloadSpatialComponents', () => { - afterEach(() => { - sinon.restore(); - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialService.prototype, 'findSpatialComponentsByCriteria') - .throws(new Error('test error')); - - const requestHandler = download.downloadSpatialComponents(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as Error).message).to.equal('test error'); - expect(dbConnectionObj.rollback).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - expect(findSpatialComponentsByCriteriaStub).to.be.calledWith({ - type: [], - datasetID: [], - boundary: [] - }); - } - }); - - it('uses getDBConnection', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - const getDBConnectionStub = sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - mockReq['keycloak_token'] = 'token'; - - sinon.stub(SpatialService.prototype, 'findSpatialComponentsByCriteria').resolves([]); - - const requestHandler = download.downloadSpatialComponents(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - - expect(getDBConnectionStub).to.be.calledWith('token'); - }); - - it('uses getAPIUserDBConnection', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - const getAPIUserDBConnectionStub = sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - sinon.stub(SpatialService.prototype, 'findSpatialComponentsByCriteria').resolves([]); - - const requestHandler = download.downloadSpatialComponents(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - - expect(getAPIUserDBConnectionStub).to.be.calledOnce; - }); - - it('returns 200', async () => { - const datasetID = '123-456-789'; - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const boundaryFeature: Feature = { - type: 'Feature', - properties: {}, - geometry: { type: 'Polygon', coordinates: [[]] } - }; - - mockReq.query = { - type: ['type'], - boundary: [JSON.stringify(boundaryFeature)], - datasetID: [datasetID] - }; - - const mockData: ISubmissionSpatialSearchResponseRow[] = [ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Boundary' }, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - } - } - }, - { - taxa_data: [{ submission_spatial_component_id: 2 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { - type: 'Occurrence' - }, - geometry: { type: 'Point', coordinates: [] } - } - ] - } - } - } - ]; - - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialService.prototype, 'findSpatialComponentsByCriteria') - .resolves(mockData); - - const requestHandler = download.downloadSpatialComponents(); - await requestHandler(mockReq, mockRes, mockNext); - expect(mockRes.statusValue).to.equal(200); - expect(mockRes).to.not.be.null; - - // Convert response back into a file - const fileData = Buffer.from(mockRes.sendValue, 'hex'); - const zip = new AdmZip(fileData); - const zipEntries = zip.getEntries(); - zipEntries.forEach((item) => { - expect(JSON.parse(item.getData().toString())).to.eql(mockData); - }); - expect(dbConnectionObj.commit).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - expect(findSpatialComponentsByCriteriaStub).to.be.calledWith({ - type: ['type'], - datasetID: [datasetID], - boundary: [ - { - type: 'Feature', - properties: {}, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - }); - }); - }); -}); diff --git a/api/src/paths/dwc/spatial/download.ts b/api/src/paths/dwc/spatial/download.ts deleted file mode 100644 index f5c7c96e1..000000000 --- a/api/src/paths/dwc/spatial/download.ts +++ /dev/null @@ -1,116 +0,0 @@ -import AdmZip from 'adm-zip'; -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { Feature } from 'geojson'; -import { getAPIUserDBConnection, getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { SpatialService } from '../../../services/spatial-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/spatial/download'); - -export const GET: Operation = [downloadSpatialComponents()]; - -GET.apiDoc = { - description: 'Archived search results for spatial components.', - tags: ['spatial', 'search', 'download'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - in: 'query', - name: 'boundary', - schema: { - type: 'array', - items: { - type: 'string' - } - }, - description: 'A stringified GeoJSON Feature. Will return results that intersect the feature.' - }, - { - in: 'query', - name: 'type', - schema: { - type: 'array', - items: { - type: 'string' - }, - nullable: true - }, - description: 'An array of spatial component types to filter on. Will return results that match any of the types.' - }, - { - in: 'query', - name: 'datasetID', - required: true, - schema: { - type: 'array', - items: { - type: 'string' - } - }, - description: 'An array of dataset IDs. Will return results that belong to any of the dataset IDs.' - } - ], - responses: { - 200: { - description: 'Archived spatial components response object.', - content: { - 'application/zip': { - schema: { - type: 'string', - description: 'Buffer of the archived search results.' - } - } - } - }, - ...defaultErrorResponses - } -}; - -export function downloadSpatialComponents(): RequestHandler { - return async (req, res) => { - const boundaries: Feature[] = []; - if (req.query.boundary?.length) { - const boundariesArray: string[] = req.query.boundary as string[]; - boundariesArray.forEach((boundary) => { - boundaries.push(JSON.parse(boundary)); - }); - } - - const criteria = { - type: (req.query.type as string[]) || [], - datasetID: (req.query.datasetID as string[]) || [], - boundary: boundaries - }; - - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - try { - await connection.open(); - const spatialService = new SpatialService(connection); - const response = await spatialService.findSpatialComponentsByCriteria(criteria); - - const zip = new AdmZip(); - const fileName = `results.json`; - - zip.addFile(fileName, Buffer.from(JSON.stringify(response)), 'Search results.'); - const zipToSend = await zip.toBuffer(); - - await connection.commit(); - - // encoding zip as hex to avoid corrupted file in response - res.status(200).send(zipToSend.toString('hex')); - } catch (error) { - defaultLog.error({ label: 'downloadSpatialComponents', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/spatial/metadata.test.ts b/api/src/paths/dwc/spatial/metadata.test.ts deleted file mode 100644 index ea8a72854..000000000 --- a/api/src/paths/dwc/spatial/metadata.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { SpatialService } from '../../../services/spatial-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as metadata from './metadata'; -import { GET } from './metadata'; - -chai.use(sinonChai); - -describe('metadata', () => { - describe('openApiSchema', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - describe('should throw an error when', () => { - describe('submissionSpatialComponentIds', () => { - it('is undefined', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: {} - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('submissionSpatialComponentIds'); - expect(response.errors[0].message).to.equal("must have required property 'submissionSpatialComponentIds'"); - }); - - it('is null', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - submissionSpatialComponentIds: null - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('submissionSpatialComponentIds'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('is zero', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - submissionSpatialComponentIds: [0] - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('submissionSpatialComponentIds.0'); - expect(response.errors[0].message).to.equal('must be >= 1'); - }); - }); - }); - - describe('should succeed when', () => { - it('all values are provided and valid', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - submissionSpatialComponentIds: [1] - } - }; - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - - describe('should throw an error when', () => { - it('returns a null response', async () => { - const apiResponse = null; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('response'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('returns invalid response (not an array)', async () => { - // array of `Feature` rather than `FeatureCollection` - const apiResponse = 'not an array'; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('response'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('returns invalid response (not an object)', async () => { - // array of `Feature` rather than `FeatureCollection` - const apiResponse = ['not an object']; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('0'); - expect(response.errors[0].message).to.equal('must be object'); - }); - }); - - describe('should succeed when', () => { - it('required values are valid (empty)', async () => { - const apiResponse: Record[] = []; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - - it('required values are valid', async () => { - const apiResponse = [{ prop1: 'val1' }]; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - }); - }); - }); - - describe('getSpatialMetadataBySubmissionSpatialComponentIds', () => { - afterEach(() => { - sinon.restore(); - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { submissionSpatialComponentIds: ['1'] }; - - sinon - .stub(SpatialService.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds') - .throws(new Error('test error')); - - const requestHandler = metadata.getSpatialMetadataBySubmissionSpatialComponentIds(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as Error).message).to.equal('test error'); - expect(dbConnectionObj.rollback).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - } - }); - - it('returns 200', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = { submissionSpatialComponentIds: ['1'] }; - - const mockResponse = [{ prop1: 'val1' }]; - - sinon.stub(SpatialService.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds').resolves(mockResponse); - - const requestHandler = metadata.getSpatialMetadataBySubmissionSpatialComponentIds(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql(mockResponse); - expect(dbConnectionObj.commit).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - }); - }); -}); diff --git a/api/src/paths/dwc/spatial/metadata.ts b/api/src/paths/dwc/spatial/metadata.ts deleted file mode 100644 index a54ecb3e7..000000000 --- a/api/src/paths/dwc/spatial/metadata.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { SpatialService } from '../../../services/spatial-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/spatial/metadata'); - -export const GET: Operation = [getSpatialMetadataBySubmissionSpatialComponentIds()]; - -GET.apiDoc = { - description: 'Retrieves spatial component metadata based on submission spatial component id', - tags: ['spatial'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'spatial component submission ids', - in: 'query', - name: 'submissionSpatialComponentIds', - schema: { - type: 'array', - items: { - type: 'number', - minimum: 1 - } - }, - required: true - } - ], - responses: { - 200: { - description: 'Spatial metadata response object.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object' - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Retrieves dataset metadata from Elastic Search. - * - * @returns {RequestHandler} - */ -export function getSpatialMetadataBySubmissionSpatialComponentIds(): RequestHandler { - return async (req, res) => { - const submissionSpatialComponentIds = ((req.query.submissionSpatialComponentIds || []) as string[]).map(Number); - - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - try { - await connection.open(); - - const spatialService = new SpatialService(connection); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds( - submissionSpatialComponentIds - ); - - await connection.commit(); - - res.status(200).json(response); - } catch (error) { - defaultLog.error({ label: 'getSpatialMetadataBySubmissionSpatialComponentIds', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/spatial/search.test.ts b/api/src/paths/dwc/spatial/search.test.ts deleted file mode 100644 index b08e85209..000000000 --- a/api/src/paths/dwc/spatial/search.test.ts +++ /dev/null @@ -1,475 +0,0 @@ -import chai, { expect } from 'chai'; -import { Feature } from 'geojson'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { ISubmissionSpatialSearchResponseRow } from '../../../repositories/spatial-repository'; -import { SpatialService } from '../../../services/spatial-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import * as search from './search'; -import { GET } from './search'; - -chai.use(sinonChai); - -describe('search', () => { - describe('openApiSchema', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - describe('should throw an error when', () => { - describe('boundary', () => { - it('is null', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: null - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('boundary'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('is not an array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: 123 - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('boundary'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - - describe('type', () => { - it('is not an array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [], - type: 'not an array' - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('type'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - - describe('datasetID', () => { - it('is not an array', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: [], - type: [], - datasetID: 'not an array' - } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors[0].path).to.equal('datasetID'); - expect(response.errors[0].message).to.equal('must be array'); - }); - }); - }); - - describe('should succeed when', () => { - it('all values are provided and valid', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: ['not null'], - type: ['type'], - datasetID: ['id'] - } - }; - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - - it('required values are provided and valid', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: ['not null'] - } - }; - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - - it('optional values are provided and valid', async () => { - const request = { - headers: { - 'content-type': 'application/json' - }, - query: { - boundary: ['not null'], - type: null, - datasetID: null - } - }; - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - - describe('should throw an error when', () => { - it('returns a null response', async () => { - const apiResponse = null; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('response'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('returns invalid response (not an array of FeatureCollection)', async () => { - // array of `Feature` rather than `FeatureCollection` - const apiResponse = [ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_data: { - type: 'Feature', - properties: {}, - geometry: { type: 'Point', coordinates: [-123.43791961669922, 48.63449682909913] } - } - } - ]; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('0/spatial_data'); - expect(response.errors[0].message).to.equal("must have required property 'features'"); - expect(response.errors[1].path).to.equal('0/spatial_data/type'); - expect(response.errors[1].message).to.equal('must be equal to one of the allowed values'); - }); - - it('returns invalid response (missing taxa_data)', async () => { - const apiResponse = [ - { - spatial_data: { - type: 'FeatureCollection', - properties: {}, - geometry: { type: 'Point', coordinates: [-123.43791961669922, 48.63449682909913] } - } - } - ]; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('0'); - expect(response.errors[0].message).to.equal("must have required property 'taxa_data'"); - }); - - it('returns invalid response (missing submission_spatial_component_id)', async () => { - const apiResponse = [ - { - taxa_data: [{ taxon_id: 'M-ALAM' }], - spatial_data: { - type: 'FeatureCollection', - properties: {}, - geometry: { type: 'Point', coordinates: [-123.43791961669922, 48.63449682909913] } - } - } - ]; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].path).to.equal('0/taxa_data/0'); - expect(response.errors[0].message).to.equal("must have required property 'submission_spatial_component_id'"); - }); - }); - - describe('should succeed when', () => { - it('required values are valid (empty)', async () => { - const apiResponse: any[] = []; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - - it('required values are valid', async () => { - const apiResponse = [ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-123.43785926699638, 48.634569504210184], - [-123.43786899000405, 48.634561749249066], - [-123.43785356730223, 48.63456285710074], - [-123.43785926699638, 48.634569504210184] - ] - ] - } - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-123.43785926699638, 48.634569504210184], - [-123.43786899000405, 48.634561749249066], - [-123.43785356730223, 48.63456285710074], - [-123.43785926699638, 48.634569504210184] - ] - ] - } - } - ] - } - }, - { - taxa_data: [{ submission_spatial_component_id: 2 }], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: {}, - geometry: { type: 'Point', coordinates: [-123.43791961669922, 48.63449682909913] } - } - ] - } - } - ]; - - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - }); - }); - }); - - describe('searchSpatialComponents', () => { - afterEach(() => { - sinon.restore(); - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection({ rollback: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialService.prototype, 'findSpatialComponentsByCriteria') - .throws(new Error('test error')); - - const requestHandler = search.searchSpatialComponents(); - - try { - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as Error).message).to.equal('test error'); - expect(dbConnectionObj.rollback).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - - expect(findSpatialComponentsByCriteriaStub).to.be.calledWith({ - type: [], - species: [], - datasetID: [], - boundary: [] - }); - } - }); - - it('uses getDBConnection', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - const getDBConnectionStub = sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - mockReq['keycloak_token'] = 'token'; - - sinon.stub(SpatialService.prototype, 'findSpatialComponentsByCriteria').resolves([]); - - const requestHandler = search.searchSpatialComponents(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - - expect(getDBConnectionStub).to.be.calledWith('token'); - }); - - it('uses getAPIUserDBConnection', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - const getAPIUserDBConnectionStub = sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.query = {}; - - sinon.stub(SpatialService.prototype, 'findSpatialComponentsByCriteria').resolves([]); - - const requestHandler = search.searchSpatialComponents(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - - expect(getAPIUserDBConnectionStub).to.be.calledOnce; - }); - - it('returns 200', async () => { - const dbConnectionObj = getMockDBConnection({ commit: sinon.stub(), release: sinon.stub() }); - sinon.stub(db, 'getAPIUserDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - const boundaryFeature: Feature = { - type: 'Feature', - properties: {}, - geometry: { type: 'Polygon', coordinates: [[]] } - }; - - mockReq.query = { - type: ['type'], - boundary: [JSON.stringify(boundaryFeature)] - }; - - const mockResponse: ISubmissionSpatialSearchResponseRow[] = [ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Boundary' }, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - } - } - }, - { - taxa_data: [{ submission_spatial_component_id: 2 }], - spatial_component: { - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Occurrence' }, - geometry: { type: 'Point', coordinates: [] } - } - ] - } - } - } - ]; - - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialService.prototype, 'findSpatialComponentsByCriteria') - .resolves(mockResponse); - - const requestHandler = search.searchSpatialComponents(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - expect(mockRes.jsonValue).to.eql([ - { - taxa_data: [{ submission_spatial_component_id: 1 }], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Boundary' }, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - } - }, - { - taxa_data: [{ submission_spatial_component_id: 2 }], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: { type: 'Occurrence' }, - geometry: { type: 'Point', coordinates: [] } - } - ] - } - } - ]); - expect(dbConnectionObj.commit).to.have.been.calledOnce; - expect(dbConnectionObj.release).to.have.been.calledOnce; - expect(findSpatialComponentsByCriteriaStub).to.be.calledWith({ - type: ['type'], - species: [], - datasetID: [], - boundary: [ - { - type: 'Feature', - properties: {}, - geometry: { type: 'Polygon', coordinates: [[]] } - } - ] - }); - }); - }); -}); diff --git a/api/src/paths/dwc/spatial/search.ts b/api/src/paths/dwc/spatial/search.ts deleted file mode 100644 index 261db55d8..000000000 --- a/api/src/paths/dwc/spatial/search.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { Feature } from 'geojson'; -import { getAPIUserDBConnection, getDBConnection } from '../../../database/db'; -import { GeoJSONFeatureCollection } from '../../../openapi/schemas/geoJson'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { SpatialService } from '../../../services/spatial-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/spatial/search'); - -export const GET: Operation = [searchSpatialComponents()]; - -GET.apiDoc = { - description: 'Searches for spatial components.', - tags: ['spatial', 'search'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - in: 'query', - name: 'boundary', - schema: { - type: 'array', - items: { - type: 'string' - } - }, - description: 'A stringified GeoJSON Feature. Will return results that intersect the feature.' - }, - { - in: 'query', - name: 'type', - schema: { - type: 'array', - items: { - type: 'string' - }, - nullable: true - }, - description: 'An array of spatial component types to filter on. Will return results that match any of the types.' - }, - { - in: 'query', - name: 'species', - schema: { - type: 'array', - items: { - type: 'string' - }, - nullable: true - }, - description: 'An array of species codes to filter on. Will return results that match any of the types.' - }, - { - in: 'query', - name: 'datasetID', - schema: { - type: 'array', - items: { - type: 'string' - }, - nullable: true - }, - description: 'An array of dataset IDs. Will return results that belong to any of the dataset IDs.' - } - ], - responses: { - 200: { - description: 'Spatial components response object.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - required: ['taxa_data', 'spatial_data'], - properties: { - taxa_data: { - type: 'array', - items: { - type: 'object', - required: ['submission_spatial_component_id'], - properties: { - submission_spatial_component_id: { - type: 'number', - minimum: 1 - }, - taxon_id: { - type: 'string', - nullable: true - }, - vernacular_name: { - type: 'string', - nullable: true - } - } - } - }, - spatial_data: { - oneOf: [ - { - ...GeoJSONFeatureCollection - }, - { - type: 'object', - properties: {}, - additionalProperties: false, - minProperties: 0, - maxProperties: 0, - description: - 'An empty object, representing a spatial component the requester does not have sufficient privileges to view.' - } - ] - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -export function searchSpatialComponents(): RequestHandler { - return async (req, res) => { - const boundaries: Feature[] = []; - if (req.query.boundary?.length) { - const boundariesArray: string[] = req.query.boundary as string[]; - boundariesArray.forEach((boundary) => { - boundaries.push(JSON.parse(boundary)); - }); - } - - const criteria = { - type: (req.query.type as string[]) || [], - species: (req.query.species as string[]) || [], - datasetID: (req.query.datasetID as string[]) || [], - boundary: boundaries - }; - - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - try { - await connection.open(); - - const spatialService = new SpatialService(connection); - - const response = await spatialService.findSpatialComponentsByCriteria(criteria); - - const structuredResponse = response.map((row) => { - const { spatial_component, taxa_data } = row; - const { spatial_data, ...rest } = spatial_component; - return { - taxa_data, - ...rest, - spatial_data: { - ...spatial_data, - features: spatial_data.features.map((feature) => { - delete feature?.properties?.dwc; - return feature; - }) - } - }; - }); - - await connection.commit(); - - res.status(200).json(structuredResponse); - } catch (error) { - defaultLog.error({ label: 'searchSpatialComponents', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/list.test.ts b/api/src/paths/dwc/submission/list.test.ts deleted file mode 100644 index c01c4e645..000000000 --- a/api/src/paths/dwc/submission/list.test.ts +++ /dev/null @@ -1,326 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../database/db'; -import { ApiGeneralError } from '../../../errors/api-error'; -import { SubmissionService } from '../../../services/submission-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db'; -import { GET, listDataset } from './list'; - -chai.use(sinonChai); - -describe('submissions', () => { - describe('openApiSchema', () => { - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - - describe('should throw an error when', () => { - describe('required return properties is missing', () => { - it('property submission_id', async () => { - const apiResponse = [{}]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'submission_id'"); - }); - - it('property uuid', async () => { - const apiResponse = [{ submission_id: 1, source: 'SIMS' }]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'uuid'"); - }); - - it('property event_timestamp', async () => { - const apiResponse = [{ submission_id: 1, source: 'SIMS', uuid: '2267501d-c6a9-43b5-b951-2324faff6397' }]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'event_timestamp'"); - }); - - it('property create_date', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z' - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'create_date'"); - }); - - it('property create_user', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z' - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'create_user'"); - }); - - it('property revision_count', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal("must have required property 'revision_count'"); - }); - }); - - describe('return properties are invalid types', () => { - it('null value', async () => { - const apiResponse = null; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be array'); - }); - - it('is not an object', async () => { - const apiResponse = ['']; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be object'); - }); - - it('property submission_id', async () => { - const apiResponse = [ - { - submission_id: '', - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be integer'); - }); - - it('property source', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 1, - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('property uuid', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: 1, - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('property event_timestamp', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: 1, - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be object'); - }); - - it('property create_date', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: 1, - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be object'); - }); - - it('property create_user', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: '', - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be integer'); - }); - - it('property revision_count', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: '' - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors[0].message).to.equal('must be integer'); - }); - }); - }); - - describe('should succeed when', () => { - it('has valid empty value', async () => { - const apiResponse: any[] = []; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - - it('has valid values', async () => { - const apiResponse = [ - { - submission_id: 1, - source: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - event_timestamp: '2022-05-24T18:41:42.211Z', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - revision_count: 1 - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response).to.equal(undefined); - }); - }); - }); - }); - describe('listSubmissions', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error if listSubmissionRecords throws an ApiGeneralError', async () => { - const dbConnectionObj = getMockDBConnection({ - commit: sinon.stub(), - rollback: sinon.stub(), - release: sinon.stub() - }); - - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - sinon.stub(SubmissionService.prototype, 'listSubmissionRecords').throws('error' as unknown as ApiGeneralError); - - try { - const requestHandler = listDataset(); - - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect(dbConnectionObj.commit).to.not.be.called; - expect(dbConnectionObj.rollback).to.be.calledOnce; - expect(dbConnectionObj.release).to.be.calledOnce; - } - }); - - it('should return rows on success', async () => { - const mockDBConnection = getMockDBConnection(); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - sinon.stub(db, 'getDBConnection').returns(mockDBConnection); - - const mockResponse = [ - { - submission_status: 'Submission Data Ingested', - submission_id: 1, - source_transform_id: 1, - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - record_effective_date: '2022-05-24T18:41:42.211Z', - record_end_date: null, - input_key: 'biohub/1/moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - input_file_name: 'moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - eml_source: null, - eml_json_source: null, - darwin_core_source: 'test', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - update_date: '2022-05-24T18:41:42.056Z', - update_user: 15, - revision_count: 1 - } - ]; - - sinon.stub(SubmissionService.prototype, 'listSubmissionRecords').resolves(mockResponse); - - const requestHandler = listDataset(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.jsonValue).to.eql(mockResponse); - }); - }); -}); diff --git a/api/src/paths/dwc/submission/list.ts b/api/src/paths/dwc/submission/list.ts deleted file mode 100644 index d85653397..000000000 --- a/api/src/paths/dwc/submission/list.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getDBConnection } from '../../../database/db'; -import { defaultErrorResponses } from '../../../openapi/schemas/http-responses'; -import { ISubmissionModelWithStatus } from '../../../repositories/submission-repository'; -import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { SubmissionService } from '../../../services/submission-service'; -import { getLogger } from '../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/submission/list'); - -export const GET: Operation = [ - authorizeRequestHandler(() => { - return { - and: [ - { - discriminator: 'SystemUser' - } - ] - }; - }), - listDataset() -]; - -GET.apiDoc = { - description: 'List submitted Darwin Core Archive (DwCA) data packages.', - tags: ['dwc'], - security: [ - { - Bearer: [] - } - ], - responses: { - 200: { - description: 'Darwin Core data packages.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - title: 'Darwin Core Data Packages Response Object', - type: 'object', - required: ['submission_id', 'uuid', 'event_timestamp', 'create_date', 'create_user', 'revision_count'], - properties: { - submission_status: { - type: 'string', - nullable: true - }, - submission_id: { - type: 'integer', - minimum: 1 - }, - source: { - type: 'string' - }, - uuid: { - type: 'string', - format: 'uuid' - }, - event_timestamp: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }] - }, - delete_timestamp: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }], - nullable: true - }, - input_key: { - type: 'string', - nullable: true - }, - input_file_name: { - type: 'string', - nullable: true - }, - eml_source: { - type: 'string', - nullable: true - }, - eml_json_source: { - type: 'string', - nullable: true - }, - darwin_core_source: { - oneOf: [{ type: 'object' }, { type: 'string' }], - nullable: true - }, - create_date: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }] - }, - create_user: { - type: 'integer' - }, - update_date: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }], - nullable: true - }, - update_user: { - type: 'integer', - nullable: true - }, - revision_count: { - type: 'integer' - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -export function listDataset(): RequestHandler { - return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); - - try { - await connection.open(); - - const submissionService = new SubmissionService(connection); - - const submissions: ISubmissionModelWithStatus[] = await submissionService.listSubmissionRecords(); - - await connection.commit(); - - res.status(200).json(submissions); - } catch (error) { - defaultLog.error({ label: 'listDataset', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/{datasetId}/artifacts.test.ts b/api/src/paths/dwc/submission/{datasetId}/artifacts.test.ts deleted file mode 100644 index e79ea17fc..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/artifacts.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../../database/db'; -import { HTTPError } from '../../../../errors/http-error'; -import { SECURITY_APPLIED_STATUS } from '../../../../repositories/security-repository'; -import { ArtifactService } from '../../../../services/artifact-service'; -import { SecurityService } from '../../../../services/security-service'; -import { UserService } from '../../../../services/user-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; -import { GET, getArtifactsByDatasetId } from './artifacts'; - -chai.use(sinonChai); - -describe('getArtifactsByDatasetId', () => { - afterEach(() => { - sinon.restore(); - }); - - describe('openApiSchema', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - describe('should throw an error when', () => { - describe('datasetId', () => { - it('is invalid type', async () => { - const request = { - params: { datasetId: 123 } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('is invalid format', async () => { - const request = { - params: { datasetId: 'abcdefg' } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must match format "uuid"'); - }); - }); - }); - - describe('should succeed when', () => { - it('required values are valid', async () => { - const request = { - params: { datasetId: '374c4d6a-3a04-405b-af6d-b6497800a691' } - }; - - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - const mockResponse = { - artifact_id: 1, - create_date: '1970-01-01T00:00:00.000Z', - description: 'description', - file_name: 'Lecture 5 - Partial Fraction.pdf', - file_size: 2405262, - file_type: 'Report', - foi_reason: false, - key: 'cupshall/platform/datasets/de621765-9fd0-4216-91b7-ec455d9c3eb1/artifacts/1/374c4d6a-3a04-405b-af6d-b6497800a691.zip', - security_review_timestamp: '1970-01-01T00:00:00.000Z', - submission_id: 1, - title: 'Report 2', - uuid: '374c4d6a-3a04-405b-af6d-b6497800a691', - supplementaryData: { - persecutionAndHarmStatus: 'SECURED', - persecutionAndHarmRules: [] - } - }; - describe('should throw an error when', () => { - it('returns a null response', async () => { - const apiResponse = null; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be array'); - }); - - describe('artifact', () => { - it('returns a null response', async () => { - const apiResponse = [null]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be object'); - }); - - describe('artifact_id', () => { - it('is undefined', async () => { - const apiResponse = [ - { - ...mockResponse, - artifact_id: undefined - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal("must have required property 'artifact_id'"); - }); - - it('is null', async () => { - const apiResponse = [ - { - ...mockResponse, - artifact_id: null - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be integer'); - }); - - it('is wrong type', async () => { - const apiResponse = [ - { - ...mockResponse, - artifact_id: '1' - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be integer'); - }); - }); - - describe('create_date', () => { - it('is undefined', async () => { - const apiResponse = [ - { - ...mockResponse, - create_date: undefined - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal("must have required property 'create_date'"); - }); - - it('is null', async () => { - const apiResponse = [ - { - ...mockResponse, - create_date: null - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(3); - expect(response.errors[0].message).to.equal('must be object'); - expect(response.errors[1].message).to.equal('must be string'); - }); - }); - }); - }); - - describe('should succeed when', () => { - it('required values are valid', async () => { - const apiResponse = [ - { - artifact_id: 1, - create_date: '1970-01-01T00:00:00.000Z', - description: 'Test description', - file_name: 'Filename.docx', - file_size: 1024, - file_type: 'Report', - foi_reason: false, - key: 'platform/datasets/de621765-9fd0-4216-91b7-ec455d9c3eb1/artifacts/1/374c4d6a-3a04-405b-af6d-b6497800a691.zip', - security_review_timestamp: '1970-01-01T00:00:00.000Z', - submission_id: 1, - title: 'Test Report', - uuid: '374c4d6a-3a04-405b-af6d-b6497800a691', - supplementaryData: { - persecutionAndHarmStatus: 'SECURED', - persecutionAndHarmRules: [] - } - } - ]; - const response = responseValidator.validateResponse(200, apiResponse); - expect(response).to.equal(undefined); - }); - }); - }); - }); - - it('should return a valid array of artifact records on success', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const artifactServiceStub = sinon.stub(ArtifactService.prototype, 'getArtifactsByDatasetId').resolves([ - { artifact_id: 1, security_review_timestamp: new Date('1970-01-01T00:00:00.000Z') }, - { artifact_id: 2, security_review_timestamp: new Date('1970-01-01T00:00:00.000Z') } - ] as any[]); - - const isSystemUserAdminStub = sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(true); - - const getArtifactSupplementaryDataStub = sinon - .stub(SecurityService.prototype, 'getArtifactSupplementaryData') - .resolves({ persecutionAndHarmRules: [], persecutionAndHarmStatus: SECURITY_APPLIED_STATUS.UNSECURED }); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - const requestHandler = getArtifactsByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - expect(artifactServiceStub).to.be.calledWith('abcd'); - expect(mockRes.jsonValue).to.eql([ - { - artifact_id: 1, - security_review_timestamp: new Date('1970-01-01T00:00:00.000Z'), - supplementaryData: { - persecutionAndHarmStatus: 'UNSECURED', - persecutionAndHarmRules: [] - } - }, - { - artifact_id: 2, - security_review_timestamp: new Date('1970-01-01T00:00:00.000Z'), - supplementaryData: { - persecutionAndHarmStatus: 'UNSECURED', - persecutionAndHarmRules: [] - } - } - ]); - expect(isSystemUserAdminStub).to.be.calledOnce; - expect(getArtifactSupplementaryDataStub).to.be.calledTwice; - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - sinon.stub(ArtifactService.prototype, 'getArtifactsByDatasetId').rejects(new Error('a test error')); - sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(true); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - try { - const requestHandler = getArtifactsByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('a test error'); - } - }); -}); diff --git a/api/src/paths/dwc/submission/{datasetId}/artifacts.ts b/api/src/paths/dwc/submission/{datasetId}/artifacts.ts deleted file mode 100644 index 82fa8c8da..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/artifacts.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../../database/db'; -import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses'; -import { ArtifactService } from '../../../../services/artifact-service'; -import { SecurityService } from '../../../../services/security-service'; -import { UserService } from '../../../../services/user-service'; -import { getLogger } from '../../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/submission/{datasetId}/artifacts'); - -export const GET: Operation = [getArtifactsByDatasetId()]; - -GET.apiDoc = { - description: 'Retrieves dataset artifact records by dataset ID', - tags: ['artifacts'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'Dataset identifier', - in: 'path', - name: 'datasetId', - schema: { - type: 'string', - format: 'uuid' - }, - required: true - } - ], - responses: { - 200: { - description: 'Dataset attachments response object.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - required: [ - 'artifact_id', - 'create_date', - 'description', - 'file_name', - 'file_size', - 'foi_reason', - 'key', - 'security_review_timestamp', - 'submission_id', - 'title', - 'uuid', - 'supplementaryData' - ], - properties: { - artifact_id: { - type: 'integer', - minimum: 1 - }, - create_date: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }] - }, - description: { - type: 'string', - nullable: true - }, - file_name: { - type: 'string' - }, - file_size: { - type: 'integer' - }, - foi_reason: { - type: 'boolean', - nullable: true - }, - key: { - type: 'string' - }, - security_review_timestamp: { - oneOf: [{ type: 'object' }, { type: 'string', format: 'date-time' }], - nullable: true - }, - submission_id: { - type: 'integer', - minimum: 1 - }, - title: { - type: 'string', - nullable: true - }, - uuid: { - type: 'string', - format: 'uuid' - }, - supplementaryData: { - type: 'object', - required: ['persecutionAndHarmRules', 'persecutionAndHarmStatus'], - properties: { - persecutionAndHarmStatus: { - type: 'string', - enum: ['SECURED', 'UNSECURED', 'PENDING'] - }, - persecutionAndHarmRules: { - type: 'array', - items: { - type: 'object', - properties: { - artifact_id: { - type: 'integer' - }, - artifact_persecution_id: { - type: 'integer' - }, - persecution_or_harm_id: { - type: 'integer' - } - } - } - } - } - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Retrieves dataset artifacts - * - * @returns {RequestHandler} - */ -export function getArtifactsByDatasetId(): RequestHandler { - return async (req, res) => { - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - const datasetId = String(req.params.datasetId); - - try { - await connection.open(); - const userService = new UserService(connection); - const artifactService = new ArtifactService(connection); - const securityService = new SecurityService(connection); - - const isAdmin = await userService.isSystemUserAdmin(); - const artifacts = await artifactService.getArtifactsByDatasetId(datasetId); - - const artifactsWithRules = await Promise.all( - artifacts.map(async (artifact) => { - const supplementaryData = await securityService.getArtifactSupplementaryData(artifact.artifact_id, isAdmin); - return { - ...artifact, - supplementaryData: supplementaryData - }; - }) - ); - - await connection.commit(); - - res.status(200).json(artifactsWithRules); - } catch (error) { - defaultLog.error({ label: 'getArtifactsByDatasetId', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/{datasetId}/get.test.ts b/api/src/paths/dwc/submission/{datasetId}/get.test.ts deleted file mode 100644 index c1abda0b6..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/get.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../../database/db'; -import { HTTPError } from '../../../../errors/http-error'; -import { SubmissionService } from '../../../../services/submission-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; -import { getMetadataByDatasetId } from './get'; - -chai.use(sinonChai); - -describe('get', () => { - describe('dataset metadata (from the DB) by datasetId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should return a valid submission record on success', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - sinon.stub(SubmissionService.prototype, 'getSubmissionRecordEMLJSONByDatasetId').resolves({}); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - const requestHandler = getMetadataByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - sinon - .stub(SubmissionService.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .rejects(new Error('a test error')); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - try { - const requestHandler = getMetadataByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('a test error'); - } - }); - }); -}); diff --git a/api/src/paths/dwc/submission/{datasetId}/get.ts b/api/src/paths/dwc/submission/{datasetId}/get.ts deleted file mode 100644 index 5b853cbe8..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/get.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../../database/db'; -import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses'; -import { SubmissionService } from '../../../../services/submission-service'; -import { getLogger } from '../../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/submission/{datasetId}/get'); - -export const GET: Operation = [getMetadataByDatasetId()]; - -GET.apiDoc = { - description: 'retrieves dataset metadata within elastic search', - tags: ['eml'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'dataset Id.', - in: 'path', - name: 'datasetId', - schema: { - type: 'string' - }, - required: true - } - ], - responses: { - 200: { - description: 'Dataset metadata response object.', - content: { - 'application/json': { - schema: { - type: 'object' - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Retrieves dataset metadata from the submission table. - * - * @returns {RequestHandler} - */ -export function getMetadataByDatasetId(): RequestHandler { - return async (req, res) => { - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - const datasetId = String(req.params.datasetId); - - try { - await connection.open(); - - const submissionService = new SubmissionService(connection); - - const result = await submissionService.getSubmissionRecordEMLJSONByDatasetId(datasetId); - - await connection.commit(); - - res.status(200).json(result); - } catch (error) { - defaultLog.error({ label: 'getMetadataByDatasetId', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/{datasetId}/handlebar.test.ts b/api/src/paths/dwc/submission/{datasetId}/handlebar.test.ts deleted file mode 100644 index 8e32c7194..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/handlebar.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../../database/db'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; -import { getHandleBarsTemplateByDatasetId } from './handlebar'; - -chai.use(sinonChai); - -describe('handlebar', () => { - describe('get a handlebars template for a given datasetId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should return template data object', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'uuid' - }; - - const requestHandler = getHandleBarsTemplateByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - }); - }); -}); diff --git a/api/src/paths/dwc/submission/{datasetId}/handlebar.ts b/api/src/paths/dwc/submission/{datasetId}/handlebar.ts deleted file mode 100644 index b779ca3c0..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/handlebar.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../../database/db'; -import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses'; -import { getLogger } from '../../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/eml/{datasetId}/handlebar'); - -export const GET: Operation = [getHandleBarsTemplateByDatasetId()]; - -GET.apiDoc = { - description: 'retrieves handle bar template for a given dataset id', - tags: ['eml'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'dataset Id.', - in: 'path', - name: 'datasetId', - schema: { - type: 'string' - }, - required: true - } - ], - responses: { - 200: { - description: 'Dataset handlebars response string', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - header: { - type: 'string' - }, - details: { - type: 'string' - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Retrieves dataset metadata from the submission table. - * - * @returns {RequestHandler} - */ -export function getHandleBarsTemplateByDatasetId(): RequestHandler { - return async (req, res) => { - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - // const datasetId = String(req.params.datasetId); - - try { - await connection.open(); - - // const submissionService = new SubmissionService(connection); - - // const result = await submissionService.getHandleBarsTemplateByDatasetId(datasetId); - - await connection.commit(); - - res.status(200).json(''); - } catch (error) { - defaultLog.error({ label: 'getHandleBarsTemplateByDatasetId', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/paths/dwc/submission/{datasetId}/related.test.ts b/api/src/paths/dwc/submission/{datasetId}/related.test.ts deleted file mode 100644 index e5a2a6f0a..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/related.test.ts +++ /dev/null @@ -1,348 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import OpenAPIRequestValidator, { OpenAPIRequestValidatorArgs } from 'openapi-request-validator'; -import OpenAPIResponseValidator, { OpenAPIResponseValidatorArgs } from 'openapi-response-validator'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import * as db from '../../../../database/db'; -import { HTTPError } from '../../../../errors/http-error'; -import { SecurityService } from '../../../../services/security-service'; -import { RelatedDataset, SubmissionService } from '../../../../services/submission-service'; -import { UserService } from '../../../../services/user-service'; -import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; -import { GET, getRelatedDatasetsByDatasetId } from './related'; - -chai.use(sinonChai); - -describe('getRelatedDatasetsByDatasetId', () => { - afterEach(() => { - sinon.restore(); - }); - - describe('openApiSchema', () => { - describe('request validation', () => { - const requestValidator = new OpenAPIRequestValidator(GET.apiDoc as unknown as OpenAPIRequestValidatorArgs); - - describe('should throw an error when', () => { - describe('datasetId', () => { - it('is invalid type', async () => { - const request = { - params: { datasetId: 123 } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('is invalid format', async () => { - const request = { - params: { datasetId: 'abcdefg' } - }; - - const response = requestValidator.validateRequest(request); - - expect(response.status).to.equal(400); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must match format "uuid"'); - }); - }); - }); - - describe('should succeed when', () => { - it('required values are valid', async () => { - const request = { - params: { datasetId: '374c4d6a-3a04-405b-af6d-b6497800a691' } - }; - - const response = requestValidator.validateRequest(request); - - expect(response).to.be.undefined; - }); - }); - }); - - describe('response validation', () => { - const responseValidator = new OpenAPIResponseValidator(GET.apiDoc as unknown as OpenAPIResponseValidatorArgs); - const mockRelatedDataset = { - datasetId: '374c4d6a-3a04-405b-af6d-b6497800a691', - title: 'Test-Related-Dataset', - url: 'https://example.com/374c4d6a-3a04-405b-af6d-b6497800a691', - supplementaryData: { - isPendingReview: false - } - }; - - describe('should throw an error when', () => { - it('returns a null response', async () => { - const apiResponse = null; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be object'); - }); - - it('returns a null array', async () => { - const apiResponse = { datasets: null }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be array'); - }); - - describe('dataset', () => { - it('returns a null response', async () => { - const apiResponse = { datasets: [null] }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be object'); - }); - - describe('datsetId', () => { - it('is undefined', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - datasetId: undefined - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal("must have required property 'datasetId'"); - }); - - it('is null', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - datasetId: null - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('is wrong type', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - datasetId: 1 - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - }); - - describe('title', () => { - it('is undefined', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - title: undefined - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal("must have required property 'title'"); - }); - - it('is null', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - title: null - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('is wrong type', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - title: 1 - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - }); - - describe('url', () => { - it('is undefined', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - url: undefined - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal("must have required property 'url'"); - }); - - it('is null', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - url: null - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - - it('is wrong type', async () => { - const apiResponse = { - datasets: [ - { - ...mockRelatedDataset, - url: 1 - } - ] - }; - const response = responseValidator.validateResponse(200, apiResponse); - - expect(response.message).to.equal('The response was not valid.'); - expect(response.errors.length).to.equal(1); - expect(response.errors[0].message).to.equal('must be string'); - }); - }); - }); - }); - - describe('should succeed when', () => { - it('required values are valid', async () => { - const apiResponse = { - datasets: [mockRelatedDataset] - }; - - const response = responseValidator.validateResponse(200, apiResponse); - expect(response).to.equal(undefined); - }); - - it('returns an empty array of related datasets', async () => { - const apiResponse = { - datasets: [] - }; - - const response = responseValidator.validateResponse(200, apiResponse); - expect(response).to.equal(undefined); - }); - }); - }); - }); - - it('should return a valid array of related datasets on success', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - - const submissionServiceStub = sinon - .stub(SubmissionService.prototype, 'findRelatedDatasetsByDatasetId') - .resolves([{ datasetId: '123' }, { datasetId: '456' }] as RelatedDataset[]); - - const isSystemUserAdminStub = sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(true); - - const securityStub = sinon.stub(SecurityService.prototype, 'isDatasetPendingReview'); - securityStub.onFirstCall().resolves(true); - securityStub.onSecondCall().resolves(false); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - const requestHandler = getRelatedDatasetsByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - - expect(mockRes.statusValue).to.equal(200); - expect(submissionServiceStub).to.be.calledWith('abcd'); - expect(mockRes.jsonValue).to.eql({ - datasetsWithSupplementaryData: [ - { - datasetId: '123', - supplementaryData: { - isPendingReview: true - } - }, - { - datasetId: '456', - supplementaryData: { - isPendingReview: false - } - } - ] - }); - expect(isSystemUserAdminStub).to.be.calledOnce; - }); - - it('catches and re-throws an error', async () => { - const dbConnectionObj = getMockDBConnection(); - sinon.stub(db, 'getDBConnection').returns(dbConnectionObj); - sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(true); - - sinon.stub(SubmissionService.prototype, 'findRelatedDatasetsByDatasetId').rejects(new Error('a test error')); - - const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - - mockReq.params = { - datasetId: 'abcd' - }; - - try { - const requestHandler = getRelatedDatasetsByDatasetId(); - - await requestHandler(mockReq, mockRes, mockNext); - expect.fail(); - } catch (actualError) { - expect((actualError as HTTPError).message).to.equal('a test error'); - } - }); -}); diff --git a/api/src/paths/dwc/submission/{datasetId}/related.ts b/api/src/paths/dwc/submission/{datasetId}/related.ts deleted file mode 100644 index 0cb794a82..000000000 --- a/api/src/paths/dwc/submission/{datasetId}/related.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { RequestHandler } from 'express'; -import { Operation } from 'express-openapi'; -import { getAPIUserDBConnection, getDBConnection } from '../../../../database/db'; -import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses'; -import { SecurityService } from '../../../../services/security-service'; -import { SubmissionService } from '../../../../services/submission-service'; -import { UserService } from '../../../../services/user-service'; -import { getLogger } from '../../../../utils/logger'; - -const defaultLog = getLogger('paths/dwc/submission/{datasetId}/related'); - -export const GET: Operation = [getRelatedDatasetsByDatasetId()]; - -GET.apiDoc = { - description: 'retrieves related datasets within elastic search', - tags: ['eml'], - security: [ - { - OptionalBearer: [] - } - ], - parameters: [ - { - description: 'Dataset ID', - in: 'path', - name: 'datasetId', - schema: { - type: 'string', - format: 'uuid' - }, - required: true - } - ], - responses: { - 200: { - description: 'Related datasets response object.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - datasets: { - type: 'array', - items: { - type: 'object', - required: ['datasetId', 'title', 'url', 'supplementaryData'], - properties: { - datasetId: { - type: 'string', - format: 'uuid' - }, - title: { - type: 'string' - }, - url: { - type: 'string' - }, - supplementaryData: { - type: 'object', - properties: { - isPendingReview: { - type: 'boolean' - } - } - } - } - } - } - } - } - } - } - }, - ...defaultErrorResponses - } -}; - -/** - * Retrieves related datasets within elastic search - * - * @returns {RequestHandler} - */ -export function getRelatedDatasetsByDatasetId(): RequestHandler { - return async (req, res) => { - const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection(); - - const datasetId = String(req.params.datasetId); - - try { - await connection.open(); - - const submissionService = new SubmissionService(connection); - const securityService = new SecurityService(connection); - const userService = new UserService(connection); - - const isAdmin = await userService.isSystemUserAdmin(); - const datasets = await submissionService.findRelatedDatasetsByDatasetId(datasetId); - - const datasetsWithSupplementaryData = await Promise.all( - datasets.map(async (dataset) => { - if (!isAdmin) { - return { - ...dataset, - supplementaryData: {} - }; - } - - const isDatasetPendingReview = await securityService.isDatasetPendingReview(dataset.datasetId); - - return { - ...dataset, - supplementaryData: { isPendingReview: isDatasetPendingReview } - }; - }) - ); - - await connection.commit(); - - res.status(200).json({ datasetsWithSupplementaryData }); - } catch (error) { - defaultLog.error({ label: 'getRelatedDatasetsByDatasetId', message: 'error', error }); - await connection.rollback(); - throw error; - } finally { - connection.release(); - } - }; -} diff --git a/api/src/repositories/spatial-repository.test.ts b/api/src/repositories/spatial-repository.test.ts deleted file mode 100644 index b276f88bb..000000000 --- a/api/src/repositories/spatial-repository.test.ts +++ /dev/null @@ -1,638 +0,0 @@ -import chai, { expect } from 'chai'; -import { FeatureCollection } from 'geojson'; -import { describe } from 'mocha'; -import { QueryResult } from 'pg'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import SQL from 'sql-template-strings'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { ApiGeneralError } from '../errors/api-error'; -import { Srid3005 } from '../services/geo-service'; -import { UserService } from '../services/user-service'; -import * as spatialUtils from '../utils/spatial-utils'; -import { getMockDBConnection } from '../__mocks__/db'; -import { - IInsertSpatialTransform, - ISpatialComponentsSearchCriteria, - ISubmissionSpatialComponent, - SpatialRepository -} from './spatial-repository'; -import { SystemUserExtended } from './user-repository'; - -chai.use(sinonChai); - -describe('SpatialRepository', () => { - describe('insertSpatialTransform', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - try { - await spatialRepository.insertSpatialTransform({} as unknown as IInsertSpatialTransform); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to insert spatial transform details'); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ spatial_transform_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.insertSpatialTransform({} as unknown as IInsertSpatialTransform); - - expect(response.spatial_transform_id).to.equal(1); - }); - }); - - describe('getSpatialTransformRecords', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [ - { - spatial_transform_id: 1, - name: 'transform name', - description: 'transform description', - notes: 'notes', - transform: 'transform details' - } - ] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.getSpatialTransformRecords(); - - expect(response[0].spatial_transform_id).to.equal(1); - expect(response[0].name).to.equal('transform name'); - expect(response[0].description).to.equal('transform description'); - expect(response[0].notes).to.equal('notes'); - expect(response[0].transform).to.equal('transform details'); - }); - }); - - describe('getSecurityTransformRecords', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [ - { - security_transform_id: 1, - name: 'transform name', - description: 'transform description', - notes: 'notes', - transform: 'transform details' - } - ] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.getSecurityTransformRecords(); - - expect(response[0].security_transform_id).to.equal(1); - expect(response[0].name).to.equal('transform name'); - expect(response[0].description).to.equal('transform description'); - expect(response[0].notes).to.equal('notes'); - expect(response[0].transform).to.equal('transform details'); - }); - }); - - describe('insertSpatialTransformSubmissionRecord', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - try { - await spatialRepository.insertSpatialTransformSubmissionRecord(1, 1); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal( - 'Failed to insert spatial transform submission id and submission spatial component id' - ); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ spatial_transform_submission_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.insertSpatialTransformSubmissionRecord(1, 1); - - expect(response.spatial_transform_submission_id).to.equal(1); - }); - }); - - describe('insertSecurityTransformSubmissionRecord', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - try { - await spatialRepository.insertSecurityTransformSubmissionRecord(1, 1); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal( - 'Failed to insert security transform submission id and submission spatial component id' - ); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ security_transform_submission_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.insertSecurityTransformSubmissionRecord(1, 1); - - expect(response.security_transform_submission_id).to.equal(1); - }); - }); - - describe('runSpatialTransformOnSubmissionId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [ - { - result_data: { - type: 'FeatureCollection', - features: [] - } as FeatureCollection - } - ] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - query: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.runSpatialTransformOnSubmissionId(1, 'string'); - - expect(response).to.eql([ - { - result_data: { - type: 'FeatureCollection', - features: [] - } as FeatureCollection - } - ]); - }); - }); - - describe('runSecurityTransformOnSubmissionId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { - rowCount: 1, - rows: [ - { - result_data: { - type: 'FeatureCollection', - features: [] - } as FeatureCollection - } - ] - } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - query: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.runSecurityTransformOnSubmissionId(1, 'string'); - - expect(response).to.eql([ - { - result_data: { - type: 'FeatureCollection', - features: [] - } as FeatureCollection - } - ]); - }); - }); - - describe('insertSubmissionSpatialComponent', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - try { - await spatialRepository.insertSubmissionSpatialComponent(1, {} as FeatureCollection); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal( - 'Failed to insert submission spatial component details' - ); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_spatial_component_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.insertSubmissionSpatialComponent(1, {} as FeatureCollection); - - expect(response.submission_spatial_component_id).to.equal(1); - }); - - it('should succeed with valid data and append geography to sql statement', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_spatial_component_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const generateGeometryCollectionSQLStub = sinon - .stub(spatialUtils, 'generateGeometryCollectionSQL') - .returns(SQL`valid`); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.insertSubmissionSpatialComponent(1, { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [125.6, 10.1] - }, - properties: {} - } - ] - } as FeatureCollection); - - expect(response.submission_spatial_component_id).to.equal(1); - expect(generateGeometryCollectionSQLStub).to.be.calledOnce; - }); - }); - - describe('updateSubmissionSpatialComponent', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - try { - await spatialRepository.updateSubmissionSpatialComponentWithSecurity(1, {} as object); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal( - 'Failed to update submission spatial component details' - ); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_spatial_component_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.updateSubmissionSpatialComponentWithSecurity(1, {} as object); - - expect(response.submission_spatial_component_id).to.equal(1); - }); - }); - - describe('findSpatialComponentsByCriteria', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should call _findSpatialComponentsByCriteria', async () => { - const mockDBConnection = getMockDBConnection(); - - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSpatialComponentsByCriteriaAsAdminUserStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteriaAsAdminUser') - .resolves(); - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteria') - .resolves(); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - await spatialRepository.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(findSpatialComponentsByCriteriaAsAdminUserStub).not.to.have.been.called; - expect(findSpatialComponentsByCriteriaStub).to.have.been.calledOnce; - }); - }); - - describe('_findSpatialComponentsByCriteriaAsAdminUser', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with minimal search criteria', async () => { - const mockResponseRow1 = { submission_spatial_component_id: 1 } as unknown as ISubmissionSpatialComponent; - const mockResponseRow2 = { submission_spatial_component_id: 2 } as unknown as ISubmissionSpatialComponent; - const mockQueryResponse = { rowCount: 2, rows: [mockResponseRow1, mockResponseRow2] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialRepository.findSpatialComponentsByCriteriaAsAdminUser(mockSearchCriteria); - - expect(response).to.eql([mockResponseRow1, mockResponseRow2]); - }); - - it('should succeed with maximal search criteria', async () => { - const mockResponseRow1 = { submission_spatial_component_id: 1 } as unknown as ISubmissionSpatialComponent; - const mockResponseRow2 = { submission_spatial_component_id: 2 } as unknown as ISubmissionSpatialComponent; - const mockQueryResponse = { rowCount: 2, rows: [mockResponseRow1, mockResponseRow2] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence', 'Boundary'], - datasetID: ['111-111-111', '222-222-222'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialRepository.findSpatialComponentsByCriteriaAsAdminUser(mockSearchCriteria); - - expect(response).to.eql([mockResponseRow1, mockResponseRow2]); - }); - }); - - describe('_findSpatialComponentsByCriteria', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with minimal search criteria', async () => { - const mockResponseRow1 = { submission_spatial_component_id: 1 } as unknown as ISubmissionSpatialComponent; - const mockResponseRow2 = { submission_spatial_component_id: 2 } as unknown as ISubmissionSpatialComponent; - const mockQueryResponse = { rowCount: 2, rows: [mockResponseRow1, mockResponseRow2] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialRepository.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(response).to.eql([mockResponseRow1, mockResponseRow2]); - }); - - it('should succeed with maximal search criteria', async () => { - const mockResponseRow1 = { submission_spatial_component_id: 1 } as unknown as ISubmissionSpatialComponent; - const mockResponseRow2 = { submission_spatial_component_id: 2 } as unknown as ISubmissionSpatialComponent; - const mockQueryResponse = { rowCount: 2, rows: [mockResponseRow1, mockResponseRow2] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence', 'Boundary'], - species: ['Moose'], - datasetID: ['111-111-111', '222-222-222'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialRepository.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(response).to.eql([mockResponseRow1, mockResponseRow2]); - }); - }); - - describe('deleteSpatialComponentsBySubmissionId', () => { - it('should successfully return submission IDs for delete spatial data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_id: 2 }] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.deleteSpatialComponentsBySubmissionId(2); - - expect(response[0].submission_id).to.equal(2); - }); - }); - - describe('deleteSpatialComponentsSpatialRefsBySubmissionId', () => { - it('should successfully return submission IDs for deleted spatial component reference', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_id: 2 }] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.deleteSpatialComponentsSpatialTransformRefsBySubmissionId(2); - - expect(response[0].submission_id).to.equal(2); - }); - }); - - describe('deleteSpatialComponentsSecurityRefsBySubmissionId', () => { - it('should successfully return submission IDs for deleted spatial component reference', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_id: 2 }] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: async () => { - return mockQueryResponse; - } - }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const response = await spatialRepository.deleteSpatialComponentsSecurityTransformRefsBySubmissionId(2); - - expect(response[0].submission_id).to.equal(2); - }); - }); - - describe('getGeometryAsWktFromBoundarySpatialComponentBySubmissionId', () => { - it('returns a geometry WKT string', async () => { - const mockResponse = { rowCount: 1, rows: [{ geometry: 'POLYGON(123,456,789)' }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ knex: async () => mockResponse }); - - const spatialRepository = new SpatialRepository(mockDBConnection); - - const submissionId = 1; - - const response = await spatialRepository.getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId, - SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, - Srid3005 - ); - - expect(response).to.eql({ geometry: 'POLYGON(123,456,789)' }); - }); - }); -}); diff --git a/api/src/repositories/spatial-repository.ts b/api/src/repositories/spatial-repository.ts deleted file mode 100644 index d60190479..000000000 --- a/api/src/repositories/spatial-repository.ts +++ /dev/null @@ -1,973 +0,0 @@ -import { Feature, FeatureCollection, GeoJsonProperties } from 'geojson'; -import { Knex } from 'knex'; -import SQL from 'sql-template-strings'; -import { z } from 'zod'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { getKnex } from '../database/db'; -import { ApiExecuteSQLError } from '../errors/api-error'; -import { Srid } from '../services/geo-service'; -import { generateGeometryCollectionSQL } from '../utils/spatial-utils'; -import { BaseRepository } from './base-repository'; - -export interface IInsertSpatialTransform { - name: string; - description: string; - notes: string; - transform: string; -} - -export interface IGetSpatialTransformRecord { - spatial_transform_id: number; - name: string; - description: string | null; - notes: string | null; - transform: string; -} - -export interface IGetSecurityTransformRecord { - security_transform_id: number; - name: string; - description: string | null; - notes: string | null; - transform: string; -} - -export interface ITransformSpatialRow { - result_data: FeatureCollection; -} - -export interface ITransformSecureRow { - spatial_component: { - spatial_data: FeatureCollection; - submission_spatial_component_id: number; - }; -} - -export interface ISubmissionSpatialComponent { - submission_spatial_component_ids: number[]; - submission_id: number; - spatial_component: FeatureCollection; - geometry: null; - geography: string; - secured_spatial_component: FeatureCollection; - secured_geometry: null; - secured_geography: string; -} - -export interface ISpatialComponentsSearchCriteria { - boundary: Feature[]; - type?: string[]; - species?: string[]; - datasetID?: string[]; -} - -export type EmptyObject = Record; - -export interface ITaxaData { - taxon_id?: string; - vernacular_name?: string; - submission_spatial_component_id: number; -} - -export interface ISubmissionSpatialSearchResponseRow { - taxa_data: ITaxaData[]; - spatial_component: { - spatial_data: FeatureCollection | EmptyObject; - }; -} - -export interface ISpatialComponentFeaturePropertiesRow { - spatial_component_properties: GeoJsonProperties; -} - -export class SpatialRepository extends BaseRepository { - /** - * Insert new spatial transform record - * - * @param {IInsertSpatialTransform} spatialTransformDetails - * @return {*} {Promise<{ spatial_transform_id: number }>} - * @memberof SpatialRepository - */ - async insertSpatialTransform( - spatialTransformDetails: IInsertSpatialTransform - ): Promise<{ spatial_transform_id: number }> { - const sqlStatement = SQL` - INSERT INTO spatial_transform ( - name, - description, - notes, - transform - ) VALUES ( - ${spatialTransformDetails.name}, - ${spatialTransformDetails.description}, - ${spatialTransformDetails.notes}, - ${spatialTransformDetails.transform} - ) - RETURNING - spatial_transform_id; - `; - - const response = await this.connection.sql<{ spatial_transform_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to insert spatial transform details', [ - 'SpatialRepository->insertSpatialTransform', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - return response.rows[0]; - } - - /** - * get spatial transform records - * - * @param - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async getSpatialTransformRecords(): Promise { - const sqlStatement = SQL` - SELECT - spatial_transform_id, - name, - description, - notes, - transform - FROM - spatial_transform; - `; - - const response = await this.connection.sql(sqlStatement); - - return response.rows; - } - - /** - *get security transform records - * - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async getSecurityTransformRecords(): Promise { - const sqlStatement = SQL` - SELECT - security_transform_id, - name, - description, - notes, - transform - FROM - security_transform; - `; - - const response = await this.connection.sql(sqlStatement); - - return response.rows; - } - - /** - * Insert record of transform id used for submission spatial component record - * - * @param {number} spatialTransformId - * @param {number} submissionSpatialComponentId - * @return {*} {Promise<{ spatial_transform_submission_id: number }>} - * @memberof SpatialRepository - */ - async insertSpatialTransformSubmissionRecord( - spatialTransformId: number, - submissionSpatialComponentId: number - ): Promise<{ spatial_transform_submission_id: number }> { - const sqlStatement = SQL` - INSERT INTO spatial_transform_submission ( - spatial_transform_id, - submission_spatial_component_id - ) VALUES ( - ${spatialTransformId}, - ${submissionSpatialComponentId} - ) - RETURNING - spatial_transform_submission_id; - `; - - const response = await this.connection.sql<{ spatial_transform_submission_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError( - 'Failed to insert spatial transform submission id and submission spatial component id', - [ - 'SpatialRepository->insertSpatialTransformSubmissionRecord', - 'rowCount was null or undefined, expected rowCount >= 1' - ] - ); - } - return response.rows[0]; - } - - /** - * Insert record of transform id used for submission security component record - * - * @param {number} securityTransformId - * @param {number} submissionSpatialComponentId - * @return {*} {Promise<{ spatial_transform_submission_id: number }>} - * @memberof SpatialRepository - */ - async insertSecurityTransformSubmissionRecord( - securityTransformId: number, - submissionSpatialComponentId: number - ): Promise<{ security_transform_submission_id: number }> { - const sqlStatement = SQL` - INSERT INTO security_transform_submission ( - security_transform_id, - submission_spatial_component_id - ) VALUES ( - ${securityTransformId}, - ${submissionSpatialComponentId} - ) - RETURNING - security_transform_submission_id; - `; - - const response = await this.connection.sql<{ security_transform_submission_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError( - 'Failed to insert security transform submission id and submission spatial component id', - [ - 'SpatialRepository->insertSecurityTransformSubmissionRecord', - 'rowCount was null or undefined, expected rowCount = 1' - ] - ); - } - return response.rows[0]; - } - - /** - * Run Spatial Transform with transform string on submissionId - * - * @param {number} submissionId - * @param {string} transform - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async runSpatialTransformOnSubmissionId(submissionId: number, transform: string): Promise { - const response = await this.connection.query(transform, [submissionId]); - - return response.rows; - } - - /** - * Run Security Transform with transform string on submissionId - * - * @param {number} submissionId - * @param {string} transform - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async runSecurityTransformOnSubmissionId(submissionId: number, transform: string): Promise { - const response = await this.connection.query(transform, [submissionId]); - - return response.rows; - } - - /** - * Insert given transformed data into Spatial Component Table - * - * @param {number} submissionObservationId - * @param {Feature[]} transformedData - * @return {*} {Promise<{ submission_spatial_component_id: number }>} - * @memberof SpatialRepository - */ - async insertSubmissionSpatialComponent( - submissionObservationId: number, - transformedData: FeatureCollection - ): Promise<{ submission_spatial_component_id: number }> { - const sqlStatement = SQL` - INSERT INTO submission_spatial_component ( - submission_observation_id, - spatial_component, - geography - ) VALUES ( - ${submissionObservationId}, - ${JSON.stringify(transformedData)} - `; - - if (transformedData.features && transformedData.features.length > 0) { - const geoCollection = generateGeometryCollectionSQL(transformedData.features); - - sqlStatement.append(SQL` - ,public.geography( - public.ST_Force2D( - public.ST_SetSRID( - `); - - sqlStatement.append(geoCollection); - - sqlStatement.append(SQL` - , 4326))) - `); - } else { - sqlStatement.append(SQL` - ,null - `); - } - - sqlStatement.append(SQL` - ) - RETURNING - submission_spatial_component_id; - `); - - const response = await this.connection.sql<{ submission_spatial_component_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to insert submission spatial component details', [ - 'SpatialRepository->insertSubmissionSpatialComponent', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - return response.rows[0]; - } - - /** - * Update secured spatial column with the transformed spatial data - * - * @param {number} submissionId - * @param {Feature[]} transformedData - * @return {*} {Promise<{ submission_spatial_component_id: number }>} - * @memberof SpatialRepository - */ - async updateSubmissionSpatialComponentWithSecurity( - submissionSpatialComponentId: number, - transformedData: object - ): Promise<{ submission_spatial_component_id: number }> { - const sqlStatement = SQL` - UPDATE - submission_spatial_component - SET - secured_spatial_component = ${transformedData} - WHERE - submission_spatial_component_id = ${submissionSpatialComponentId} - RETURNING - submission_spatial_component_id; - `; - - const response = await this.connection.sql<{ submission_spatial_component_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to update submission spatial component details', [ - 'SpatialRepository->updateSubmissionSpatialComponentWithSecurity', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - return response.rows[0]; - } - - /** - * Query builder to find spatial component by given criteria, specifically for admin users that bypass all security - * rules. - * - * @param {ISpatialComponentsSearchCriteria} criteria - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async findSpatialComponentsByCriteriaAsAdminUser( - criteria: ISpatialComponentsSearchCriteria - ): Promise { - const knex = getKnex(); - - const queryBuilder = knex - .queryBuilder() - .with('distinct_geographic_points', this._withDistinctGeographicPoints) - .with('with_filtered_spatial_component', (qb1) => { - // Get the spatial components that match the search filters - qb1 - .select( - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, datasetID}' as dataset_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, taxonID}' as taxon_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, vernacularName}' as vernacular_name" - ), - 'ssc.submission_spatial_component_id', - 'ssc.submission_observation_id', - 'ssc.spatial_component', - 'ssc.geography' - ) - .from('submission_spatial_component as ssc') - .leftJoin('distinct_geographic_points as p', 'p.geography', 'ssc.geography') - .leftJoin('submission_observation as so', 'so.submission_observation_id', 'ssc.submission_observation_id') - .whereNull('so.record_end_timestamp') - .groupBy('ssc.submission_spatial_component_id') - .groupBy('ssc.submission_observation_id') - .groupBy('ssc.spatial_component') - .groupBy('ssc.geography'); - - if (criteria.type?.length) { - this._whereTypeIn(criteria.type, qb1); - } - - if (criteria.species?.length) { - this._whereSpeciesIn(criteria.species, qb1); - } - - if (criteria.datasetID?.length) { - this._whereDatasetIDIn(criteria.datasetID, qb1); - } - - this._whereBoundaryIntersects(criteria.boundary, 'ssc.geography', qb1); - }) - .with('with_coalesced_spatial_components', (qb7) => { - qb7 - .select( - // Select the non-secure spatial component from the search results - knex.raw( - ` - submission_spatial_component_id, - submission_observation_id, - geography, - jsonb_build_object( - 'submission_spatial_component_id', - wfsc.submission_spatial_component_id, - 'taxon_id', - wfsc.taxon_id, - 'vernacular_name', - wfsc.vernacular_name - ) taxa_data_object, - jsonb_build_object( - 'spatial_data', - wfsc.spatial_component - ) spatial_component - ` - ) - ) - .from(knex.raw('with_filtered_spatial_component as wfsc')); - }) - .select( - knex.raw('array_agg(submission_spatial_component_id) as submission_spatial_component_ids'), - knex.raw('array_agg(taxa_data_object) as taxa_data'), - knex.raw('(array_agg(spatial_component))[1] as spatial_component'), - 'geography' - ) - .from('with_coalesced_spatial_components') - // Filter out secure spatial components that have no spatial representation - // The user is not allowed to see any aspect of these particular spatial components - .whereRaw("spatial_component->'spatial_data' != '{}'") - .groupBy('geography'); - - const response = await this.connection.knex(queryBuilder); - - return response.rows; - } - - /** - * Query builder to find spatial component by given criteria. - * - * @param {ISpatialComponentsSearchCriteria} criteria - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async findSpatialComponentsByCriteria( - criteria: ISpatialComponentsSearchCriteria - ): Promise { - const knex = getKnex(); - - // non-admin rules: - // see the secured spatial component if it is not null (and if you do not have an exception to all security rules applied to it), - // otherwise you see the non-secured spatial component - - const queryBuilder = knex - .queryBuilder() - .with('distinct_geographic_points', this._withDistinctGeographicPoints) - .with('with_filtered_spatial_component_with_security_transforms', (qb1) => { - // Get the spatial components that match the search filters, and for each record, build the array of spatial security transforms that ran against that row - qb1 - .select( - knex.raw( - 'array_remove(array_agg(sts.security_transform_id), null) as spatial_component_security_transforms' - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, datasetID}' as dataset_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, taxonID}' as taxon_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, vernacularName}' as vernacular_name" - ), - 'ssc.submission_spatial_component_id', - 'ssc.submission_observation_id', - 'ssc.spatial_component', - 'ssc.secured_spatial_component', - 'ssc.geography' - ) - .from('submission_spatial_component as ssc') - .leftJoin('submission_observation as so', 'so.submission_observation_id', 'ssc.submission_observation_id') - .whereNull('so.record_end_timestamp') - .leftJoin('distinct_geographic_points as p', 'p.geography', 'ssc.geography') - .leftJoin( - 'security_transform_submission as sts', - 'sts.submission_spatial_component_id', - 'ssc.submission_spatial_component_id' - ) - .groupBy('sts.submission_spatial_component_id') - .groupBy('ssc.submission_spatial_component_id') - .groupBy('ssc.submission_observation_id') - .groupBy('ssc.spatial_component') - .groupBy('ssc.secured_spatial_component') - .groupBy('ssc.geography'); - if (criteria.type?.length) { - this._whereTypeIn(criteria.type, qb1); - } - if (criteria.species?.length) { - this._whereSpeciesIn(criteria.species, qb1); - } - - if (criteria.datasetID?.length) { - this._whereDatasetIDIn(criteria.datasetID, qb1); - } - - this._whereBoundaryIntersects(criteria.boundary, 'ssc.geography', qb1); - }) - .with('with_user_security_transform_exceptions', (qb6) => { - this._buildSpatialSecurityExceptions(qb6, this.connection.systemUserId()); - }) - .with('with_coalesced_spatial_components', (qb7) => { - qb7 - .select( - 'submission_spatial_component_id', - 'submission_observation_id', - 'geography', - this._buildSelectForSecureNonSecureSpatialComponents() - ) - .from( - knex.raw( - 'with_filtered_spatial_component_with_security_transforms as wfscwst, with_user_security_transform_exceptions as wuste' - ) - ); - }) - .select( - knex.raw('array_agg(submission_spatial_component_id) as submission_spatial_component_ids'), - knex.raw('array_agg(taxa_data_object) as taxa_data'), - knex.raw('(array_agg(spatial_component))[1] as spatial_component'), - 'geography' - ) - .from('with_coalesced_spatial_components') - // Filter out secure spatial components that have no spatial representation - // The user is not allowed to see any aspect of these particular spatial components - .whereRaw("spatial_component->'spatial_data' != '{}'") - .groupBy('geography'); - - const response = await this.connection.knex(queryBuilder); - - return response.rows; - } - - /** - * Append where clause condition for spatial component type. - * - * @param {string[]} types - * @param {Knex.QueryBuilder} qb1 - * @memberof SpatialRepository - */ - _whereTypeIn(types: string[], qb1: Knex.QueryBuilder) { - // Append AND where clause for types - qb1.where((qb2) => { - for (const type of types) { - // Append OR clause for each item in types array - qb2.or.where((qb3) => { - qb3.whereRaw(`jsonb_path_exists(spatial_component,'$.features[*] \\? (@.properties.type == "${type}")')`); - }); - } - }); - } - - /** - * Append where clause condition for spatial component taxonID. - * Append where clause condition for spatial component taxonID - * - * @param {string[]} species - * @param {Knex.QueryBuilder} qb1 - * @memberof SpatialRepository - */ - _whereSpeciesIn(species: string[], qb1: Knex.QueryBuilder) { - // Append AND where clause for species - qb1.where((qb2) => { - for (const singleSpecies of species) { - // Append OR clause for each item in species array - qb2.or.where((qb3) => { - qb3.whereRaw( - `jsonb_path_exists(spatial_component,'$.features[*] \\? (@.properties.dwc.taxonID == "${singleSpecies}")')` - ); - }); - } - }); - } - - /** - * Append where clause condition for spatial component parent dataset id. - * - * @param {string[]} datasetIDs - * @param {Knex.QueryBuilder} qb1 - * @memberof SpatialRepository - */ - _whereDatasetIDIn(datasetIDs: string[], qb1: Knex.QueryBuilder) { - qb1.where((qb2) => { - qb2.whereRaw( - `ssc.submission_observation_id in ( - select submission_observation_id from submission_observation so - left join submission s on so.submission_id = s.submission_id - where s.uuid in (${"'" + datasetIDs.join("','") + "'"}))` - ); - }); - } - - /** - * Append where clause condition for spatial component boundaries intersect. - * - * @param {Feature[]} boundaries - * @param {string} geoColumn - * @param {Knex.QueryBuilder} qb1 - * @memberof SpatialRepository - */ - _whereBoundaryIntersects(boundaries: Feature[], geoColumn: string, qb1: Knex.QueryBuilder) { - const generateSqlStatement = (geometry: Feature) => { - return SQL` - public.ST_INTERSECTS(`.append(`${geoColumn}`).append(`, - public.geography( - public.ST_Force2D( - public.ST_SetSRID( - public.ST_Force2D( - public.ST_GeomFromGeoJSON('${JSON.stringify(geometry.geometry)}') - ), - 4326 - ) - ) - ) - ) - `); - }; - - qb1.where((qb2) => { - for (const boundary of boundaries) { - // Append OR clause for each item in boundary array - qb2.or.where((qb3) => { - const sqlStatement = generateSqlStatement(boundary); - qb3.whereRaw(sqlStatement.sql, sqlStatement.values); - }); - } - }); - } - - _withDistinctGeographicPoints(qb1: Knex.QueryBuilder) { - qb1 - .distinct() - .select('geography') - .from('submission_spatial_component') - .whereRaw(`geometrytype(geography) = 'POINT'`) - .whereRaw(`jsonb_path_exists(spatial_component,'$.features[*] \\? (@.properties.type == "Occurrence")')`); - } - - /** - * Query builder to find spatial component from a given submission id, specifically for admin users that bypass all security - * rules. - * - * @param {number} submission_spatial_component_id - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin( - submission_spatial_component_ids: number[] - ): Promise { - const knex = getKnex(); - const queryBuilder = knex - .queryBuilder() - .with('with_filtered_spatial_component', (qb1) => { - // Get the spatial components that match the search filters - qb1 - .select() - .from('submission_spatial_component as ssc') - // Filter out submission_spatial_component records for end dated submission_observation records - .leftJoin('submission_observation as so', 'so.submission_observation_id', 'ssc.submission_observation_id') - .where('so.record_end_timestamp', null) - .whereIn('submission_spatial_component_id', submission_spatial_component_ids); - }) - .select( - // Select the non-secure spatial component from the search results - knex.raw( - `jsonb_array_elements(wfsc.spatial_component -> 'features') #> '{properties}' as spatial_component_properties` - ) - ) - .from(knex.raw('with_filtered_spatial_component as wfsc')); - - const response = await this.connection.knex(queryBuilder); - - return response.rows; - } - - /** - * Query builder to find spatial component from a given submission id, no security - * - * @param {number[]} submission_spatial_component_ids - * @return {*} {Promise} - * @memberof SpatialRepository - */ - async findSpatialMetadataBySubmissionSpatialComponentIds( - submission_spatial_component_ids: number[] - ): Promise { - const knex = getKnex(); - const queryBuilder = knex - .queryBuilder() - .with('with_filtered_spatial_component_with_security_transforms', (qb1) => { - // Get the spatial components that match the search filters, and for each record, build the array of spatial security transforms that ran against that row - qb1 - .select( - knex.raw( - 'array_remove(array_agg(sts.security_transform_id), null) as spatial_component_security_transforms' - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, datasetID}' as dataset_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, taxonID}' as taxon_id" - ), - knex.raw( - "jsonb_array_elements(ssc.spatial_component -> 'features') #> '{properties, dwc, vernacularName}' as vernacular_name" - ), - 'ssc.submission_spatial_component_id', - 'ssc.submission_observation_id', - 'ssc.spatial_component', - 'ssc.secured_spatial_component' - ) - .from('submission_spatial_component as ssc') - // Filter out submission_spatial_component records for end dated submission_observation records - .leftJoin('submission_observation as so', 'so.submission_observation_id', 'ssc.submission_observation_id') - .where('so.record_end_timestamp', null) - .leftJoin( - 'security_transform_submission as sts', - 'sts.submission_spatial_component_id', - 'ssc.submission_spatial_component_id' - ) - .whereIn('ssc.submission_spatial_component_id', submission_spatial_component_ids) - .groupBy('sts.submission_spatial_component_id') - .groupBy('ssc.submission_spatial_component_id') - .groupBy('ssc.submission_observation_id') - .groupBy('ssc.spatial_component') - .groupBy('ssc.secured_spatial_component'); - }) - .with('with_user_security_transform_exceptions', (qb6) => { - this._buildSpatialSecurityExceptions(qb6, this.connection.systemUserId()); - }) - .with('with_coalesced_spatial_components', (qb7) => { - qb7 - .select(this._buildSelectForSecureNonSecureSpatialComponents()) - .from( - knex.raw( - 'with_filtered_spatial_component_with_security_transforms as wfscwst, with_user_security_transform_exceptions as wuste' - ) - ); - }) - .select( - knex.raw( - `jsonb_array_elements(spatial_component -> 'spatial_data' -> 'features') #> '{properties}' as spatial_component_properties` - ) - ) - .from('with_coalesced_spatial_components') - // Filter out secure spatial components that have no spatial representation - // The user is not allowed to see any aspect of these particular spatial components - .whereRaw("spatial_component->'spatial_data' != '{}'"); - - const spatialComponentResponse = await this.connection.knex(queryBuilder); - - return spatialComponentResponse.rows; - } - - /** - * Select either the non-secure or secure spatial component from the search results, - * based on whether or not the record had security transforms applied to it and whether or not the user has the necessary exceptions - * - * @param {Knex} knex - * @return {*} { Knex.Raw } - * @memberof SpatialRepository - */ - _buildSelectForSecureNonSecureSpatialComponents(): Knex.Raw { - const knex = getKnex(); - return knex.raw( - ` - jsonb_build_object( - 'submission_spatial_component_id', - wfscwst.submission_spatial_component_id, - 'taxon_id', - wfscwst.taxon_id, - 'vernacular_name', - wfscwst.vernacular_name - ) taxa_data_object, - jsonb_build_object( - 'spatial_data', - -- when: the user's security transform ids array contains all of the rows security transform ids (user has all necessary exceptions) - -- then: return the spatial component - -- else: return the secure spatial component if it is not null (secure, insufficient exceptions), otherwise return the spatial component (non-secure, no exceptions required) - case - when - wuste.user_security_transform_exceptions @> wfscwst.spatial_component_security_transforms - then - wfscwst.spatial_component - else - coalesce(wfscwst.secured_spatial_component, wfscwst.spatial_component) - end - ) spatial_component - ` - ); - } - - /** - * Build an array of the users spatial security transform exceptions - * - * @param {Knex} knex - * @param {Knex.QueryBuilder} qb - * @param {number} system_user_id - * @memberof SpatialRepository - */ - async _buildSpatialSecurityExceptions(qb: Knex.QueryBuilder, system_user_id: number) { - const knex = getKnex(); - qb.select(knex.raw('array_remove(array_agg(st.security_transform_id), null) as user_security_transform_exceptions')) - .from('system_user_security_exception as suse') - .leftJoin('security_transform as st', 'st.persecution_or_harm_id', 'suse.persecution_or_harm_id') - .where('suse.system_user_id', system_user_id) - .and.whereRaw('(suse.end_date is null or now() < suse.end_date)'); - } - - /** - * // TODO - The SQL is outdated - `submission_id` no longer exists on table `submission_spatial_component` - * - * Deletes spatial components in a submission id before updating it with new data - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialRepository - */ - async deleteSpatialComponentsBySubmissionId(submission_id: number): Promise<{ submission_id: number }[]> { - const sqlStatement = SQL` - DELETE FROM - submission_spatial_component - WHERE - submission_id=${submission_id} - RETURNING - submission_id; - ;`; - - const response = await this.connection.sql<{ submission_id: number }>(sqlStatement); - - return response.rows; - } - - /** - * // TODO - The SQL is outdated - `submission_id` no longer exists on table `submission_spatial_component` - * - * Remove references in spatial_transform_submission table - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialRepository - */ - async deleteSpatialComponentsSpatialTransformRefsBySubmissionId( - submission_id: number - ): Promise<{ submission_id: number }[]> { - const sqlStatement = SQL` - DELETE FROM - spatial_transform_submission - WHERE - submission_spatial_component_id IN ( - SELECT - submission_spatial_component_id - FROM - submission_spatial_component - WHERE - submission_id=${submission_id} - ) - RETURNING - ${submission_id}; - `; - - const response = await this.connection.sql<{ submission_id: number }>(sqlStatement); - - return response.rows; - } - - /** - * // TODO - The SQL is outdated - `submission_id` no longer exists on table `submission_spatial_component` - * - * Remove references in security_transform_submission table - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialRepository - */ - async deleteSpatialComponentsSecurityTransformRefsBySubmissionId( - submission_id: number - ): Promise<{ submission_id: number }[]> { - const sqlStatement = SQL` - DELETE FROM - security_transform_submission - WHERE - submission_spatial_component_id IN ( - SELECT - submission_spatial_component_id - FROM - submission_spatial_component - WHERE - submission_id=${submission_id} - ) - RETURNING - ${submission_id}; - `; - - const response = await this.connection.sql<{ submission_id: number }>(sqlStatement); - - return response.rows; - } - - /** - * Returns the geometry of a `Boundary` or `Boundary Centroid` type spatial component for a given submission id, in - * WKT format using the provided `srid`. - * - * @param {number} submissionId A submission id - * @param {(SPATIAL_COMPONENT_TYPE.BOUNDARY | SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID)} spatialComponentType - * @param {Srid} srid The id of the projection used when converting the geography to WKT - * @return {*} - * @memberof SpatialRepository - */ - async getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId: number, - spatialComponentType: SPATIAL_COMPONENT_TYPE.BOUNDARY | SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, - srid: Srid - ) { - const knex = getKnex(); - - // Fetch the spatial component - const queryBuilder = knex - .queryBuilder() - .select(knex.raw(`ST_AsText(ST_TRANSFORM(geography::geometry, ${srid})) as geometry`)) - .from('submission_spatial_component as ssc') - .leftJoin('submission_observation as so', 'so.submission_observation_id', 'ssc.submission_observation_id') - .where('so.record_end_timestamp', null) - .where('so.record_effective_timestamp', 'is not', null) - .where('so.submission_id', submissionId) - .whereRaw( - `jsonb_path_exists(spatial_component,'$.features[*] \\? (@.properties.type == "${spatialComponentType}")')` - ); - - const response = await this.connection.knex(queryBuilder, z.object({ geometry: z.string() })); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to get boundary spatial component', [ - 'SpatialRepository->getBoundarySpatialComponentBySubmissionId', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - - return response.rows[0]; - } -} diff --git a/api/src/repositories/submission-job-queue-repository.ts b/api/src/repositories/submission-job-queue-repository.ts index 26ca85dc6..5c1d2565e 100644 --- a/api/src/repositories/submission-job-queue-repository.ts +++ b/api/src/repositories/submission-job-queue-repository.ts @@ -89,32 +89,6 @@ export class SubmissionJobQueueRepository extends BaseRepository { return { queueId: response.rows[0].nextval }; } - /** - * Finds a transform source Id based for a particular user - * - * @param {number} userId - * @return {*} {Promise} - * @memberof SubmissionJobQueueRepository - */ - async getSourceTransformIdForUserId(userId: number): Promise { - const sqlStatement = SQL` - SELECT source_transform_id - FROM source_transform - WHERE system_user_id = ${userId}; - `; - - const response = await this.connection.sql<{ source_transform_id: number }>(sqlStatement); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to get source transform Id', [ - 'SubmissionJobQueueRepository->getSourceTransformIdForUserId', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - - return response.rows[0].source_transform_id; - } - /** * Fetch the next available job queue record(s). * diff --git a/api/src/repositories/submission-repository.test.ts b/api/src/repositories/submission-repository.test.ts index 9c9e0a777..79e98b991 100644 --- a/api/src/repositories/submission-repository.test.ts +++ b/api/src/repositories/submission-repository.test.ts @@ -5,7 +5,6 @@ import { QueryResult } from 'pg'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; import { ApiExecuteSQLError, ApiGeneralError } from '../errors/api-error'; -import { EMLFile } from '../utils/media/eml/eml-file'; import { getMockDBConnection } from '../__mocks__/db'; import { SECURITY_APPLIED_STATUS } from './security-repository'; import { @@ -64,89 +63,6 @@ describe('SubmissionRepository', () => { }); }); - describe('updateSubmissionMetadataEMLSource', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when update sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - try { - await submissionRepository.updateSubmissionMetadataEMLSource(1, 1, { - emlFile: Buffer.from('') - } as unknown as EMLFile); - - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to update submission Metadata source'); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_metadata_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionMetadataEMLSource(1, 1, { - emlFile: Buffer.from('') - } as unknown as EMLFile); - - expect(response.submission_metadata_id).to.equal(1); - }); - }); - - describe('updateSubmissionMetadataEMLJSONSource', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when update sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - try { - await submissionRepository.updateSubmissionMetadataEMLJSONSource(1, 1, 'string'); - - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to update submission Metadata eml json'); - } - }); - - it('should succeed with valid data', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ submission_metadata_id: 1 }] } as any as Promise< - QueryResult - >; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionMetadataEMLJSONSource(1, 1, 'string'); - - expect(response.submission_metadata_id).to.equal(1); - }); - }); - describe('getSubmissionRecordBySubmissionId', () => { afterEach(() => { sinon.restore(); @@ -227,26 +143,6 @@ describe('SubmissionRepository', () => { }); }); - describe('getSubmissionRecordEMLJSONByDatasetId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should return a query result', async () => { - const mockQueryResponse = { rowCount: 0, rows: [] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.getSubmissionRecordEMLJSONByDatasetId('111-222-333'); - - expect(response).to.equal(mockQueryResponse); - }); - }); - describe('getSpatialComponentCountByDatasetId', () => { afterEach(() => { sinon.restore(); @@ -323,43 +219,6 @@ describe('SubmissionRepository', () => { }); }); - describe('getSubmissionMetadataJson', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ query: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - try { - await submissionRepository.getSubmissionMetadataJson(1, 'transform sql'); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to transform submission eml to json'); - } - }); - - it('should succeed with valid data, without optional version parameter', async () => { - const mockResponse = { - result_data: 'transformed eml' - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ query: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.getSubmissionMetadataJson(1, 'transform sql'); - - expect(response).to.eql('transformed eml'); - }); - }); - describe('getSourceTransformRecordBySourceTransformId', () => { afterEach(() => { sinon.restore(); @@ -525,79 +384,6 @@ describe('SubmissionRepository', () => { }); }); - describe('listSubmissionRecords', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - submission_status: 'Submission Data Ingested', - submission_id: 1, - source_transform_id: 'SIMS', - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - record_effective_date: '2022-05-24T18:41:42.211Z', - delete_timestamp: null, - input_key: 'biohub/1/moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - input_file_name: 'moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - eml_source: null, - eml_json_source: null, - darwin_core_source: 'test', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - update_date: '2022-05-24T18:41:42.056Z', - update_user: 15, - revision_count: 1 - }; - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: async () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.listSubmissionRecords(); - - expect(response).to.eql([mockResponse]); - }); - }); - - describe('getSourceTransformRecordBySubmissionId', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - try { - await submissionRepository.getSourceTransformRecordBySubmissionId(1); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to get submission source transform record'); - } - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - source_transform_id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.getSourceTransformRecordBySubmissionId(1); - - expect(response).to.eql(mockResponse); - }); - }); - describe('getSubmissionJobQueue', () => { afterEach(() => { sinon.restore(); @@ -637,47 +423,6 @@ describe('SubmissionRepository', () => { }); }); - describe('insertSubmissionMetadataRecord', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const submissionData = { submission_id: 1, eml_source: '', eml_json_source: '' }; - - try { - await submissionRepository.insertSubmissionMetadataRecord(submissionData); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to insert submission metadata record'); - } - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const submissionData = { submission_id: 1, eml_source: '', eml_json_source: '' }; - - const response = await submissionRepository.insertSubmissionMetadataRecord(submissionData); - - expect(response).to.eql(mockResponse); - }); - }); - describe('insertSubmissionObservationRecord', () => { afterEach(() => { sinon.restore(); @@ -728,165 +473,6 @@ describe('SubmissionRepository', () => { }); }); - describe('updateSubmissionMetadataRecordEndDate', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionMetadataRecordEndDate(1); - - expect(response).to.eql(1); - }); - }); - - describe('updateSubmissionMetadataRecordEffectiveDate', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - try { - await submissionRepository.updateSubmissionMetadataRecordEffectiveDate(1); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal( - 'Failed to update record_effective_timestamp submission metadata record' - ); - } - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionMetadataRecordEffectiveDate(1); - - expect(response).to.eql(1); - }); - }); - - describe('updateSubmissionObservationRecordEndDate', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionObservationRecordEndDate(1); - - expect(response).to.eql(1); - }); - }); - - describe('updateSubmissionMetadataWithSearchKeys', () => { - beforeEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.updateSubmissionMetadataWithSearchKeys(1, ''); - - expect(response).to.eql(1); - }); - }); - - describe('getArtifactForReviewCountForSubmissionUUID', () => { - beforeEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - dataset_id: 'UUID', - submission_id: 1, - artifacts_to_review: 1, - last_updated: '2023-05-25' - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.getArtifactForReviewCountForSubmissionUUID(''); - - expect(response).to.eql(mockResponse); - }); - }); - - describe('getDatasetsForReview', () => { - beforeEach(() => { - sinon.restore(); - }); - - it('should succeed with valid data', async () => { - const mockResponse = [ - { - dataset_id: 'UUID', - submission_id: 1, - submitter_system: 'sims', - dataset_name: 'Project Name', - keywords: [], - related_projects: [] - } - ]; - - const mockQueryResponse = { rowCount: 1, rows: mockResponse } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ knex: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const response = await submissionRepository.getDatasetsForReview(['']); - - expect(response).to.eql(mockResponse); - }); - }); - describe('getUnreviewedSubmissionsForAdmins', () => { beforeEach(() => { sinon.restore(); diff --git a/api/src/repositories/submission-repository.ts b/api/src/repositories/submission-repository.ts index 0c1c4fd25..6d45bcc78 100644 --- a/api/src/repositories/submission-repository.ts +++ b/api/src/repositories/submission-repository.ts @@ -1,25 +1,11 @@ import { Knex } from 'knex'; -import { QueryResult } from 'pg'; import SQL from 'sql-template-strings'; import { z } from 'zod'; import { getKnex, getKnexQueryBuilder } from '../database/db'; import { ApiExecuteSQLError } from '../errors/api-error'; -import { EMLFile } from '../utils/media/eml/eml-file'; import { BaseRepository } from './base-repository'; import { SECURITY_APPLIED_STATUS } from './security-repository'; -export interface IHandlebarsTemplates { - header: string; - details: string; -} -export interface IDatasetsForReview { - dataset_id: string; // UUID - artifacts_to_review: number; - dataset_name: string; - last_updated: string; - keywords: string[]; -} - export interface ISubmissionFeature { id: string | null; type: string; @@ -27,29 +13,6 @@ export interface ISubmissionFeature { child_features: ISubmissionFeature[]; } -export const DatasetMetadata = z.object({ - dataset_id: z.string(), - submission_id: z.number(), - dataset_name: z.string(), - keywords: z.array(z.string()), - related_projects: z - .array(z.any()) - .nullable() - .optional() - .transform((item) => item || []) -}); - -export type DatasetMetadata = z.infer; - -export const DatasetArtifactCount = z.object({ - dataset_id: z.string(), - submission_id: z.number(), - artifacts_to_review: z.number(), - last_updated: z.string().nullable() -}); - -export type DatasetArtifactCount = z.infer; - export interface ISpatialComponentCount { spatial_type: string; count: number; @@ -503,84 +466,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Update the `eml_source` column of a submission record. - * - * @param {number} submissionId - * @param {number} submissionMetadataId - * @param {EMLFile} file - * @return {*} {Promise<{ submission_metadata_id: number }>} - * @memberof SubmissionRepository - */ - async updateSubmissionMetadataEMLSource( - submissionId: number, - submissionMetadataId: number, - file: EMLFile - ): Promise<{ submission_metadata_id: number }> { - const sqlStatement = SQL` - UPDATE - submission_metadata - SET - eml_source = ${file.emlFile.buffer.toString()} - WHERE - submission_id = ${submissionId} - AND - submission_metadata_id =${submissionMetadataId} - RETURNING - submission_metadata_id; - `; - - const response = await this.connection.sql<{ submission_metadata_id: number }>(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to update submission Metadata source', [ - 'SubmissionRepository->updateSubmissionMetadataEMLSource', - 'rowCount was null or undefined, expected rowCount != 0' - ]); - } - - return response.rows[0]; - } - - /** - * Update the `eml_json_source` column of a submission metadata. - * - * @param {number} submissionId - * @param {number} submissionMetadataId - * @param {ISubmissionMetadataRecord['eml_json_source']} EMLJSONSource - * @return {*} {Promise<{ submission_metadata_id: number }>} - * @memberof SubmissionRepository - */ - async updateSubmissionMetadataEMLJSONSource( - submissionId: number, - submissionMetadataId: number, - EMLJSONSource: ISubmissionMetadataRecord['eml_json_source'] - ): Promise<{ submission_metadata_id: number }> { - const sqlStatement = SQL` - UPDATE - submission_metadata - SET - eml_json_source = ${EMLJSONSource} - WHERE - submission_id = ${submissionId} - AND - submission_metadata_id =${submissionMetadataId} - RETURNING - submission_metadata_id; - `; - - const response = await this.connection.sql<{ submission_metadata_id: number }>(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to update submission Metadata eml json', [ - 'SubmissionRepository->updateSubmissionMetadataEMLJSONSource', - 'rowCount was null or undefined, expected rowCount != 0' - ]); - } - - return response.rows[0]; - } - /** * Fetch a submission record by primary id. * @@ -635,29 +520,6 @@ export class SubmissionRepository extends BaseRepository { } } - /** - * Get submission eml json by dataset id. - * - * @param {string} datasetId - * @return {*} {Promise }>>} - * @memberof SubmissionRepository - */ - async getSubmissionRecordEMLJSONByDatasetId( - datasetId: string - ): Promise }>> { - const sqlStatement = SQL` - SELECT - sm.eml_json_source - FROM submission s, submission_metadata sm - WHERE s.submission_id = sm.submission_id - AND sm.record_end_timestamp IS NULL - AND sm.record_effective_timestamp IS NOT NULL - AND s.uuid = ${datasetId}; - `; - - return this.connection.sql<{ eml_json_source: Record }>(sqlStatement); - } - /** * Get spatial component counts by dataset id * @@ -716,27 +578,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Fetch a submissions metadata json representation. - * - * @param {number} sourceTransformId - * @param {string} transform - * @return {*} {Promise} - * @memberof SubmissionRepository - */ - async getSubmissionMetadataJson(submissionId: number, transform: string): Promise { - const response = await this.connection.query<{ result_data: any }>(transform, [submissionId]); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to transform submission eml to json', [ - 'SubmissionRepository->getSubmissionMetadataJson', - 'rowCount was null or undefined, expected rowCount != 0' - ]); - } - - return response.rows[0].result_data; - } - /** * Fetch a submission source transform record by associated source transform id. * @@ -865,74 +706,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Fetch a submission record by primary id. - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionRepository - */ - async listSubmissionRecords(): Promise { - const sqlStatement = SQL` - SELECT - t1.submission_status, - s.* - FROM - submission s - LEFT JOIN - (SELECT DISTINCT ON (ss.submission_id) - ss.submission_id, - sst.name AS submission_status - FROM - submission_status ss - LEFT JOIN - submission_status_type sst - ON - ss.submission_status_type_id = sst.submission_status_type_id - ORDER BY - ss.submission_id, ss.submission_status_id DESC) t1 - ON - t1.submission_id = s.submission_id; - `; - - const response = await this.connection.sql(sqlStatement); - - return response.rows; - } - - /** - * Fetch a submission source transform record by associated source system user id. - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionRepository - */ - async getSourceTransformRecordBySubmissionId(submissionId: number): Promise { - const sqlStatement = SQL` - SELECT - * - FROM - source_transform st - LEFT JOIN - submission s - ON - st.source_transform_id = s.source_transform_id - WHERE - s.submission_id = ${submissionId}; - `; - - const response = await this.connection.sql(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to get submission source transform record', [ - 'SubmissionRepository->getSourceTransformRecordBySubmissionId', - 'rowCount was null or undefined, expected rowCount != 0' - ]); - } - - return response.rows[0]; - } - /** * Fetch row of submission job queue by submission Id * @@ -963,43 +736,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Insert a new metadata record - * - * @param {ISubmissionMetadataRecord} submissionMetadata - * @return {*} {Promise<{ submission_metadata_id: number }>} - * @memberof SubmissionRepository - */ - async insertSubmissionMetadataRecord( - submissionMetadata: ISubmissionMetadataRecord - ): Promise<{ submission_metadata_id: number }> { - const sqlStatement = SQL` - INSERT INTO submission_metadata ( - submission_id, - eml_source, - eml_json_source - ) VALUES ( - ${submissionMetadata.submission_id}, - ${submissionMetadata.eml_source}, - ${submissionMetadata.eml_json_source} - ) - RETURNING - submission_metadata_id - ; - `; - - const response = await this.connection.sql<{ submission_metadata_id: number }>(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to insert submission metadata record', [ - 'SubmissionRepository->insertSubmissionMetadataRecord', - 'rowCount was null or undefined, expected rowCount >= 0' - ]); - } - - return response.rows[0]; - } - /** * Insert a new Observation Record * @@ -1041,173 +777,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Update record_end_timestamp of submission id - * - * @param {number} submissionId - * @return {*} {Promise<{ submission_id: number }>} - * @memberof SubmissionRepository - */ - async updateSubmissionMetadataRecordEndDate(submissionId: number): Promise { - const sqlStatement = SQL` - UPDATE - submission_metadata - SET - record_end_timestamp = now() - WHERE - submission_id = ${submissionId} - AND - record_end_timestamp IS NULL - AND - record_effective_timestamp IS NOT NULL - ; - `; - - const response = await this.connection.sql(sqlStatement); - - return response.rowCount; - } - - /** - * Update start time stamp of submission metadata record - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionRepository - */ - async updateSubmissionMetadataRecordEffectiveDate(submissionId: number): Promise { - const sqlStatement = SQL` - UPDATE - submission_metadata - SET - record_effective_timestamp = now() - WHERE - submission_id = ${submissionId} - AND - record_effective_timestamp IS NULL - AND - record_end_timestamp IS NULL - ; - `; - - const response = await this.connection.sql(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to update record_effective_timestamp submission metadata record', [ - 'SubmissionRepository->updateSubmissionMetadataRecordEffectiveDate', - 'rowCount was null or undefined, expected rowCount >= 0' - ]); - } - - return response.rowCount; - } - - /** - * Update end time stamp for submission observation record - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionRepository - */ - async updateSubmissionObservationRecordEndDate(submissionId: number): Promise { - const sqlStatement = SQL` - UPDATE - submission_observation - SET - record_end_timestamp = now() - WHERE - submission_id = ${submissionId} - AND - record_end_timestamp IS NULL - AND - record_effective_timestamp IS NOT NULL - ; - `; - - const response = await this.connection.sql(sqlStatement); - - return response.rowCount; - } - - /** - * - * @param submissionId the submission to update - * @param datasetSearch - * @returns {*} {Promise} the number of rows updated - * @memberof SubmissionRepository - */ - async updateSubmissionMetadataWithSearchKeys(submissionId: number, datasetSearch: any): Promise { - const sql = SQL` - UPDATE - submission_metadata - SET - dataset_search_criteria=${datasetSearch} - WHERE submission_id = ${submissionId} - AND record_end_timestamp IS NULL - AND record_effective_timestamp IS NOT NULL; - `; - - const response = await this.connection.sql(sql); - - return response.rowCount; - } - - /** - * Gets datasets that have artifacts that require a security review. - * - * @param keywordFilter A list of keys to filter the data based on search criteria defined by the transform process - * @returns {*} {Promise} - */ - async getDatasetsForReview(keywordFilter: string[]): Promise { - const knex = getKnex(); - const queryBuilder = knex - .queryBuilder() - .select( - 's.uuid as dataset_id', - 'sm.submission_id', - knex.raw(`sm.eml_json_source::json->'eml:eml'->'dataset'->>'title' as dataset_name`), - knex.raw(`sm.dataset_search_criteria::json->'primaryKeywords' as keywords`), - knex.raw(`sm.eml_json_source::json->'eml:eml'->'dataset'->'project'->'relatedProject' as related_projects`) - ) - .from('submission as s') - .leftJoin('submission_metadata as sm', 'sm.submission_id', 's.submission_id') - .whereNull('sm.record_end_timestamp') - // the ?| operator does a containment check meaning it will check if any elements in the left side array exist in the right side array - .whereRaw( - `(sm.dataset_search_criteria->'primaryKeywords')::jsonb \\?| array[${"'" + keywordFilter.join("','") + "'"}]` - ); - - const response = await this.connection.knex(queryBuilder, DatasetMetadata); - - return response.rows; - } - - /** - * Gets a count of all artifacts for a given submission UUID. - * - * @param uuid UUID of the submission to look for - * @returns {*} Promise - */ - async getArtifactForReviewCountForSubmissionUUID(uuid: string): Promise { - const knex = getKnex(); - const queryBuilder = knex - .queryBuilder() - .select( - 's.uuid as dataset_id', - 's.submission_id', - knex.raw(`COUNT(a.artifact_id)::int as artifacts_to_review`), - knex.raw(`MAX(a.create_date)::date as last_updated`) - ) - .from('submission as s') - .leftJoin('artifact as a', 'a.submission_id', 's.submission_id') - .whereNull('a.security_review_timestamp') - .where('s.uuid', uuid) - .groupBy(['s.submission_id', 's.uuid']); - const response = await this.connection.knex(queryBuilder, DatasetArtifactCount); - - return response.rows[0]; - } - /** * Get all submissions that have not completed security review. * diff --git a/api/src/services/spatial-service.test.ts b/api/src/services/spatial-service.test.ts deleted file mode 100644 index a13876b43..000000000 --- a/api/src/services/spatial-service.test.ts +++ /dev/null @@ -1,525 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { SYSTEM_ROLE } from '../constants/roles'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { - IGetSecurityTransformRecord, - IGetSpatialTransformRecord, - IInsertSpatialTransform, - ISpatialComponentFeaturePropertiesRow, - ISpatialComponentsSearchCriteria, - ISubmissionSpatialSearchResponseRow, - SpatialRepository -} from '../repositories/spatial-repository'; -import { SystemUserExtended } from '../repositories/user-repository'; -import { getMockDBConnection } from '../__mocks__/db'; -import { Srid3005 } from './geo-service'; -import { SpatialService } from './spatial-service'; -import { UserService } from './user-service'; - -chai.use(sinonChai); - -describe('SpatialService', () => { - afterEach(() => { - sinon.restore(); - }); - - describe('insertSpatialTransform', () => { - it('should return spatial_transform_id on insert', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const repo = sinon - .stub(SpatialRepository.prototype, 'insertSpatialTransform') - .resolves({ spatial_transform_id: 1 }); - - const response = await spatialService.insertSpatialTransform({} as unknown as IInsertSpatialTransform); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ spatial_transform_id: 1 }); - }); - }); - - describe('getSpatialTransformRecords', () => { - it('should return IGetSpatialTransformRecord on get', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const repo = sinon - .stub(SpatialRepository.prototype, 'getSpatialTransformRecords') - .resolves([{ name: 'name' }] as unknown as IGetSpatialTransformRecord[]); - - const response = await spatialService.getSpatialTransformRecords(); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([{ name: 'name' }]); - }); - }); - - describe('getSecurityTransformRecords', () => { - it('should return IGetSecurityTransformRecord on get', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const repo = sinon - .stub(SpatialRepository.prototype, 'getSecurityTransformRecords') - .resolves([{ name: 'name' }] as unknown as IGetSecurityTransformRecord[]); - - const response = await spatialService.getSecurityTransformRecords(); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([{ name: 'name' }]); - }); - }); - - describe('insertSpatialTransformSubmissionRecord', () => { - it('should return spatial_transform_submission_id after insert', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const repo = sinon - .stub(SpatialRepository.prototype, 'insertSpatialTransformSubmissionRecord') - .resolves({ spatial_transform_submission_id: 1 }); - - const response = await spatialService.insertSpatialTransformSubmissionRecord(1, 1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ spatial_transform_submission_id: 1 }); - }); - }); - - describe('insertSecurityTransformSubmissionRecord', () => { - it('should return security_transform_submission_id after insert', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const repo = sinon - .stub(SpatialRepository.prototype, 'insertSecurityTransformSubmissionRecord') - .resolves({ security_transform_submission_id: 1 }); - - const response = await spatialService.insertSecurityTransformSubmissionRecord(1, 1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ security_transform_submission_id: 1 }); - }); - }); - - describe('findSpatialComponentsByCriteria', () => { - it('should return spatial component search result rows', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows = [ - { - spatial_component: { - spatial_data: {}, - submission_spatial_component_id: 1 - } - }, - { - spatial_component: { - spatial_data: {}, - submission_spatial_component_id: 2 - } - } - ] as unknown as ISubmissionSpatialSearchResponseRow[]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteria') - .resolves(mockResponseRows); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialService.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponseRows); - }); - - it('should call findSpatialComponentsByCriteriaAsAdminUser as data admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.DATA_ADMINISTRATOR] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSpatialComponentsByCriteriaAsAdminUserStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteriaAsAdminUser') - .resolves(); - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteria') - .resolves(); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - await spatialService.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(findSpatialComponentsByCriteriaAsAdminUserStub).to.be.calledOnce; - expect(findSpatialComponentsByCriteriaStub).not.to.have.been.called; - }); - - it('should call findSpatialComponentsByCriteriaAsAdminUser as system admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSpatialComponentsByCriteriaAsAdminUserStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteriaAsAdminUser') - .resolves(); - const findSpatialComponentsByCriteriaStub = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteria') - .resolves(); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - await spatialService.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(findSpatialComponentsByCriteriaAsAdminUserStub).to.be.calledOnce; - expect(findSpatialComponentsByCriteriaStub).not.to.have.been.called; - }); - - it('should return spatial component search result rows', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows = [ - { - spatial_component: { - spatial_data: {}, - submission_spatial_component_id: 1 - } - }, - { - spatial_component: { - spatial_data: {}, - submission_spatial_component_id: 2 - } - } - ] as unknown as ISubmissionSpatialSearchResponseRow[]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialComponentsByCriteria') - .resolves(mockResponseRows); - - const mockSearchCriteria: ISpatialComponentsSearchCriteria = { - type: ['Occurrence'], - boundary: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[]] } }] - }; - - const response = await spatialService.findSpatialComponentsByCriteria(mockSearchCriteria); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponseRows); - }); - }); - - describe('deleteSpatialComponentsBySubmissionId', () => { - it('should return submission IDs upon deleting spatial data', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const mockResponseRows = [{ submission_id: 3 }] as unknown as { submission_id: number }[]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'deleteSpatialComponentsBySubmissionId') - .resolves(mockResponseRows); - - const response = await spatialService.deleteSpatialComponentsBySubmissionId(3); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponseRows); - }); - }); - - describe('deleteSpatialComponentsSpatialTransformRefsBySubmissionId', () => { - it('should return submission IDs upon deleting spatial data', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const mockResponseRows = [{ submission_id: 3 }] as unknown as { submission_id: number }[]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'deleteSpatialComponentsSpatialTransformRefsBySubmissionId') - .resolves(mockResponseRows); - - const response = await spatialService.deleteSpatialComponentsSpatialTransformRefsBySubmissionId(3); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponseRows); - }); - }); - - describe('deleteSpatialComponentsSecurityTransformRefsBySubmissionId', () => { - it('should return submission IDs upon deleting security data', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const mockResponseRows = [{ submission_id: 3 }] as unknown as { submission_id: number }[]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'deleteSpatialComponentsSecurityTransformRefsBySubmissionId') - .resolves(mockResponseRows); - - const response = await spatialService.deleteSpatialComponentsSecurityTransformRefsBySubmissionId(3); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponseRows); - }); - }); - - describe('findSpatialMetadataBySubmissionSpatialComponentIds', () => { - describe('with multiple features', () => { - it('should return spatial component metadata', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows: ISpatialComponentFeaturePropertiesRow[] = [ - { - spatial_component_properties: { - prop1: 'val1', - prop2: 'val2' - } - } - ]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds') - .resolves(mockResponseRows); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([{ prop1: 'val1', prop2: 'val2' }]); - }); - }); - - describe('with single feature', () => { - it('should return spatial component metadata', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows: ISpatialComponentFeaturePropertiesRow[] = [ - { - spatial_component_properties: { - prop1: 'val1', - prop2: 'val2' - } - }, - { - spatial_component_properties: { - prop3: 'val3', - prop4: 'val4' - } - } - ]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds') - .resolves(mockResponseRows); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([ - { prop1: 'val1', prop2: 'val2' }, - { prop3: 'val3', prop4: 'val4' } - ]); - }); - }); - - describe('with single feature', () => { - it('should return spatial component metadata as system admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows: ISpatialComponentFeaturePropertiesRow[] = [ - { - spatial_component_properties: { - prop1: 'val1', - prop2: 'val2' - } - }, - { - spatial_component_properties: { - prop3: 'val3', - prop4: 'val4' - } - } - ]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin') - .resolves(mockResponseRows); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([ - { prop1: 'val1', prop2: 'val2' }, - { prop3: 'val3', prop4: 'val4' } - ]); - }); - - it('should return spatial component metadata as data admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.DATA_ADMINISTRATOR] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const mockResponseRows: ISpatialComponentFeaturePropertiesRow[] = [ - { - spatial_component_properties: { - prop1: 'val1', - prop2: 'val2' - } - }, - { - spatial_component_properties: { - prop3: 'val3', - prop4: 'val4' - } - } - ]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin') - .resolves(mockResponseRows); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([ - { prop1: 'val1', prop2: 'val2' }, - { prop3: 'val3', prop4: 'val4' } - ]); - }); - - it('should return non secure spatial component metadata when user is not admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(false); - - const mockResponseRows: ISpatialComponentFeaturePropertiesRow[] = [ - { - spatial_component_properties: { - prop1: 'val1', - prop2: 'val2' - } - }, - { - spatial_component_properties: { - prop3: 'val3', - prop4: 'val4' - } - } - ]; - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds') - .resolves(mockResponseRows); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([ - { prop1: 'val1', prop2: 'val2' }, - { prop3: 'val3', prop4: 'val4' } - ]); - }); - }); - - describe('with no features', () => { - it('should return [] as system admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin') - .resolves([]); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([]); - }); - - it('should return [] as data admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - const mockUserObject = { role_names: [SYSTEM_ROLE.DATA_ADMINISTRATOR] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin') - .resolves([]); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([]); - }); - - it('should return [] when user is not admin', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - sinon.stub(UserService.prototype, 'isSystemUserAdmin').resolves(false); - - const repo = sinon - .stub(SpatialRepository.prototype, 'findSpatialMetadataBySubmissionSpatialComponentIds') - .resolves([]); - - const response = await spatialService.findSpatialMetadataBySubmissionSpatialComponentIds([3]); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql([]); - }); - }); - }); - - describe('getGeometryAsWktFromBoundarySpatialComponentBySubmissionId', () => { - it('returns a geometry WKT string', async () => { - const mockDBConnection = getMockDBConnection(); - const spatialService = new SpatialService(mockDBConnection); - - const submissionId = 1; - - const mockResponse = { geometry: 'POLYGON(123,456,789)' }; - - const repo = sinon - .stub(SpatialRepository.prototype, 'getGeometryAsWktFromBoundarySpatialComponentBySubmissionId') - .resolves(mockResponse); - - const response = await spatialService.getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId, - SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, - Srid3005 - ); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponse); - }); - }); -}); diff --git a/api/src/services/spatial-service.ts b/api/src/services/spatial-service.ts deleted file mode 100644 index 15eadc16d..000000000 --- a/api/src/services/spatial-service.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { GeoJsonProperties } from 'geojson'; -import { SPATIAL_COMPONENT_TYPE } from '../constants/spatial'; -import { IDBConnection } from '../database/db'; -import { - IGetSecurityTransformRecord, - IGetSpatialTransformRecord, - IInsertSpatialTransform, - ISpatialComponentsSearchCriteria, - ISubmissionSpatialSearchResponseRow, - SpatialRepository -} from '../repositories/spatial-repository'; -import { DBService } from './db-service'; -import { Srid } from './geo-service'; -import { UserService } from './user-service'; - -export class SpatialService extends DBService { - spatialRepository: SpatialRepository; - - constructor(connection: IDBConnection) { - super(connection); - - this.spatialRepository = new SpatialRepository(connection); - } - - /** - * Insert new spatial transform record - * - * @param {IInsertSpatialTransform} spatialTransformDetails - * @return {*} {Promise<{ spatial_transform_id: number }>} - * @memberof SpatialService - */ - async insertSpatialTransform( - spatialTransformDetails: IInsertSpatialTransform - ): Promise<{ spatial_transform_id: number }> { - return this.spatialRepository.insertSpatialTransform(spatialTransformDetails); - } - - /** - * get spatial transform record from name - * - * @param {string} spatialTransformName - * @return {*} {Promise} - * @memberof SpatialService - */ - async getSpatialTransformRecords(): Promise { - return this.spatialRepository.getSpatialTransformRecords(); - } - - /** - * get security transform record from name - * - * @param {string} spatialTransformName - * @return {*} {Promise} - * @memberof SpatialService - */ - async getSecurityTransformRecords(): Promise { - return this.spatialRepository.getSecurityTransformRecords(); - } - - /** - * Insert record of transform id used for submission spatial component record - * - * @param {number} spatialTransformId - * @param {number} submissionSpatialComponentId - * @return {*} {Promise<{ spatial_transform_submission_id: number }>} - * @memberof SpatialService - */ - async insertSpatialTransformSubmissionRecord( - spatialTransformId: number, - submissionSpatialComponentId: number - ): Promise<{ spatial_transform_submission_id: number }> { - return this.spatialRepository.insertSpatialTransformSubmissionRecord( - spatialTransformId, - submissionSpatialComponentId - ); - } - - /** - * Insert record of transform id used for submission security component record - * - * @param {number} securityTransformId - * @param {number} submissionSpatialComponentId - * @return {*} {Promise<{ spatial_transform_submission_id: number }>} - * @memberof SpatialService - */ - async insertSecurityTransformSubmissionRecord( - securityTransformId: number, - submissionSpatialComponentId: number - ): Promise<{ security_transform_submission_id: number }> { - return this.spatialRepository.insertSecurityTransformSubmissionRecord( - securityTransformId, - submissionSpatialComponentId - ); - } - - /** - * Query builder to find spatial component by given criteria. - * - * Note: Returns an empty array of results if no matches are found. - * - * @param {ISpatialComponentsSearchCriteria} criteria - * @return {*} {Promise} - * @memberof SpatialService - */ - async findSpatialComponentsByCriteria( - criteria: ISpatialComponentsSearchCriteria - ): Promise { - const userService = new UserService(this.connection); - - if (await userService.isSystemUserAdmin()) { - return this.spatialRepository.findSpatialComponentsByCriteriaAsAdminUser(criteria); - } - - return this.spatialRepository.findSpatialComponentsByCriteria(criteria); - } - - /** - * Delete spatial component records by submission id. - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialService - */ - async deleteSpatialComponentsBySubmissionId(submission_id: number): Promise<{ submission_id: number }[]> { - return this.spatialRepository.deleteSpatialComponentsBySubmissionId(submission_id); - } - - /** - * Delete records referencing which spatial transforms were applied to a spatial component - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialService - */ - async deleteSpatialComponentsSpatialTransformRefsBySubmissionId( - submission_id: number - ): Promise<{ submission_id: number }[]> { - return this.spatialRepository.deleteSpatialComponentsSpatialTransformRefsBySubmissionId(submission_id); - } - - /** - * Delete records referencing which security transforms were applied to a spatial component - * - * @param {number} submission_id - * @return {*} {Promise<{ submission_id: number }[]>} - * @memberof SpatialService - */ - async deleteSpatialComponentsSecurityTransformRefsBySubmissionId( - submission_id: number - ): Promise<{ submission_id: number }[]> { - return this.spatialRepository.deleteSpatialComponentsSecurityTransformRefsBySubmissionId(submission_id); - } - - /** - * Query builder to find spatial component by given criteria - * - * @param {number[]} submissionSpatialComponentIds - * @return {*} {Promise} - * @memberof SpatialService - */ - async findSpatialMetadataBySubmissionSpatialComponentIds( - submissionSpatialComponentIds: number[] - ): Promise { - const userService = new UserService(this.connection); - - const response = (await userService.isSystemUserAdmin()) - ? this.spatialRepository.findSpatialMetadataBySubmissionSpatialComponentIdsAsAdmin(submissionSpatialComponentIds) - : this.spatialRepository.findSpatialMetadataBySubmissionSpatialComponentIds(submissionSpatialComponentIds); - - return (await response).map((row) => row.spatial_component_properties); - } - - /** - * Get the `Boundary` or `Boundary Centroid` spatial component for a specified dataset and return as WKT projected - * using the provided SRID. - * - * Note: Does not check roles or permissions. Should only be used for internal functionality. - * - * Note: Can be used as part of a CQL Filter when making WFS requests (see GeoService). - * - * @param {number} submissionId A submission id - * @param {(SPATIAL_COMPONENT_TYPE.BOUNDARY | SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID)} spatialComponentType - * @param {Srid} srid The id of the projection used when converting the geography to WKT - * @return {*} {Promise} - * @throws {Error} if no matches are found. - * @memberof SpatialService - */ - async getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId: number, - spatialComponentType: SPATIAL_COMPONENT_TYPE.BOUNDARY | SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID, - srid: Srid - ) { - return this.spatialRepository.getGeometryAsWktFromBoundarySpatialComponentBySubmissionId( - submissionId, - spatialComponentType, - srid - ); - } -} diff --git a/api/src/services/submission-job-queue-service.test.ts b/api/src/services/submission-job-queue-service.test.ts index 2b722b533..f4bc2279f 100644 --- a/api/src/services/submission-job-queue-service.test.ts +++ b/api/src/services/submission-job-queue-service.test.ts @@ -8,7 +8,6 @@ import { import * as FileUtils from '../utils/file-utils'; import { getMockDBConnection } from '../__mocks__/db'; import { SubmissionJobQueueService } from './submission-job-queue-service'; -import { SubmissionService } from './submission-service'; chai.use(sinonChai); @@ -36,64 +35,6 @@ describe('SubmissionJobQueueService', () => { }); }); - describe('getSourceTransformIdForUserId', () => { - it('should return an transform ID', async () => { - const mockDBConnection = getMockDBConnection(); - const service = new SubmissionJobQueueService(mockDBConnection); - const repo = sinon.stub(SubmissionJobQueueRepository.prototype, 'getSourceTransformIdForUserId').resolves(1); - - const response = await service.getSourceTransformIdForUserId(1); - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(1); - }); - }); - - describe('intake', () => { - it('should return queue id and create new submission', async () => { - const mockDBConnection = getMockDBConnection({ - systemUserId: () => { - return 1; - } - }); - const service = new SubmissionJobQueueService(mockDBConnection); - - sinon.stub(SubmissionJobQueueRepository.prototype, 'getNextQueueId').resolves({ queueId: 1 }); - sinon.stub(SubmissionJobQueueService.prototype, 'getSourceTransformIdForUserId').resolves(3); - sinon.stub(SubmissionService.prototype, 'getSubmissionIdByUUID').resolves(null); - sinon.stub(SubmissionJobQueueService.prototype, 'uploadDatasetToS3').resolves('key'); - const insert = sinon.stub(SubmissionService.prototype, 'insertSubmissionRecord').resolves({ submission_id: 1 }); - sinon.stub(SubmissionJobQueueService.prototype, 'createQueueJob').resolves({ queue_id: 1 }); - sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - const response = await service.intake('uuid', {} as unknown as Express.Multer.File); - - expect(response.queue_id).to.be.eql(1); - expect(insert).to.be.calledOnce; - }); - - it('should return queue id and update submission', async () => { - const mockDBConnection = getMockDBConnection({ - systemUserId: () => { - return 1; - } - }); - const service = new SubmissionJobQueueService(mockDBConnection); - - sinon.stub(SubmissionJobQueueRepository.prototype, 'getNextQueueId').resolves({ queueId: 1 }); - sinon.stub(SubmissionJobQueueService.prototype, 'getSourceTransformIdForUserId').resolves(3); - sinon.stub(SubmissionService.prototype, 'getSubmissionIdByUUID').resolves({ submission_id: 1 }); - sinon.stub(SubmissionJobQueueService.prototype, 'uploadDatasetToS3').resolves('key'); - const insert = sinon.stub(SubmissionService.prototype, 'insertSubmissionRecord').resolves({ submission_id: 1 }); - sinon.stub(SubmissionJobQueueService.prototype, 'createQueueJob').resolves({ queue_id: 1 }); - sinon.stub(SubmissionService.prototype, 'insertSubmissionStatusAndMessage').resolves(); - - const response = await service.intake('uuid', {} as unknown as Express.Multer.File); - - expect(response.queue_id).to.be.eql(1); - expect(insert).not.be.called; - }); - }); - describe('uploadDatasetToS3', () => { it('should create key and upload to S3', async () => { const mockDBConnection = getMockDBConnection(); diff --git a/api/src/services/submission-job-queue-service.ts b/api/src/services/submission-job-queue-service.ts index 4050df7b1..bc1fdfec3 100644 --- a/api/src/services/submission-job-queue-service.ts +++ b/api/src/services/submission-job-queue-service.ts @@ -3,10 +3,8 @@ import { ISubmissionJobQueueRecord, SubmissionJobQueueRepository } from '../repositories/submission-job-queue-repository'; -import { SUBMISSION_MESSAGE_TYPE, SUBMISSION_STATUS_TYPE } from '../repositories/submission-repository'; import { generateQueueS3FileKey, uploadFileToS3 } from '../utils/file-utils'; import { DBService } from './db-service'; -import { SubmissionService } from './submission-service'; export interface ISecurityRequest { first_nations_id: number; @@ -40,33 +38,9 @@ export class SubmissionJobQueueService extends DBService { file: Express.Multer.File, securityRequest?: ISecurityRequest ): Promise<{ queue_id: number }> { - const submissionService = new SubmissionService(this.connection); - const { queueId } = await this.jobQueueRepository.getNextQueueId(); + // NOT IMPLEMENTED - const key = await this.uploadDatasetToS3(datasetUUID, queueId, file); - let submission = await submissionService.getSubmissionIdByUUID(datasetUUID); - - if (!submission) { - // Create a submission if one does not exist - const currentUserId = this.connection.systemUserId(); - - const sourceTransformId = await this.getSourceTransformIdForUserId(currentUserId); - submission = await submissionService.insertSubmissionRecord({ - uuid: datasetUUID, - source_transform_id: sourceTransformId - }); - } - - const queueRecord = await this.createQueueJob(queueId, submission.submission_id, key, securityRequest); - - await submissionService.insertSubmissionStatusAndMessage( - submission.submission_id, - SUBMISSION_STATUS_TYPE.INGESTED, - SUBMISSION_MESSAGE_TYPE.NOTICE, - 'Uploaded successfully.' - ); - - return queueRecord; + return { queue_id: 0 }; } /** @@ -106,17 +80,6 @@ export class SubmissionJobQueueService extends DBService { return await this.jobQueueRepository.insertJobQueueRecord(queueId, submissionId, s3Key, securityRequest); } - /** - * Gets Transform Id for user Id - * - * @param {number} userId - * @return {*} {Promise} - * @memberof SubmissionJobQueueService - */ - async getSourceTransformIdForUserId(userId: number): Promise { - return await this.jobQueueRepository.getSourceTransformIdForUserId(userId); - } - /** * Fetch the next available job queue record(s). * diff --git a/api/src/services/submission-service.test.ts b/api/src/services/submission-service.test.ts index f6f78d4b9..f38e504f4 100644 --- a/api/src/services/submission-service.test.ts +++ b/api/src/services/submission-service.test.ts @@ -1,17 +1,13 @@ import chai, { expect } from 'chai'; -import * as JSONPathPlus from 'jsonpath-plus'; import { describe } from 'mocha'; -import { QueryResult } from 'pg'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; -import { ApiExecuteSQLError, ApiGeneralError } from '../errors/api-error'; +import { ApiGeneralError } from '../errors/api-error'; import { SubmissionFeatureSearchKeyValues } from '../repositories/search-index-respository'; import { SECURITY_APPLIED_STATUS } from '../repositories/security-repository'; import { - ISourceTransformModel, ISubmissionFeature, ISubmissionJobQueueRecord, - ISubmissionMetadataRecord, ISubmissionModel, ISubmissionObservationRecord, PatchSubmissionRecord, @@ -27,13 +23,10 @@ import { SUBMISSION_MESSAGE_TYPE, SUBMISSION_STATUS_TYPE } from '../repositories/submission-repository'; -import { SystemUserExtended } from '../repositories/user-repository'; import * as fileUtils from '../utils/file-utils'; -import { EMLFile } from '../utils/media/eml/eml-file'; import { getMockDBConnection } from '../__mocks__/db'; import { SearchIndexService } from './search-index-service'; import { SubmissionService } from './submission-service'; -import { UserService } from './user-service'; chai.use(sinonChai); @@ -295,38 +288,6 @@ describe('SubmissionService', () => { }); }); - describe('updateSubmissionMetadataEMLSource', () => { - it('should return submission_id on update', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'updateSubmissionMetadataEMLSource') - .resolves({ submission_metadata_id: 1 }); - - const response = await submissionService.updateSubmissionMetadataEMLSource(1, 1, { emlFile: {} } as EMLFile); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ submission_metadata_id: 1 }); - }); - }); - - describe('updateSubmissionRecordEMLJSONSource', () => { - it('should return submission_id on update', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'updateSubmissionMetadataEMLJSONSource') - .resolves({ submission_metadata_id: 1 }); - - const response = await submissionService.updateSubmissionRecordEMLJSONSource(1, 1, 'test'); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ submission_metadata_id: 1 }); - }); - }); - describe('getSubmissionRecordBySubmissionId', () => { it('should return submission_id on update', async () => { const mockDBConnection = getMockDBConnection(); @@ -357,169 +318,6 @@ describe('SubmissionService', () => { }); }); - describe('updateSubmissionMetadataRecordEndDate', () => { - it('should return submission_id on update', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon.stub(SubmissionRepository.prototype, 'updateSubmissionMetadataRecordEndDate').resolves(1); - - const response = await submissionService.updateSubmissionMetadataRecordEndDate(1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(1); - }); - }); - - describe('updateSubmissionMetadataRecordEffectiveDate', () => { - it('should return submission_id on update', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'updateSubmissionMetadataRecordEffectiveDate') - .resolves(1); - - const response = await submissionService.updateSubmissionMetadataRecordEffectiveDate(1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(1); - }); - }); - - describe('updateSubmissionObservationRecordEndDate', () => { - it('should return submission_id on update', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon.stub(SubmissionRepository.prototype, 'updateSubmissionObservationRecordEndDate').resolves(1); - - const response = await submissionService.updateSubmissionObservationRecordEndDate(1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(1); - }); - }); - - describe('getSourceTransformRecordBySystemUserId', () => { - it('should return submission source transform row object', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'getSourceTransformRecordBySystemUserId') - .resolves({ source_transform_id: 1 } as unknown as ISourceTransformModel); - - const response = await submissionService.getSourceTransformRecordBySystemUserId(1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ source_transform_id: 1 }); - }); - }); - - describe('getSubmissionMetadataJson', () => { - it('should return submission source transform row object', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'getSubmissionMetadataJson') - .resolves('transformed metadata'); - - const response = await submissionService.getSubmissionMetadataJson(1, 'transform'); - - expect(repo).to.be.calledOnce; - expect(response).to.be.equal('transformed metadata'); - }); - }); - - describe('getSourceTransformRecordBySourceTransformId', () => { - it('should return submission source transform row object', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon - .stub(SubmissionRepository.prototype, 'getSourceTransformRecordBySourceTransformId') - .resolves({ source_transform_id: 1 } as unknown as ISourceTransformModel); - - const response = await submissionService.getSourceTransformRecordBySourceTransformId(1); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ source_transform_id: 1 }); - }); - }); - - describe('getSubmissionRecordEMLJSONByDatasetId', () => { - describe('with no matching submission', () => { - it('should return eml string', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const getSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves({ rowCount: 0, rows: [] } as unknown as QueryResult); - - try { - await submissionService.getSubmissionRecordEMLJSONByDatasetId('333-333-333'); - expect.fail(); - } catch (error) { - expect(getSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect((error as ApiExecuteSQLError).message).to.equal('Failed to get dataset'); - } - }); - }); - - describe('with matching submission', () => { - it('should return eml string', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const getSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves({ rowCount: 1, rows: [{ eml_json_source: 'eml string' }] } as unknown as QueryResult); - - const response = await submissionService.getSubmissionRecordEMLJSONByDatasetId('333-333-333'); - - expect(getSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(response).to.equal('eml string'); - }); - }); - }); - - describe('findSubmissionRecordEMLJSONByDatasetId', () => { - describe('with no matching submission', () => { - it('should return eml string', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const getSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves({ rowCount: 0, rows: [] } as unknown as QueryResult); - - const response = await submissionService.findSubmissionRecordEMLJSONByDatasetId('333-333-333'); - - expect(getSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(response).to.be.null; - }); - }); - - describe('with matching submission', () => { - it('should return eml string', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const getSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves({ rowCount: 1, rows: [{ eml_json_source: 'eml string' }] } as unknown as QueryResult); - - const response = await submissionService.findSubmissionRecordEMLJSONByDatasetId('333-333-333'); - - expect(getSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(response).to.equal('eml string'); - }); - }); - }); - describe('insertSubmissionStatus', () => { it('should return submission status data', async () => { const mockDBConnection = getMockDBConnection(); @@ -556,40 +354,6 @@ describe('SubmissionService', () => { }); }); - describe('listSubmissionRecords', () => { - it('should return submission message data', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - const mockResponse = [ - { - submission_status: 'Submission Data Ingested', - submission_id: 1, - source_transform_id: 1, - uuid: '2267501d-c6a9-43b5-b951-2324faff6397', - record_effective_date: '2022-05-24T18:41:42.211Z', - record_end_date: null, - input_key: 'biohub/1/moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - input_file_name: 'moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - eml_source: null, - eml_json_source: null, - darwin_core_source: 'test', - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - update_date: '2022-05-24T18:41:42.056Z', - update_user: 15, - revision_count: 1 - } - ]; - - const repo = sinon.stub(SubmissionRepository.prototype, 'listSubmissionRecords').resolves(mockResponse); - - const response = await submissionService.listSubmissionRecords(); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(mockResponse); - }); - }); - describe('insertSubmissionStatusAndMessage', () => { it('should return submission status id and message id', async () => { const mockDBConnection = getMockDBConnection(); @@ -621,112 +385,6 @@ describe('SubmissionService', () => { }); }); - describe('findSubmissionRecordsWithSpatialCount', () => { - it('should return array of records', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const findSubmissionRecordWithSpatialCountStub = sinon - .stub(SubmissionService.prototype, 'findSubmissionRecordWithSpatialCount') - .onCall(0) - .resolves({ id: '111-111-111', source: {}, observation_count: 0 }) - .onCall(1) - .resolves({ id: '222-222-222', source: {}, observation_count: 200 }) - .onCall(2) - .resolves(null); - - const response = await submissionService.findSubmissionRecordsWithSpatialCount([ - '111-111-111', - '222-222-222', - '333-333-333' - ]); - - expect(findSubmissionRecordWithSpatialCountStub).to.be.calledThrice; - expect(response).to.be.eql([ - { id: '111-111-111', source: {}, observation_count: 0 }, - { id: '222-222-222', source: {}, observation_count: 200 }, - null - ]); - }); - }); - - describe('findSubmissionRecordWithSpatialCount', () => { - afterEach(() => { - sinon.restore(); - }); - - describe('with no occurrence spatial components', () => { - it('should return submission with count object', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionService.prototype, 'findSubmissionRecordEMLJSONByDatasetId') - .resolves({}); - - const getSpatialComponentCountByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSpatialComponentCountByDatasetId') - .resolves([{ spatial_type: 'Occurrence', count: 0 }]); - - const response = await submissionService.findSubmissionRecordWithSpatialCount('111-111-111'); - - expect(findSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(getSpatialComponentCountByDatasetIdStub).to.be.calledOnce; - expect(response).to.be.eql({ id: '111-111-111', source: {}, observation_count: 0 }); - }); - }); - - describe('with a non-zero number of occurrence spatial components', () => { - it('should return submission with count object', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionService.prototype, 'findSubmissionRecordEMLJSONByDatasetId') - .resolves({}) - .resolves({}); - - const getSpatialComponentCountByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSpatialComponentCountByDatasetId') - - .resolves([{ spatial_type: 'Occurrence', count: 200 }]); - - const response = await submissionService.findSubmissionRecordWithSpatialCount('222-222-222'); - - expect(findSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(getSpatialComponentCountByDatasetIdStub).to.be.calledOnce; - expect(response).to.be.eql({ id: '222-222-222', source: {}, observation_count: 200 }); - }); - }); - - describe('with no matching submission', () => { - it('should return null', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - const mockUserObject = { role_names: [] } as unknown as SystemUserExtended; - sinon.stub(UserService.prototype, 'getUserById').resolves(mockUserObject); - - const findSubmissionRecordEMLJSONByDatasetIdStub = sinon - .stub(SubmissionService.prototype, 'findSubmissionRecordEMLJSONByDatasetId') - .resolves(null); - - const getSpatialComponentCountByDatasetIdStub = sinon - .stub(SubmissionRepository.prototype, 'getSpatialComponentCountByDatasetId') - .resolves([]); - - const response = await submissionService.findSubmissionRecordWithSpatialCount('333-333-333'); - - expect(findSubmissionRecordEMLJSONByDatasetIdStub).to.be.calledOnce; - expect(getSpatialComponentCountByDatasetIdStub).to.be.calledOnce; - expect(response).to.be.null; - }); - }); - }); - describe('getSubmissionJobQueue', () => { it('should return a submission job queue record', async () => { const mockDBConnection = getMockDBConnection(); @@ -743,26 +401,6 @@ describe('SubmissionService', () => { }); }); - describe('insertSubmissionMetadataRecord', () => { - it('should return a submission observation record', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon.stub(SubmissionRepository.prototype, 'insertSubmissionMetadataRecord').resolves({ - submission_metadata_id: 1 - }); - - const response = await submissionService.insertSubmissionMetadataRecord({ - submission_id: 1 - } as unknown as ISubmissionMetadataRecord); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ - submission_metadata_id: 1 - }); - }); - }); - describe('insertSubmissionObservationRecord', () => { it('should return a submission observation record', async () => { const mockDBConnection = getMockDBConnection(); @@ -783,165 +421,6 @@ describe('SubmissionService', () => { }); }); - describe('findRelatedDatasetsByDatasetId', () => { - it('should return a valid array of related datasets on success', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const mockEmlJson = { - 'eml:eml': { - dataset: { - project: { - relatedProject: [ - { - '@_id': 'abcde', - title: 'Test-Title', - '@_system': 'http://example.com/datasets' - } - ] - } - } - } - }; - - const emlStub = sinon - .stub(SubmissionService.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves(mockEmlJson); - - const response = await submissionService.findRelatedDatasetsByDatasetId('test-dataset-id'); - - expect(response).to.eql([ - { - datasetId: 'abcde', - title: 'Test-Title', - url: 'http://example.com/datasets/abcde' - } - ]); - - expect(emlStub).to.be.calledOnce; - expect(emlStub).to.be.calledWith('test-dataset-id'); - }); - - it('should return an empty array if no EML JSON could be found', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const emlStub = sinon - .stub(SubmissionService.prototype, 'getSubmissionRecordEMLJSONByDatasetId') - .resolves(null as unknown as Record); - - const response = await submissionService.findRelatedDatasetsByDatasetId('test-dataset-id'); - - expect(response).to.eql([]); - expect(emlStub).to.be.calledOnce; - expect(emlStub).to.be.calledWith('test-dataset-id'); - }); - - it.skip('should return an empty array if JSON Path fails to return any results', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const emlStub = sinon.stub(SubmissionService.prototype, 'getSubmissionRecordEMLJSONByDatasetId').resolves({}); - - const jsonPathStub = sinon.stub(JSONPathPlus, 'JSONPath').returns([]); - - const response = await submissionService.findRelatedDatasetsByDatasetId('test-dataset-id'); - - expect(response).to.eql([]); - expect(emlStub).to.be.calledOnce; - expect(emlStub).to.be.calledWith('test-dataset-id'); - expect(jsonPathStub).to.be.calledOnce; - expect(jsonPathStub).to.be.calledWith({ - path: '$..eml:eml..relatedProject', - json: {}, - resultType: 'all' - }); - }); - }); - - describe('getDatasetsForReview', () => { - it('should return a rolled up dataset for review', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const stubDataset = sinon.stub(SubmissionRepository.prototype, 'getDatasetsForReview').resolves([ - { - dataset_id: 'UUID', - submission_id: 1, - dataset_name: 'Project Name', - keywords: [], - related_projects: [] - }, - { - dataset_id: 'UUID', - submission_id: 2, - dataset_name: 'Project Name', - keywords: [], - related_projects: [{ ['@_id']: 'RP_UUID_1' }, { ['@_id']: 'RP_UUID_2' }] - }, - { - dataset_id: 'UUID', - submission_id: 3, - dataset_name: 'Project Name', - keywords: [], - related_projects: [{ ['@_id']: 'RP_UUID_3' }] - } - ]); - const stubArtifactCount = sinon - .stub(SubmissionRepository.prototype, 'getArtifactForReviewCountForSubmissionUUID') - .resolves({ - dataset_id: '', - submission_id: 1, - artifacts_to_review: 1, - last_updated: '' - }); - - const response = await submissionService.getDatasetsForReview(['']); - - expect(stubDataset).to.be.calledOnce; - expect(stubArtifactCount).to.be.calledWith('RP_UUID_1'); - expect(stubArtifactCount).to.be.calledWith('RP_UUID_2'); - expect(stubArtifactCount).to.be.calledWith('RP_UUID_3'); - expect(response).to.be.eql([ - { - dataset_id: '', - artifacts_to_review: 1, - dataset_name: 'Project Name', - last_updated: '', - keywords: [] - }, - { - dataset_id: '', - artifacts_to_review: 3, - dataset_name: 'Project Name', - last_updated: '', - keywords: [] - }, - { - dataset_id: '', - artifacts_to_review: 2, - dataset_name: 'Project Name', - last_updated: '', - keywords: [] - } - ]); - }); - }); - - describe('updateSubmissionMetadataWithSearchKeys', () => { - it('should succeed with valid data', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon.stub(SubmissionRepository.prototype, 'updateSubmissionMetadataWithSearchKeys').resolves(1); - - const response = await submissionService.updateSubmissionMetadataWithSearchKeys(1, {}); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql(1); - }); - }); - describe('getUnreviewedSubmissionsForAdmins', () => { it('should return an array of submission records', async () => { const mockSubmissionRecords: SubmissionRecordWithSecurityAndRootFeatureType[] = [ diff --git a/api/src/services/submission-service.ts b/api/src/services/submission-service.ts index 58f9eaf54..f56254366 100644 --- a/api/src/services/submission-service.ts +++ b/api/src/services/submission-service.ts @@ -1,20 +1,13 @@ -import { default as dayjs } from 'dayjs'; import { JSONPath } from 'jsonpath-plus'; -import { z } from 'zod'; import { IDBConnection } from '../database/db'; -import { ApiExecuteSQLError, ApiGeneralError } from '../errors/api-error'; +import { ApiGeneralError } from '../errors/api-error'; import { SubmissionFeatureSearchKeyValues } from '../repositories/search-index-respository'; import { - IDatasetsForReview, - ISourceTransformModel, ISubmissionFeature, ISubmissionJobQueueRecord, - ISubmissionMetadataRecord, ISubmissionModel, - ISubmissionModelWithStatus, ISubmissionObservationRecord, ISubmissionRecord, - ISubmissionRecordWithSpatial, PatchSubmissionRecord, SubmissionFeatureDownloadRecord, SubmissionFeatureRecord, @@ -31,20 +24,11 @@ import { } from '../repositories/submission-repository'; import { getS3SignedURL } from '../utils/file-utils'; import { getLogger } from '../utils/logger'; -import { EMLFile } from '../utils/media/eml/eml-file'; import { DBService } from './db-service'; import { SearchIndexService } from './search-index-service'; const defaultLog = getLogger('submission-service'); -export const RelatedDataset = z.object({ - datasetId: z.string(), - title: z.string(), - url: z.string() -}); - -export type RelatedDataset = z.infer; - export class SubmissionService extends DBService { submissionRepository: SubmissionRepository; @@ -158,42 +142,6 @@ export class SubmissionService extends DBService { } } - /** - * Update the `eml_source` column of a submission record. - * - * @param {number} submissionId - * @param {EMLFile} file - * @return {*} {Promise<{ submission_id: number }>} - * @memberof SubmissionService - */ - async updateSubmissionMetadataEMLSource( - submissionId: number, - submissionMetadataId: number, - file: EMLFile - ): Promise<{ submission_metadata_id: number }> { - return this.submissionRepository.updateSubmissionMetadataEMLSource(submissionId, submissionMetadataId, file); - } - - /** - * Update the `eml_json_source` column of a submission record. - * - * @param {number} submissionId - * @param {ISubmissionRecord['eml_json_source']} EMLJSONSource - * @return {*} {Promise<{ submission_metadata_id: number }>} - * @memberof SubmissionService - */ - async updateSubmissionRecordEMLJSONSource( - submissionId: number, - submissionMetadataId: number, - EMLJSONSource: ISubmissionMetadataRecord['eml_json_source'] - ): Promise<{ submission_metadata_id: number }> { - return this.submissionRepository.updateSubmissionMetadataEMLJSONSource( - submissionId, - submissionMetadataId, - EMLJSONSource - ); - } - /** * Get submission record by id. * @@ -216,111 +164,6 @@ export class SubmissionService extends DBService { return this.submissionRepository.getSubmissionIdByUUID(uuid); } - /** - * Set record_end_date of submission id - * - * @param {number} submissionId - * @return {*} {Promise<{ submission_id: number }>} - * @memberof SubmissionService - */ - async updateSubmissionMetadataRecordEndDate(submissionId: number): Promise { - return this.submissionRepository.updateSubmissionMetadataRecordEndDate(submissionId); - } - - /** - * Set record_effective_timestamp of submission id - * - * @param {number} submissionId - * @return {*} {Promise<{ submission_id: number }>} - * @memberof SubmissionService - */ - async updateSubmissionMetadataRecordEffectiveDate(submissionId: number): Promise { - return this.submissionRepository.updateSubmissionMetadataRecordEffectiveDate(submissionId); - } - - /** - * Update end time stamp for submission observation record - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionService - */ - async updateSubmissionObservationRecordEndDate(submissionId: number): Promise { - return this.submissionRepository.updateSubmissionObservationRecordEndDate(submissionId); - } - - /** - * Get source transform record by its associated source system user id. - * - * @param {number} systemUserId - * @return {*} {Promise} - * @memberof SubmissionService - */ - async getSourceTransformRecordBySystemUserId(systemUserId: number, version?: string): Promise { - return this.submissionRepository.getSourceTransformRecordBySystemUserId(systemUserId, version); - } - - /** - * Get json representation of eml source from submission. - * - * @param {number} submissionId - * @param {string} transform - * @return {string} - * @memberof SubmissionService - */ - async getSubmissionMetadataJson(submissionId: number, transform: string): Promise { - return this.submissionRepository.getSubmissionMetadataJson(submissionId, transform); - } - - /** - * Get source transform record by its associated source transform id. - * - * @param {number} sourceTransformId - * @return {*} {Promise} - * @memberof SubmissionService - */ - async getSourceTransformRecordBySourceTransformId(sourceTransformId: number): Promise { - return this.submissionRepository.getSourceTransformRecordBySourceTransformId(sourceTransformId); - } - - /** - * Get json representation of eml source from submission by datasetId. - * - * @param {string} datasetId - * @return {Promise>} - * @memberof SubmissionService - */ - async getSubmissionRecordEMLJSONByDatasetId(datasetId: string): Promise> { - const response = await this.submissionRepository.getSubmissionRecordEMLJSONByDatasetId(datasetId); - - if (response.rowCount !== 1) { - throw new ApiExecuteSQLError('Failed to get dataset', [ - 'SubmissionRepository->getSubmissionRecordEMLJSONByDatasetId', - 'rowCount was null or undefined, expected rowCount = 1' - ]); - } - - return response.rows[0].eml_json_source; - } - - /** - * Find json representation of eml source from submission by datasetId. May return null if `datasetId` does not match - * any existing records. - * - * @param {string} datasetId - * @return {Record | null} - * @memberof SubmissionService - */ - async findSubmissionRecordEMLJSONByDatasetId(datasetId: string): Promise | null> { - const response = await this.submissionRepository.getSubmissionRecordEMLJSONByDatasetId(datasetId); - - if (response.rowCount !== 1) { - return null; - } - - return response.rows[0].eml_json_source; - } - /** * Insert a submission status record. * @@ -368,17 +211,6 @@ export class SubmissionService extends DBService { ); } - /** - * List all submissions - * - * @param {number} submissionId - * @return {*} {Promise} - * @memberof SubmissionService - */ - async listSubmissionRecords(): Promise { - return this.submissionRepository.listSubmissionRecords(); - } - /** * Inserts both the status and message of a submission * @@ -419,41 +251,6 @@ export class SubmissionService extends DBService { }; } - /** - * Retrieves an array of submission records with spatial count by dataset id. - * - * @param {string[]} datasetIds - * @return {*} {(Promise<(ISubmissionRecordWithSpatial | null)[]>)} - * @memberof SubmissionService - */ - async findSubmissionRecordsWithSpatialCount(datasetIds: string[]): Promise<(ISubmissionRecordWithSpatial | null)[]> { - return Promise.all(datasetIds.map(async (datasetId) => this.findSubmissionRecordWithSpatialCount(datasetId))); - } - - /** - * Retrieves a submission record with spatial count by dataset id. - * - * @param {string} datasetId - * @return {*} {(Promise)} - * @memberof SubmissionService - */ - async findSubmissionRecordWithSpatialCount(datasetId: string): Promise { - const [submissionEMLJSON, spatialComponentCounts] = await Promise.all([ - this.findSubmissionRecordEMLJSONByDatasetId(datasetId), - this.submissionRepository.getSpatialComponentCountByDatasetId(datasetId) - ]); - - if (!submissionEMLJSON) { - return null; - } - - return { - id: datasetId, - source: submissionEMLJSON, - observation_count: spatialComponentCounts.find((countItem) => countItem.spatial_type === 'Occurrence')?.count ?? 0 - }; - } - /** * Fetch row of submission job queue by submission Id * @@ -465,19 +262,6 @@ export class SubmissionService extends DBService { return this.submissionRepository.getSubmissionJobQueue(submissionId); } - /** - * Insert a new metadata record - * - * @param {ISubmissionMetadataRecord} submissionMetadata - * @return {*} {Promise<{ submission_metadata_id: number }>} - * @memberof SubmissionService - */ - async insertSubmissionMetadataRecord( - submissionMetadata: ISubmissionMetadataRecord - ): Promise<{ submission_metadata_id: number }> { - return this.submissionRepository.insertSubmissionMetadataRecord(submissionMetadata); - } - /** * Insert a new Observation Record * @@ -491,110 +275,6 @@ export class SubmissionService extends DBService { return this.submissionRepository.insertSubmissionObservationRecord(submissionObservation); } - /** - * Retrieves an array of datasets related to the given dataset. - * - * @param {string} datasetId - * @return {*} {Promise} - * @memberof SubmissionService - */ - async findRelatedDatasetsByDatasetId(datasetId: string): Promise { - const emlJson = await this.getSubmissionRecordEMLJSONByDatasetId(datasetId); - - if (!emlJson) { - return []; - } - - const result = JSONPath({ - path: '$..eml:eml..relatedProject', - json: emlJson, - resultType: 'all' - }); - - if (!result.length) { - return []; - } - - return result[0].value.map((relatedProject: any) => { - return { - datasetId: relatedProject['@_id'], - title: relatedProject['title'], - url: [relatedProject['@_system'], relatedProject['@_id']].join('/') - }; - }); - } - - /** - * Gets datasets that have artifacts that require a security review. - * This will roll up any related projects to provide a "total" count of artifacts to review - * - * @param keys A list of keys to filter the data based on search criteria defined by the transform process - * @returns {*} {Promise} - * @memberof SubmissionService - */ - async getDatasetsForReview(keys: string[]): Promise { - const data = await this.submissionRepository.getDatasetsForReview(keys); - const datasetsForReview: IDatasetsForReview[] = []; - - for await (const item of data) { - let rollUpCount = 0; - const dates: string[] = []; - - if (item.related_projects) { - for await (const rp of item.related_projects) { - const rpCount = await this.submissionRepository.getArtifactForReviewCountForSubmissionUUID(rp['@_id']); - if (rpCount) { - rollUpCount += rpCount.artifacts_to_review; - dates.push(rpCount.last_updated ?? ''); - } - } - } - - const parentArtifactCount = await this.submissionRepository.getArtifactForReviewCountForSubmissionUUID( - item.dataset_id - ); - if (parentArtifactCount) { - const finalCount = rollUpCount + parentArtifactCount.artifacts_to_review; - - // only push projects with artifacts to review - if (finalCount > 0) { - dates.push(parentArtifactCount.last_updated ?? ''); - datasetsForReview.push({ - dataset_id: parentArtifactCount.dataset_id, - artifacts_to_review: finalCount, - dataset_name: item.dataset_name, - last_updated: this.mostRecentDate(dates), - keywords: item.keywords - }); - } - } - } - return datasetsForReview; - } - - /** - * Compares and finds the most recent date given a list of date strings. Todays date is returned if no data is present in the list - * - * @param dates a list of date strings - * @returns {*} {string} the most recent date found - */ - mostRecentDate(dates: string[]): string { - dates.sort((d1, d2) => dayjs(d1).diff(dayjs(d2))); - return dates[0] ?? dayjs(); - } - - /** - * - * @param submissionId - * @param submitterSystem - * @param datasetSearch - * @returns - * @memberof SubmissionService - */ - async updateSubmissionMetadataWithSearchKeys(submissionId: number, datasetSearch: any): Promise { - return this.submissionRepository.updateSubmissionMetadataWithSearchKeys(submissionId, datasetSearch); - } - /** * Get all submissions that are pending security review (are unreviewed). * diff --git a/api/src/utils/media/csv/csv-file.test.ts b/api/src/utils/media/csv/csv-file.test.ts deleted file mode 100644 index 4f0b48da9..000000000 --- a/api/src/utils/media/csv/csv-file.test.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import xlsx from 'xlsx'; -import { CSVValidation, CSVWorkBook, CSVWorksheet, IHeaderError, IRowError } from './csv-file'; - -describe('CSVWorkBook', () => { - it('constructs with no rawWorkbook param', () => { - const csvWorkBook = new CSVWorkBook(); - - expect(csvWorkBook).not.to.be.null; - expect(csvWorkBook.rawWorkbook).not.to.be.null; - expect(csvWorkBook.worksheets).to.eql({}); - }); - - it('constructs with rawWorkbook param', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data', 'Header2Data'] - ]); - - const xlsxWorkBook = xlsx.utils.book_new(); - xlsx.utils.book_append_sheet(xlsxWorkBook, xlsxWorkSheet); - - const csvWorkBook = new CSVWorkBook(xlsxWorkBook); - - expect(csvWorkBook).not.to.be.null; - expect(csvWorkBook.rawWorkbook).not.to.be.null; - expect(csvWorkBook.worksheets['Sheet1']).not.to.be.null; - }); -}); - -describe('CSVWorksheet', () => { - it('constructs', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data', 'Header2Data'] - ]); - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - }); - - describe('getHeaders', () => { - it('returns empty array if the worksheet is null', () => { - const xlsxWorkSheet = null as unknown as xlsx.WorkSheet; - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getHeaders()).to.eql([]); - }); - - it('returns an array of headers', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data', 'Header2Data'] - ]); - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getHeaders()).to.eql(['Header1', 'Header2']); - }); - }); - - describe('getRows', () => { - it('returns empty array if the worksheet is null', () => { - const xlsxWorkSheet = null as unknown as xlsx.WorkSheet; - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getRows()).to.eql([]); - }); - - it('returns an array of rows data arrays', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data1', 'Header2Data1'], - ['Header1Data2', 'Header2Data2'] - ]); - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getRows()).to.eql([ - ['Header1Data1', 'Header2Data1'], - ['Header1Data2', 'Header2Data2'] - ]); - }); - }); - - describe('getRowObjects', () => { - it('returns empty array if the worksheet is null', () => { - const xlsxWorkSheet = null as unknown as xlsx.WorkSheet; - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getRowObjects()).to.eql([]); - }); - - it('returns an array of rows data arrays', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data1', 'Header2Data1'], - ['Header1Data2', 'Header2Data2'] - ]); - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - expect(csvWorksheet.getRowObjects()).to.eql([ - { - Header1: 'Header1Data1', - Header2: 'Header2Data1' - }, - { - Header1: 'Header1Data2', - Header2: 'Header2Data2' - } - ]); - }); - }); - - describe('validate', () => { - it('calls all provided validator functions', () => { - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data1', 'Header2Data1'], - ['Header1Data2', 'Header2Data2'] - ]); - - const csvWorksheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - expect(csvWorksheet).not.to.be.null; - - const mockValidationFunction1 = sinon.stub(); - const mockValidationFunction2 = sinon.stub(); - const mockValidationFunction3 = sinon.stub(); - - csvWorksheet.validate([mockValidationFunction1, mockValidationFunction2, mockValidationFunction3]); - - expect(mockValidationFunction1).to.have.been.calledOnce; - expect(mockValidationFunction2).to.have.been.calledOnce; - expect(mockValidationFunction3).to.have.been.calledOnce; - }); - }); -}); - -describe('CSVValidation', () => { - it('constructs', () => { - const csvValidation = new CSVValidation('fileName'); - - expect(csvValidation).not.to.be.null; - }); - - describe('addFileErrors', () => { - it('adds new file errors', () => { - const csvValidation = new CSVValidation('fileName'); - - expect(csvValidation).not.to.be.null; - - const fileError1 = 'a file error'; - const fileError2 = 'a second file error'; - - csvValidation.addFileErrors([fileError1]); - - expect(csvValidation.fileErrors).to.eql([fileError1]); - - csvValidation.addFileErrors([fileError2]); - - expect(csvValidation.fileErrors).to.eql([fileError1, fileError2]); - }); - }); - - describe('addHeaderErrors', () => { - it('adds new header errors', () => { - const csvValidation = new CSVValidation('fileName'); - - expect(csvValidation).not.to.be.null; - - const headerError1: IHeaderError = { - errorCode: 'Duplicate Header', - message: 'a header error', - col: 0 - }; - - const headerError2: IHeaderError = { - errorCode: 'Unknown Header', - message: 'a second header error', - col: 1 - }; - - csvValidation.addHeaderErrors([headerError1]); - - expect(csvValidation.headerErrors).to.eql([headerError1]); - - csvValidation.addHeaderErrors([headerError2]); - - expect(csvValidation.headerErrors).to.eql([headerError1, headerError2]); - }); - }); - - describe('addRowErrors', () => { - it('adds new header errors', () => { - const csvValidation = new CSVValidation('fileName'); - - expect(csvValidation).not.to.be.null; - - const rowError1: IRowError = { - errorCode: 'Missing Required Field', - message: 'a row error', - col: 'col1', - row: 1 - }; - - const rowError2: IRowError = { - errorCode: 'Missing Required Field', - message: 'a second row error', - col: 'col1', - row: 2 - }; - - csvValidation.addRowErrors([rowError1]); - - expect(csvValidation.rowErrors).to.eql([rowError1]); - - csvValidation.addRowErrors([rowError2]); - - expect(csvValidation.rowErrors).to.eql([rowError1, rowError2]); - }); - }); - - describe('getState', () => { - it('gets the current validation state', () => { - const csvValidation = new CSVValidation('fileName'); - - expect(csvValidation).not.to.be.null; - - const fileError1 = 'a file error'; - - const headerError1: IHeaderError = { - errorCode: 'Duplicate Header', - message: 'a header error', - col: 0 - }; - - const rowError1: IRowError = { - errorCode: 'Missing Required Field', - message: 'a row error', - col: 'col1', - row: 1 - }; - - csvValidation.addFileErrors([fileError1]); - csvValidation.addHeaderErrors([headerError1]); - csvValidation.addRowErrors([rowError1]); - - const validationState = csvValidation.getState(); - - expect(validationState).to.eql({ - fileName: 'fileName', - fileErrors: [fileError1], - headerErrors: [headerError1], - rowErrors: [rowError1], - isValid: false - }); - }); - }); -}); diff --git a/api/src/utils/media/csv/csv-file.ts b/api/src/utils/media/csv/csv-file.ts deleted file mode 100644 index dbe7a2b31..000000000 --- a/api/src/utils/media/csv/csv-file.ts +++ /dev/null @@ -1,299 +0,0 @@ -import xlsx from 'xlsx'; -import { safeToLowerCase, safeTrim } from '../../string-utils'; -import { IMediaState, MediaValidation } from '../media-file'; -import { getCellValue, getWorksheetRange, replaceCellDates, trimCellWhitespace } from '../xlsx/xlsx-utils'; - -export type CSVWorksheets = { [name: string]: CSVWorksheet }; - -export class CSVWorkBook { - rawWorkbook: xlsx.WorkBook; - - worksheets: CSVWorksheets; - - constructor(workbook?: xlsx.WorkBook) { - this.rawWorkbook = workbook || xlsx.utils.book_new(); - - const worksheets: CSVWorksheets = {}; - - Object.entries(this.rawWorkbook.Sheets).forEach(([key, value]) => { - worksheets[key] = new CSVWorksheet(key, value); - }); - - this.worksheets = worksheets; - } -} - -export class CSVWorksheet { - name: string; - - worksheet: xlsx.WorkSheet; - - _headers: string[]; - _headersLowerCase: string[]; - _rows: string[][]; - _rowObjects: object[]; - - csvValidation: CSVValidation; - - constructor(name: string, worksheet?: xlsx.WorkSheet) { - this.name = name; - - this.worksheet = worksheet || xlsx.utils.aoa_to_sheet([]); - - this._headers = []; - this._headersLowerCase = []; - this._rows = []; - this._rowObjects = []; - - this.csvValidation = new CSVValidation(this.name); - } - - /** - * Returns an array of header values. - * - * Note: This is always the first row (index 0) - * - * @return {*} {string[]} - * @memberof CSVWorksheet - */ - getHeaders(): string[] { - if (!this.worksheet) { - return []; - } - - const originalRange = getWorksheetRange(this.worksheet); - - if (!originalRange) { - return []; - } - - if (!this._headers.length) { - // Specify range to only include the 0th row (header row) - const customRange: xlsx.Range = { ...originalRange, e: { ...originalRange.e, r: 0 } }; - - const aoaHeaders: any[][] = xlsx.utils.sheet_to_json(this.worksheet, { - header: 1, - blankrows: false, - range: customRange - }); - - if (aoaHeaders.length > 0) { - // Parse the headers array from the array of arrays produced by calling `xlsx.utils.sheet_to_json` - this._headers = aoaHeaders[0].map(safeTrim); - } - } - - return this._headers; - } - - getHeadersLowerCase(): string[] { - if (!this._headersLowerCase.length) { - this._headersLowerCase = this.getHeaders().map(safeToLowerCase); - } - - return this._headersLowerCase; - } - - getHeaderIndex(headerName: string): number { - return this.getHeaders().indexOf(headerName); - } - - /** - * Return an array of row value arrays. - * - * Note: This does not include the first row (header row). - * - * @return {*} {string[][]} - * @memberof CSVWorksheet - */ - getRows(): string[][] { - if (!this.worksheet) { - return []; - } - - const originalRange = getWorksheetRange(this.worksheet); - - if (!originalRange) { - return []; - } - - if (!this._rows.length) { - const rowsToReturn: string[][] = []; - - for (let i = 1; i <= originalRange.e.r; i++) { - const row = new Array(this.getHeaders().length); - let rowHasValues = false; - - for (let j = 0; j <= originalRange.e.c; j++) { - const cellAddress = { c: j, r: i }; - const cellRef = xlsx.utils.encode_cell(cellAddress); - const cell = this.worksheet[cellRef]; - - if (!cell) { - continue; - } - - row[j] = getCellValue(trimCellWhitespace(replaceCellDates(cell))); - - rowHasValues = true; - } - - if (row.length && rowHasValues) { - rowsToReturn.push(row); - } - } - - this._rows = rowsToReturn; - } - - return this._rows; - } - - getRowObjects(): object[] { - if (!this.worksheet) { - return []; - } - - const ref = this.worksheet['!ref']; - - if (!ref) { - return []; - } - - if (!this._rowObjects.length) { - const rowObjectsArray: object[] = []; - const rows = this.getRows(); - const headers = this.getHeaders(); - - rows.forEach((row: string[]) => { - const rowObject = {}; - - headers.forEach((header: string, index: number) => { - rowObject[header] = row[index]; - }); - - rowObjectsArray.push(rowObject); - }); - - this._rowObjects = rowObjectsArray; - } - - return this._rowObjects; - } - - getCell(headerName: string, rowIndex: number) { - const headerIndex = this.getHeaderIndex(headerName); - - if (headerIndex < 0) { - return undefined; - } - - const rows = this.getRows(); - - const row = rows?.[rowIndex]; - - if (!row || !row.length) { - return undefined; - } - - return row[headerIndex]; - } - - /** - * Runs all of the given validators on the worksheet, whereby the results of all validations - * are stored in `this.csvValidation`. - * - * @param {CSVValidator[]} validators A series of CSV validators to be run on the worksheet. - * @return {*} {CSVValidation} The result of all validations, namely `this.csvValidation`. - * @memberof CSVWorksheet - */ - validate(validators: CSVValidator[]): CSVValidation { - validators.forEach((validator) => validator(this)); - - return this.csvValidation; - } -} - -export type CSVValidator = (csvWorksheet: CSVWorksheet) => CSVWorksheet; - -// ensure these error codes match the 'name' column in the table: submission_message_type - -export type IHeaderErrorCode = - | 'Duplicate Header' - | 'Unknown Header' - | 'Missing Required Header' - | 'Missing Recommended Header' - | 'Miscellaneous'; - -export type IRowErrorCode = - | 'Missing Required Field' - | 'Unexpected Format' - | 'Out of Range' - | 'Invalid Value' - | 'Miscellaneous'; - -export interface IHeaderError { - errorCode: IHeaderErrorCode; - message: string; - col: string | number; -} - -export interface IRowError { - errorCode: IRowErrorCode; - message: string; - col: string; - row: number; -} -export interface ICsvState extends IMediaState { - headerErrors: IHeaderError[]; - rowErrors: IRowError[]; -} - -/** - * Supports getting/setting validation errors for any csv file. - * - * @export - * @class CSVValidation - * @extends {MediaValidation} - */ -export class CSVValidation extends MediaValidation { - headerErrors: IHeaderError[]; - rowErrors: IRowError[]; - - constructor(fileName: string) { - super(fileName); - - this.headerErrors = []; - this.rowErrors = []; - } - - addHeaderErrors(errors: IHeaderError[]) { - this.headerErrors = this.headerErrors.concat(errors); - - if (errors?.length) { - this.isValid = false; - } - } - - addHeaderWarnings(errors: IHeaderError[]) { - this.headerErrors = this.headerErrors.concat(errors); - } - - addRowErrors(errors: IRowError[]) { - this.rowErrors = this.rowErrors.concat(errors); - - if (errors?.length) { - this.isValid = false; - } - } - - getState(): ICsvState { - return { - fileName: this.fileName, - fileErrors: this.fileErrors, - headerErrors: this.headerErrors, - rowErrors: this.rowErrors, - isValid: this.isValid - }; - } -} diff --git a/api/src/utils/media/csv/validation/csv-header-validator.test.ts b/api/src/utils/media/csv/validation/csv-header-validator.test.ts deleted file mode 100644 index 9ba17182d..000000000 --- a/api/src/utils/media/csv/validation/csv-header-validator.test.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import xlsx from 'xlsx'; -import { CSVWorksheet } from '../csv-file'; -import { - getDuplicateHeadersValidator, - getValidHeadersValidator, - hasRecommendedHeadersValidator, - hasRequiredHeadersValidator -} from './csv-header-validator'; - -describe('getDuplicateHeadersValidator', () => { - it('adds no errors when there are no headers', () => { - const validator = getDuplicateHeadersValidator(); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[]]); // empty csv - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds no errors if no headers are duplicates', () => { - const validator = getDuplicateHeadersValidator(); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2', 'Header3']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds errors for each header that is a duplicate', () => { - const validator = getDuplicateHeadersValidator(); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2', 'Header1', 'Header3', 'Header2', 'Header4']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Duplicate Header', - col: 'Header1', - message: 'Duplicate header' - }, - { - errorCode: 'Duplicate Header', - col: 'Header2', - message: 'Duplicate header' - } - ]); - }); -}); - -describe('hasRequiredHeadersValidator', () => { - it('adds no errors when required headers are undefined', () => { - const requiredHeaders = undefined; - - const validator = hasRequiredHeadersValidator(requiredHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when there are no required headers', () => { - const requiredHeaders = { file_required_columns_validator: { required_columns: [] } }; - - const validator = hasRequiredHeadersValidator(requiredHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds errors for all headers if there are required headers but no header row', () => { - const requiredHeaders = { - file_required_columns_validator: { required_columns: ['Header1', 'Header2', 'Header3'] } - }; - - const validator = hasRequiredHeadersValidator(requiredHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[]]); // empty csv - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Missing Required Header', - col: 'Header1', - message: 'Missing required header' - }, - { - errorCode: 'Missing Required Header', - col: 'Header2', - message: 'Missing required header' - }, - { - errorCode: 'Missing Required Header', - col: 'Header3', - message: 'Missing required header' - } - ]); - }); - - it('adds errors for each missing header that is required', () => { - const requiredHeaders = { - file_required_columns_validator: { required_columns: ['Header1', 'Header3', 'Header4', 'Header5'] } - }; - - const validator = hasRequiredHeadersValidator(requiredHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2', 'Header4']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Missing Required Header', - col: 'Header3', - message: 'Missing required header' - }, - { - errorCode: 'Missing Required Header', - col: 'Header5', - message: 'Missing required header' - } - ]); - }); -}); - -describe('getValidHeadersValidator', () => { - it('adds no errors when configuration is not found', () => { - const validHeaders = undefined; - - const validator = getValidHeadersValidator(validHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds no errors when no valid headers provided', () => { - const validHeaders = { file_valid_columns_validator: { valid_columns: [] } }; - - const validator = getValidHeadersValidator(validHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds no errors if there are no invalid headers', () => { - const validHeaders = { file_valid_columns_validator: { valid_columns: ['Header1', 'Header2', 'Header3'] } }; - - const validator = getValidHeadersValidator(validHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds errors for all headers that are not valid ', () => { - const validHeaders = { file_valid_columns_validator: { valid_columns: ['Header1', 'Header2', 'Header3'] } }; - - const validator = getValidHeadersValidator(validHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'UnknownHeader2', 'Header3', 'UnknownHeader4']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Unknown Header', - col: 'UnknownHeader2', - message: 'Unsupported header' - }, - { - errorCode: 'Unknown Header', - col: 'UnknownHeader4', - message: 'Unsupported header' - } - ]); - }); -}); - -describe('hasRecommendedHeadersValidator', () => { - it('adds no errors when there are no recommended headers', () => { - const recommendedHeaders = undefined; - - const validator = hasRecommendedHeadersValidator(recommendedHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors/warnings when there are no recommended headers', () => { - const recommendedHeaders = { - file_recommended_columns_validator: { recommended_columns: [] } - }; - - const validator = hasRecommendedHeadersValidator(recommendedHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2', 'Header4']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([]); - }); - - it('adds errors for each missing header that is recommended', () => { - const recommendedHeaders = { - file_recommended_columns_validator: { recommended_columns: ['Header1', 'Header3', 'Header4', 'Header5'] } - }; - - const validator = hasRecommendedHeadersValidator(recommendedHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2', 'Header4']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Missing Recommended Header', - col: 'Header3', - message: 'Missing recommended header' - }, - { - errorCode: 'Missing Recommended Header', - col: 'Header5', - message: 'Missing recommended header' - } - ]); - }); - - it('adds warnings for all headers if there are recommended headers but no header row', () => { - const recommendedHeaders = { - file_recommended_columns_validator: { recommended_columns: ['Header1', 'Header2', 'Header3'] } - }; - - const validator = hasRecommendedHeadersValidator(recommendedHeaders); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[]]); // empty csv - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.headerErrors).to.eql([ - { - errorCode: 'Missing Recommended Header', - col: 'Header1', - message: 'Missing recommended header' - }, - { - errorCode: 'Missing Recommended Header', - col: 'Header2', - message: 'Missing recommended header' - }, - { - errorCode: 'Missing Recommended Header', - col: 'Header3', - message: 'Missing recommended header' - } - ]); - }); -}); diff --git a/api/src/utils/media/csv/validation/csv-header-validator.ts b/api/src/utils/media/csv/validation/csv-header-validator.ts deleted file mode 100644 index bc8d05a8a..000000000 --- a/api/src/utils/media/csv/validation/csv-header-validator.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { CSVValidator } from '../csv-file'; - -/** - * Adds an error for each header that is not unique. - * - * @return {*} {CSVValidator} - */ -export const getDuplicateHeadersValidator = (): CSVValidator => { - return (csvWorksheet) => { - const headers = csvWorksheet.getHeaders(); - - if (!headers?.length) { - return csvWorksheet; - } - - const seenHeaders: string[] = []; - - for (const header of headers) { - if (seenHeaders.includes(header)) { - csvWorksheet.csvValidation.addHeaderErrors([ - { - errorCode: 'Duplicate Header', - message: 'Duplicate header', - col: header - } - ]); - } else { - seenHeaders.push(header); - } - } - - return csvWorksheet; - }; -}; - -export type FileRequiredHeaderValidatorConfig = { - file_required_columns_validator: { - name?: string; - description?: string; - required_columns: string[]; - }; -}; - -/** - * For a specified set of required columns, adds an error if the column is not present in the csv. - * - * @param {FileRequiredHeaderValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const hasRequiredHeadersValidator = (config?: FileRequiredHeaderValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - if (!config.file_required_columns_validator.required_columns.length) { - // No required columns - return csvWorksheet; - } - - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - if (!headersLowerCase?.length) { - csvWorksheet.csvValidation.addHeaderErrors( - config.file_required_columns_validator.required_columns.map((requiredHeader) => { - return { - errorCode: 'Missing Required Header', - message: 'Missing required header', - col: requiredHeader - }; - }) - ); - - return csvWorksheet; - } - - for (const requiredHeader of config.file_required_columns_validator.required_columns) { - if (!headersLowerCase.includes(requiredHeader.toLowerCase())) { - csvWorksheet.csvValidation.addHeaderErrors([ - { - errorCode: 'Missing Required Header', - message: 'Missing required header', - col: requiredHeader - } - ]); - } - } - - return csvWorksheet; - }; -}; - -export type FileRecommendedHeaderValidatorConfig = { - file_recommended_columns_validator: { - name?: string; - description?: string; - recommended_columns: string[]; - }; -}; - -/** - * For a specified set of recommended columns, adds a warning if the column is not present in the csv. - * - * @param {FileRecommendedHeaderValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const hasRecommendedHeadersValidator = (config?: FileRecommendedHeaderValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - if (!config.file_recommended_columns_validator.recommended_columns.length) { - return csvWorksheet; - } - - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - if (!headersLowerCase?.length) { - csvWorksheet.csvValidation.addHeaderWarnings( - config.file_recommended_columns_validator.recommended_columns.map((recommendedHeader) => { - return { - errorCode: 'Missing Recommended Header', - message: 'Missing recommended header', - col: recommendedHeader - }; - }) - ); - - return csvWorksheet; - } - - for (const recommendedHeader of config.file_recommended_columns_validator.recommended_columns) { - if (!headersLowerCase.includes(recommendedHeader.toLowerCase())) { - csvWorksheet.csvValidation.addHeaderWarnings([ - { - errorCode: 'Missing Recommended Header', - message: 'Missing recommended header', - col: recommendedHeader - } - ]); - } - } - - return csvWorksheet; - }; -}; - -export type FileValidHeadersValidatorConfig = { - file_valid_columns_validator: { - name?: string; - description?: string; - valid_columns: string[]; - }; -}; - -/** - * Adds a warning if a column in the csv does not match a value in a specified set of valid columns. - * - * @param {FileValidHeadersValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const getValidHeadersValidator = (config?: FileValidHeadersValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - if (!config.file_valid_columns_validator.valid_columns.length) { - return csvWorksheet; - } - - const headers = csvWorksheet.getHeaders(); - - for (const header of headers) { - if ( - !config.file_valid_columns_validator.valid_columns - .map((item) => item.toLowerCase()) - .includes(header.trim().toLowerCase()) - ) { - csvWorksheet.csvValidation.addHeaderWarnings([ - { - errorCode: 'Unknown Header', - message: 'Unsupported header', - col: header - } - ]); - } - } - - return csvWorksheet; - }; -}; diff --git a/api/src/utils/media/csv/validation/csv-row-validator.test.ts b/api/src/utils/media/csv/validation/csv-row-validator.test.ts deleted file mode 100644 index cf4976d85..000000000 --- a/api/src/utils/media/csv/validation/csv-row-validator.test.ts +++ /dev/null @@ -1,607 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import xlsx from 'xlsx'; -import { CSVWorksheet } from '../csv-file'; -import { - getCodeValueFieldsValidator, - getNumericFieldsValidator, - getRequiredFieldsValidator, - getValidFormatFieldsValidator, - getValidRangeFieldsValidator -} from './csv-row-validator'; - -describe('getRequiredFieldsValidator', () => { - it('adds no errors when required fields are not provided', () => { - const requiredFieldsByHeader: string[] = []; - - const validator = getRequiredFieldsValidator(requiredFieldsByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data', 'Header2Data'] - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when header does not exist', () => { - const requiredFieldsByHeader: string[] = ['Header1', 'Header2']; // fields for these headers are required - const validator = getRequiredFieldsValidator(requiredFieldsByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[], [5]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds errors for every field if required fields are provided and there are zero data rows in the worksheet', () => { - const requiredFieldsByHeader: string[] = ['Header1', 'Header2']; // fields for these headers are required - - const validator = getRequiredFieldsValidator(requiredFieldsByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1', 'Header2']]); // no data rows - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Missing Required Field', - message: 'Missing required value for column', - row: 2 - }, - { - col: 'Header2', - errorCode: 'Missing Required Field', - message: 'Missing required value for column', - row: 2 - } - ]); - }); - - it('adds errors for required fields that are empty', () => { - const requiredFieldsByHeader: string[] = ['Header1', 'Header2']; // fields for these headers are required - - const validator = getRequiredFieldsValidator(requiredFieldsByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2', 'Header3'], - ['', 'Header2Data', ''] // mixture of required and non required headers with non-empty and empty values - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Missing Required Field', - message: 'Missing required value for column', - row: 2 - } - ]); - }); - - it('adds no errors if there are no invalid required fields', () => { - const requiredFieldsByHeader: string[] = ['Header1', 'Header2']; // fields for these headers are required - - const validator = getRequiredFieldsValidator(requiredFieldsByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2', 'Header3'], - ['header2Data', 'Header2Data', ''] // valid fields - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); -}); - -describe('getCodeValueFieldsValidator', () => { - it('adds no errors when header does not exist', () => { - const requiredCodeValuesByHeader = { - columnName: 'Header1', - column_code_validator: { allowed_code_values: [{ name: 'Code1' }, { name: 'Code2' }] } - }; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[], [5]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when code values are empty', () => { - const requiredCodeValuesByHeader = { - columnName: 'Header1', - column_code_validator: { allowed_code_values: [] } - }; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[], [5]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - it('adds no errors when no required code values are provided', () => { - const requiredCodeValuesByHeader = undefined; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Header1Data', 'Header2Data'] - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds errors for non-empty fields whose value is not part of a specified code set', () => { - const requiredCodeValuesByHeader = { - columnName: 'Header1', - column_code_validator: { allowed_code_values: [{ name: 'Code1' }, { name: 'Code2' }] } - }; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['invalidCode', 'Header2Data'] // non-empty field does not match one of the valid codes - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Invalid Value', - message: 'Invalid value: invalidCode. Must be one of [Code1, Code2]', - row: 2 - } - ]); - }); - - it('adds no errors for empty fields whose value is not part of a specified code set', () => { - const requiredCodeValuesByHeader = { - columnName: 'Header1', - column_code_validator: { allowed_code_values: [{ name: 'Code1' }, { name: 'Code2' }] } - }; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['', 'Header2Data'] // empty field does not match one of the valid codes - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors for fields whose value is part of a specified code set', () => { - const requiredCodeValuesByHeader = { - columnName: 'Header1', - column_code_validator: { allowed_code_values: [{ name: 'Code1' }, { name: 'Code2' }] } - }; - - const validator = getCodeValueFieldsValidator(requiredCodeValuesByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([ - ['Header1', 'Header2'], - ['Code1', 'Code4'] // fields that match one of the valid codes - ]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); -}); - -describe('getValidRangeFieldsValidator', () => { - it('adds no errors when no code value range is not provided', () => { - const codeValuesRangeByHeader = undefined; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], []]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when header does not exist', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 1, - max_value: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[], [5]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when valid value range is provided', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 1, - max_value: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], [6]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds an out of range error when value provided exceeds range', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 1, - max_value: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], [11]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Out of Range', - message: 'Invalid value: 11. Value must be between 1 and 10 ', - row: 2 - } - ]); - }); - - it('adds an out of range error when value provided is less than the range', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 5, - max_value: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], [1]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Out of Range', - message: 'Invalid value: 1. Value must be between 5 and 10 ', - row: 2 - } - ]); - }); - - it('adds an out of range error when value provided is greater than the max_value, and only max_value is provided', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - max_value: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], [11]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Out of Range', - message: 'Invalid value: 11. Value must be less than 10 ', - row: 2 - } - ]); - }); - - it('adds an out of range error when value provided is less than the min_value, and only the min-value is specified', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 5 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], [4]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Out of Range', - message: 'Invalid value: 4. Value must be greater than 5 ', - row: 2 - } - ]); - }); - - it('adds an invalid value error when value provided is not a number', () => { - const codeValuesRangeByHeader = { - columnName: 'Header1', - column_range_validator: { - min_value: 5, - max_vlaue: 10 - } - }; - - const validator = getValidRangeFieldsValidator(codeValuesRangeByHeader); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['a']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Invalid Value', - message: 'Invalid value: a. Value must be a number ', - row: 2 - } - ]); - }); -}); - -describe('getNumericFieldsValidator', () => { - it('adds no errors when configuration is not provided', () => { - const columnNumericValidatorConfig = undefined; - - const validator = getNumericFieldsValidator(columnNumericValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], []]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when header does not exist', () => { - const columnNumericValidatorConfig = { - columnName: 'Header 1', - column_numeric_validator: {} - }; - - const validator = getNumericFieldsValidator(columnNumericValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([[], [5]]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds an error when row value is not numeric', () => { - const columnNumericValidatorConfig = { - columnName: 'Header1', - column_numeric_validator: {} - }; - - const validator = getNumericFieldsValidator(columnNumericValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['a']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Invalid Value', - message: 'Invalid value: a. Value must be a number ', - row: 2 - } - ]); - }); -}); - -describe('getValidFormatFieldsValidator', () => { - it('adds no errors when configuration is not provided', () => { - const columnFormatValidatorConfig = undefined; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['WPT1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when regular expression is not provided', () => { - const columnFormatValidatorConfig = { - columnName: 'Header1', - column_format_validator: { - reg_exp: '', - expected_format: '' - } - }; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['WPT1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when header is not provided', () => { - const columnFormatValidatorConfig = { - columnName: 'Header1', - column_format_validator: { - reg_exp: '^wpt \\d+$', - expected_format: '' - } - }; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header2'], ['WPT1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds no errors when row value matches regular expression because regex is case insensitive', () => { - const columnFormatValidatorConfig = { - columnName: 'Header1', - column_format_validator: { - reg_exp: '^wpt \\d+$', - reg_exp_flags: 'i', - expected_format: '' - } - }; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['WPT 1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([]); - }); - - it('adds an error when row value does not match regular expression because regex is case sensitive', () => { - const columnFormatValidatorConfig = { - columnName: 'Header1', - column_format_validator: { - reg_exp: '^wpt \\d+$', - expected_format: 'Must be in the format "WPT X": WPT 11 (case sensitive)' - } - }; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['WPT 1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Unexpected Format', - message: 'Unexpected Format: WPT 1. Must be in the format "WPT X": WPT 11 (case sensitive)', - row: 2 - } - ]); - }); - - it('adds an error when row value does not match regular expression', () => { - const columnFormatValidatorConfig = { - columnName: 'Header1', - column_format_validator: { - reg_exp: '^wpt \\d+$', - expected_format: 'Must be in the format "WPT X": WPT 11 (case sensitive)' - } - }; - - const validator = getValidFormatFieldsValidator(columnFormatValidatorConfig); - - const xlsxWorkSheet = xlsx.utils.aoa_to_sheet([['Header1'], ['WXT1']]); - - const csvWorkSheet = new CSVWorksheet('Sheet1', xlsxWorkSheet); - - validator(csvWorkSheet); - - expect(csvWorkSheet.csvValidation.rowErrors).to.eql([ - { - col: 'Header1', - errorCode: 'Unexpected Format', - message: 'Unexpected Format: WXT1. Must be in the format "WPT X": WPT 11 (case sensitive)', - row: 2 - } - ]); - }); -}); diff --git a/api/src/utils/media/csv/validation/csv-row-validator.ts b/api/src/utils/media/csv/validation/csv-row-validator.ts deleted file mode 100644 index a5ffcfd14..000000000 --- a/api/src/utils/media/csv/validation/csv-row-validator.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { CSVValidator } from '../csv-file'; - -/** - * TODO needs updating to use new config style, etc. - */ -export const getRequiredFieldsValidator = (requiredFieldsByHeader?: string[]): CSVValidator => { - return (csvWorksheet) => { - if (!requiredFieldsByHeader?.length) { - return csvWorksheet; - } - - const rows = csvWorksheet.getRows(); - - // If there are no rows, then add errors for all cells in the first data row based on the array of required headers - if (!rows?.length) { - csvWorksheet.csvValidation.addRowErrors( - requiredFieldsByHeader.map((requiredFieldByHeader) => { - return { - errorCode: 'Missing Required Field', - message: `Missing required value for column`, - col: requiredFieldByHeader, - row: 2 - }; - }) - ); - - return csvWorksheet; - } - - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - // If there are rows, then check each cell in each row against the list of required headers, adding errors as needed - rows.forEach((row, rowIndex) => { - for (const requiredFieldByHeader of requiredFieldsByHeader) { - const columnIndex = headersLowerCase.indexOf(requiredFieldByHeader.toLowerCase()); - - //if column does not exist, return csvWorksheet - if (columnIndex < 0) { - return csvWorksheet; - } - - const rowValueForColumn = row[columnIndex]; - - // Add an error if the cell value is empty - if (rowValueForColumn === undefined || rowValueForColumn === null || rowValueForColumn === '') { - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Missing Required Field', - message: `Missing required value for column`, - col: requiredFieldByHeader, - row: rowIndex + 2 - } - ]); - } - } - }); - - return csvWorksheet; - }; -}; - -export type ColumnCodeValidatorConfig = { - columnName: string; - column_code_validator: { - name?: string; - description?: string; - allowed_code_values: { name: string | number; description?: string }[]; - }; -}; - -/** - * For a specified column, adds an error for each row whose column value does not match a value in a specified set of - * allowed values (codes). - * - * Note: If the cell is empty, this check will be skipped. Use the `getRequiredFieldsValidator` validator to assert - * required fields. - * - * @param {ColumnCodeValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const getCodeValueFieldsValidator = (config?: ColumnCodeValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - if (!config.column_code_validator.allowed_code_values.length) { - return csvWorksheet; - } - - const rows = csvWorksheet.getRows(); - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - rows.forEach((row, rowIndex) => { - const columnIndex = headersLowerCase.indexOf(config.columnName.toLowerCase()); - - // if column does not exist, return - if (columnIndex < 0) { - return csvWorksheet; - } - - const rowValueForColumn = row[columnIndex]; - - if (rowValueForColumn === undefined || rowValueForColumn === null || rowValueForColumn === '') { - // cell is empty, use the getRequiredFieldsValidator to assert required fields - return csvWorksheet; - } - - // compare allowed code values as lowercase strings - const allowedCodeValuesLowerCase: string[] = []; - const allowedCodeValues = config.column_code_validator.allowed_code_values.map((allowedCode) => { - allowedCodeValuesLowerCase.push(allowedCode.name?.toString().toLowerCase()); - return allowedCode.name; - }); - - // Add an error if the cell value is not one of the elements in the codeValues array - if (!allowedCodeValuesLowerCase.includes(rowValueForColumn?.toLowerCase())) { - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Invalid Value', - message: `Invalid value: ${rowValueForColumn}. Must be one of [${allowedCodeValues.join(', ')}]`, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - }); - - return csvWorksheet; - }; -}; - -export type ColumnRangeValidatorConfig = { - columnName: string; - column_range_validator: { - name?: string; - description?: string; - min_value?: number; - max_value?: number; - }; -}; - -/** - * For a specified column, adds an error for each row whose column value does not match a specified range. - * - * Note: If the cell is empty, this check will be skipped. Use the `getRequiredFieldsValidator` validator to assert - * required fields. - * - * @param {ColumnRangeValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const getValidRangeFieldsValidator = (config?: ColumnRangeValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - const rows = csvWorksheet.getRows(); - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - rows.forEach((row, rowIndex) => { - const columnIndex = headersLowerCase.indexOf(config.columnName.toLowerCase()); - - // if column does not exist, return - if (columnIndex < 0) { - return csvWorksheet; - } - - const rowValueForColumn = Number(row[columnIndex]); - - if (isNaN(rowValueForColumn)) { - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Invalid Value', - message: `Invalid value: ${row[columnIndex]}. Value must be a number `, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - - if (config.column_range_validator.min_value && config.column_range_validator.max_value) { - // Value must be between min value and max value - if ( - rowValueForColumn < config.column_range_validator.min_value || - rowValueForColumn > config.column_range_validator.max_value - ) { - // Add an error if the cell value is not in the correct range provided in the array - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Out of Range', - message: `Invalid value: ${rowValueForColumn}. Value must be between ${config.column_range_validator.min_value} and ${config.column_range_validator.max_value} `, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - } else if (!config.column_range_validator.min_value && config.column_range_validator.max_value) { - // Value must be less than max value - if (rowValueForColumn > config.column_range_validator.max_value) { - // Add an error if the cell value is not in the correct range provided in the array - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Out of Range', - message: `Invalid value: ${rowValueForColumn}. Value must be less than ${config.column_range_validator.max_value} `, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - } else if (config.column_range_validator.min_value && !config.column_range_validator.max_value) { - // Value must be greater than min value - if (rowValueForColumn < config.column_range_validator.min_value) { - // Add an error if the cell value is not in the correct range provided in the array - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Out of Range', - message: `Invalid value: ${rowValueForColumn}. Value must be greater than ${config.column_range_validator.min_value} `, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - } - }); - - return csvWorksheet; - }; -}; - -export type ColumnNumericValidatorConfig = { - columnName: string; - column_numeric_validator: { - name?: string; - description?: string; - }; -}; - -/** - * For a specified column, adds an error for each row whose column value is not numeric. - * - * Note: If the cell is empty, this check will be skipped. Use the `getRequiredFieldsValidator` validator to assert - * required fields. - * - * @param {ColumnNumericValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const getNumericFieldsValidator = (config?: ColumnNumericValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - const rows = csvWorksheet.getRows(); - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - rows.forEach((row, rowIndex) => { - const columnIndex = headersLowerCase.indexOf(config.columnName.toLowerCase()); - - // if column does not exist, return - if (columnIndex < 0) { - return csvWorksheet; - } - - if (row[columnIndex] === undefined || row[columnIndex] === null) { - return csvWorksheet; - } - - const rowValueForColumn = Number(row[columnIndex]); - - if (isNaN(rowValueForColumn)) { - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Invalid Value', - message: `Invalid value: ${row[columnIndex]}. Value must be a number `, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - }); - - return csvWorksheet; - }; -}; - -export type ColumnFormatValidatorConfig = { - columnName: string; - column_format_validator: { - name?: string; - description?: string; - reg_exp: string; - reg_exp_flags?: string; - expected_format: string; - }; -}; - -/** - * For a specified column, adds an error for each row whose column value does not match a specified regex format. - * - * Note: If the cell is empty, this check will be skipped. Use the `getRequiredFieldsValidator` validator to assert - * required fields. - * - * @param {ColumnFormatValidatorConfig} [config] - * @return {*} {CSVValidator} - */ -export const getValidFormatFieldsValidator = (config?: ColumnFormatValidatorConfig): CSVValidator => { - return (csvWorksheet) => { - if (!config) { - return csvWorksheet; - } - - if (!config.column_format_validator.reg_exp) { - return csvWorksheet; - } - - const rows = csvWorksheet.getRows(); - const headersLowerCase = csvWorksheet.getHeadersLowerCase(); - - rows.forEach((row, rowIndex) => { - const columnIndex = headersLowerCase.indexOf(config.columnName.toLowerCase()); - - // if column does not exist, return - if (columnIndex < 0) { - return csvWorksheet; - } - - const rowValueForColumn = row[columnIndex]; - - if (rowValueForColumn === undefined || rowValueForColumn === null) { - return csvWorksheet; - } - - const regexFlags = config.column_format_validator.reg_exp_flags || ''; - - const regex = new RegExp(config.column_format_validator.reg_exp, regexFlags); - - // Add an error if the cell value is not in the correct range provided in the array - if (!regex.test(rowValueForColumn)) { - csvWorksheet.csvValidation.addRowErrors([ - { - errorCode: 'Unexpected Format', - message: `Unexpected Format: ${rowValueForColumn}. ${config.column_format_validator.expected_format}`, - col: config.columnName, - row: rowIndex + 2 - } - ]); - } - }); - - return csvWorksheet; - }; -}; diff --git a/api/src/utils/media/dwc/dwc-archive-file.test.ts b/api/src/utils/media/dwc/dwc-archive-file.test.ts deleted file mode 100644 index 415bfdbee..000000000 --- a/api/src/utils/media/dwc/dwc-archive-file.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import xlsx from 'xlsx'; -import { CSVWorksheet } from '../csv/csv-file'; -import { ArchiveFile } from '../media-file'; -import { DWCArchive } from './dwc-archive-file'; -chai.use(sinonChai); - -describe('dwc-archive-file', () => { - describe('DWCArchive', () => { - afterEach(() => { - sinon.restore(); - }); - - it('builds DWCAchive File with empty mediaFiles', () => { - const testFile = { - fileName: 'file', - mimetype: 'type', - buffer: {} as unknown as Buffer, - mediaFiles: [] - } as unknown as ArchiveFile; - - const dwca = new DWCArchive(testFile); - - expect(dwca.mediaValidation).eql({ fileName: 'file', fileErrors: [], isValid: true }); - }); - - it('builds DWCAchive File with only metaData', () => { - const testFile = { - fileName: 'file', - mimetype: 'type', - buffer: {} as unknown as Buffer, - mediaFiles: [{ name: 'eml' }] - } as unknown as ArchiveFile; - - const dwca = new DWCArchive(testFile); - - expect(dwca.mediaValidation).eql({ fileName: 'file', fileErrors: [], isValid: true }); - expect(dwca.isMetaDataOnly()).eql(true); - }); - - it('initializes worksheets with one file', () => { - const buffer = Buffer.alloc(1024 * 1024 * 10, '.'); - - const testFile = { - fileName: 'file', - mimetype: 'type', - buffer: {} as unknown as Buffer, - mediaFiles: [{ name: 'event', buffer: buffer }] - } as unknown as ArchiveFile; - - const dwca = new DWCArchive(testFile); - - const csvw = new CSVWorksheet('event', xlsx.read(buffer).Sheets['Sheet1']); - - expect(dwca.mediaValidation).eql({ fileName: 'file', fileErrors: [], isValid: true }); - expect(dwca.worksheets.event).eql(csvw); - }); - }); -}); diff --git a/api/src/utils/media/dwc/dwc-archive-file.ts b/api/src/utils/media/dwc/dwc-archive-file.ts deleted file mode 100644 index 5b4739e87..000000000 --- a/api/src/utils/media/dwc/dwc-archive-file.ts +++ /dev/null @@ -1,187 +0,0 @@ -import xlsx from 'xlsx'; -import { CSVWorksheet, ICsvState } from '../csv/csv-file'; -import { EMLFile } from '../eml/eml-file'; -import { ArchiveFile, IMediaState, MediaValidation } from '../media-file'; -import { ValidationSchemaParser } from '../validation/validation-schema-parser'; - -export enum DWC_CLASS { - EVENT = 'event', - OCCURRENCE = 'occurrence', - MEASUREMENTORFACT = 'measurementorfact', - RESOURCERELATIONSHIP = 'resourcerelationship', - TAXON = 'taxon', - LOCATION = 'location', - RECORD = 'record', - EML = 'eml' -} - -export const DEFAULT_XLSX_SHEET = 'Sheet1'; - -export type DWCWorksheets = { [name in DWC_CLASS]?: CSVWorksheet }; - -/** - * Supports Darwin Core Archive CSV files. - * - * Expects an array of known named-files - * - * @export - * @class DWCArchive - */ -export class DWCArchive { - rawFile: ArchiveFile; - - mediaValidation: MediaValidation; - - worksheets: DWCWorksheets; - - eml: EMLFile | undefined; - - constructor(archiveFile: ArchiveFile) { - this.rawFile = archiveFile; - - this.mediaValidation = new MediaValidation(this.rawFile.fileName); - - this.worksheets = {}; - - // parse archive files - this._initArchiveFiles(); - } - - _initArchiveFiles() { - // See https://www.npmjs.com/package/xlsx#parsing-options for details on parsing options - const parsingOptions: xlsx.ParsingOptions = { raw: true }; - - for (const rawFile of this.rawFile.mediaFiles) { - switch (rawFile.name) { - case DWC_CLASS.EVENT: - this.worksheets[DWC_CLASS.EVENT] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.OCCURRENCE: - this.worksheets[DWC_CLASS.OCCURRENCE] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.MEASUREMENTORFACT: - this.worksheets[DWC_CLASS.MEASUREMENTORFACT] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.RESOURCERELATIONSHIP: - this.worksheets[DWC_CLASS.RESOURCERELATIONSHIP] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.TAXON: - this.worksheets[DWC_CLASS.TAXON] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.LOCATION: - this.worksheets[DWC_CLASS.LOCATION] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.RECORD: - this.worksheets[DWC_CLASS.RECORD] = new CSVWorksheet( - rawFile.name, - xlsx.read(rawFile.buffer, parsingOptions).Sheets[DEFAULT_XLSX_SHEET] - ); - break; - case DWC_CLASS.EML: - this.eml = new EMLFile(rawFile); - break; - } - } - } - - /** - * This function checks worksheet data if the DwCArchive only contains metadata - * - * @returns {boolean} True if no worksheet data is present - */ - isMetaDataOnly(): boolean { - return ( - this.eml !== undefined && - !this.worksheets.event && - !this.worksheets.occurrence && - !this.worksheets.measurementorfact && - !this.worksheets.resourcerelationship && - !this.worksheets.taxon && - !this.worksheets.location && - !this.worksheets.record - ); - } - - isMediaValid(validationSchemaParser: ValidationSchemaParser): IMediaState { - const validators = validationSchemaParser.getSubmissionValidations(); - - const mediaValidation = this.validate(validators as DWCArchiveValidator[]); - - return mediaValidation.getState(); - } - - isContentValid(validationSchemaParser: ValidationSchemaParser): ICsvState[] { - const csvStates: ICsvState[] = []; - - Object.keys(this.worksheets).forEach((fileName) => { - const fileValidators = validationSchemaParser.getFileValidations(fileName); - - const columnValidators = validationSchemaParser.getAllColumnValidations(fileName); - - const validators = [...fileValidators, ...columnValidators]; - - const worksheet: CSVWorksheet = this.worksheets[fileName]; - - if (!worksheet) { - return; - } - - const csvValidation = worksheet.validate(validators); - - csvStates.push(csvValidation.getState()); - }); - - return csvStates; - } - - /** - * Executes each validator function in the provided `validators` against this instance, returning - * `this.mediaValidation` - * - * @param {DWCArchiveValidator[]} validators - * @return {*} {MediaValidation} - * @memberof DWCArchive - */ - validate(validators: DWCArchiveValidator[]): MediaValidation { - validators.forEach((validator) => validator(this)); - - return this.mediaValidation; - } - - /** - * Returns normalized DwC Archive file data - * - * @return {*} {string} - */ - normalize(): string { - const normalized = {}; - - Object.entries(this.worksheets).forEach(([key, value]) => { - if (value) { - normalized[key] = value.getRowObjects(); - } - }); - - return JSON.stringify(normalized); - } -} - -export type DWCArchiveValidator = (dwcArchive: DWCArchive) => DWCArchive; diff --git a/api/src/utils/media/eml/eml-file.ts b/api/src/utils/media/eml/eml-file.ts deleted file mode 100644 index 80a287407..000000000 --- a/api/src/utils/media/eml/eml-file.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { MediaFile } from '../media-file'; - -export class EMLFile { - emlFile: MediaFile; - - constructor(emlFile: MediaFile) { - this.emlFile = emlFile; - } -} diff --git a/api/src/utils/media/media-file.test.ts b/api/src/utils/media/media-file.test.ts deleted file mode 100644 index 86bd8d9d9..000000000 --- a/api/src/utils/media/media-file.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import { MediaFile, MediaValidation } from './media-file'; - -describe('MediaFile', () => { - it('constructs', () => { - const mediaFile = new MediaFile('fileName', 'mimetype', Buffer.from('')); - - expect(mediaFile).not.to.be.null; - }); - - describe('validate', () => { - it('calls all provided validator functions', () => { - const mediaFile = new MediaFile('fileName', 'mimetype', Buffer.from('')); - - expect(mediaFile).not.to.be.null; - - const mockValidationFunction1 = sinon.stub(); - const mockValidationFunction2 = sinon.stub(); - const mockValidationFunction3 = sinon.stub(); - - mediaFile.validate([mockValidationFunction1, mockValidationFunction2, mockValidationFunction3]); - - expect(mockValidationFunction1).to.have.been.calledOnce; - expect(mockValidationFunction2).to.have.been.calledOnce; - expect(mockValidationFunction3).to.have.been.calledOnce; - }); - }); -}); - -describe('MediaValidation', () => { - it('constructs', () => { - const mediaValidation = new MediaValidation('fileName'); - - expect(mediaValidation).not.to.be.null; - }); - - describe('addFileErrors', () => { - it('adds new file errors', () => { - const mediaValidation = new MediaValidation('fileName'); - - expect(mediaValidation).not.to.be.null; - - const fileError1 = 'a file error'; - const fileError2 = 'a second file error'; - - mediaValidation.addFileErrors([fileError1]); - - expect(mediaValidation.fileErrors).to.eql([fileError1]); - - mediaValidation.addFileErrors([fileError2]); - - expect(mediaValidation.fileErrors).to.eql([fileError1, fileError2]); - }); - }); - - describe('getState', () => { - it('gets the current validation state', () => { - const mediaValidation = new MediaValidation('fileName'); - - expect(mediaValidation).not.to.be.null; - - const fileError1 = 'a file error'; - - mediaValidation.addFileErrors([fileError1]); - - const validationState = mediaValidation.getState(); - - expect(validationState).to.eql({ - fileName: 'fileName', - fileErrors: [fileError1], - isValid: false - }); - }); - }); -}); diff --git a/api/src/utils/media/media-file.ts b/api/src/utils/media/media-file.ts deleted file mode 100644 index 64e6b96c1..000000000 --- a/api/src/utils/media/media-file.ts +++ /dev/null @@ -1,159 +0,0 @@ -export interface IMediaFile { - fileName: string; - mimetype: string; - buffer: Buffer; - mediaValidation: MediaValidation; -} - -/** - * A generic wrapper for any media file. - * - * @export - * @class MediaFile - * @implements {IMediaFile} - */ -export class MediaFile implements IMediaFile { - fileName: string; - mimetype: string; - buffer: Buffer; - mediaValidation: MediaValidation; - - constructor(fileName: string, mimetype: string, buffer: Buffer) { - this.fileName = fileName.toLowerCase(); - this.mimetype = mimetype; - this.buffer = buffer; - this.mediaValidation = new MediaValidation(this.fileName); - } - - /** - * The file name without extension. - * - * @readonly - * @type {string} - * @memberof MediaFile - */ - get name(): string { - const lastPeriodindex = this.fileName.lastIndexOf('.'); - - if (lastPeriodindex >= 0) { - // strip out the file extension, if it exists - return this.fileName.substring(0, lastPeriodindex); - } else { - return this.fileName; - } - } - - /** - * Executes each validator function in the provided `validators` against this instance, returning - * `this.mediaValidation` - * - * @param {MediaValidator[]} validators - * @return {*} {MediaValidation} - * @memberof MediaFile - */ - validate(validators: MediaValidator[]): MediaValidation { - validators.forEach((validator) => validator(this)); - - return this.mediaValidation; - } -} - -export type MediaValidator = (mediaFile: IMediaFile) => IMediaFile; - -/** - * A generic wrapper for any archive file. - * - * @class ArchiveFile - * @implements {IMediaFile} - */ -export class ArchiveFile implements IMediaFile { - fileName: string; - mimetype: string; - buffer: Buffer; - mediaValidation: MediaValidation; - - mediaFiles: MediaFile[]; - - constructor(fileName: string, mimetype: string, buffer: Buffer, mediaFiles: MediaFile[]) { - this.fileName = fileName.toLowerCase(); - this.mimetype = mimetype; - this.buffer = buffer; - this.mediaValidation = new MediaValidation(this.fileName); - - this.mediaFiles = mediaFiles; - } - - /** - * The file name without extension. - * - * @readonly - * @type {string} - * @memberof ArchiveFile - */ - get name(): string { - const lastPeriodindex = this.fileName.lastIndexOf('.'); - - if (lastPeriodindex >= 0) { - // strip out the file extension, if it exists - return this.fileName.substring(0, lastPeriodindex); - } else { - return this.fileName; - } - } - - /** - * Executes each validator function in the provided `validators` against this instance, returning - * `this.mediaValidation` - * - * @param {ArchiveValidator[]} validators - * @return {*} {MediaValidation} - * @memberof ArchiveFile - */ - validate(validators: ArchiveValidator[]): MediaValidation { - validators.forEach((validator) => validator(this)); - - return this.mediaValidation; - } -} - -export type ArchiveValidator = (archiveFile: ArchiveFile) => ArchiveFile; - -export interface IMediaState { - fileName: string; - fileErrors?: string[]; - isValid: boolean; -} - -/** - * Supports getting/setting validation errors for any media file. - * - * @export - * @class MediaValidation - */ -export class MediaValidation { - fileName: string; - fileErrors: string[]; - isValid: boolean; - - constructor(fileName: string) { - this.fileName = fileName; - this.fileErrors = []; - this.isValid = true; - } - - addFileErrors(errors: string[]) { - this.fileErrors = this.fileErrors.concat(errors); - - if (errors?.length) { - this.isValid = false; - } - } - - getState(): IMediaState { - return { - fileName: this.fileName, - fileErrors: this.fileErrors, - isValid: this.isValid - }; - } -} diff --git a/api/src/utils/media/media-utils.test.ts b/api/src/utils/media/media-utils.test.ts deleted file mode 100644 index 71b7927f6..000000000 --- a/api/src/utils/media/media-utils.test.ts +++ /dev/null @@ -1,203 +0,0 @@ -import AdmZip from 'adm-zip'; -import { GetObjectOutput } from 'aws-sdk/clients/s3'; -import chai, { expect } from 'chai'; -import { describe } from 'mocha'; -import sinon from 'sinon'; -import sinonChai from 'sinon-chai'; -import { ArchiveFile, MediaFile } from './media-file'; -import * as media_utils from './media-utils'; - -chai.use(sinonChai); - -describe('parseUnknownMedia', () => { - afterEach(() => { - sinon.restore(); - }); - - it('calls parseUnknownMulterFile', () => { - const parseUnknownMulterFileStub = sinon.stub(media_utils, 'parseUnknownMulterFile'); - - media_utils.parseUnknownMedia({ originalname: 'name' } as unknown as Express.Multer.File); - - expect(parseUnknownMulterFileStub).to.have.been.calledOnce; - }); - - it('calls parseUnknownS3File', () => { - const parseUnknownS3FileStub = sinon.stub(media_utils, 'parseUnknownS3File'); - - media_utils.parseUnknownMedia({} as unknown as GetObjectOutput); - - expect(parseUnknownS3FileStub).to.have.been.calledOnce; - }); -}); - -describe('parseUnknownMulterFile', () => { - it('returns a MediaFile', () => { - const multerFile = { - originalname: 'file1.txt', - buffer: Buffer.from('file1data') - } as unknown as Express.Multer.File; - - const response = media_utils.parseUnknownMulterFile(multerFile); - - expect(response).to.eql(new MediaFile('file1.txt', 'text/plain', Buffer.from('file1data'))); - }); - - it('returns an ArchiveFile, when a zip file is provided', () => { - const zipFile = new AdmZip(); - - zipFile.addFile('file1.txt', Buffer.from('file1data')); - zipFile.addFile('folder2/', Buffer.from('')); // add folder - zipFile.addFile('folder2/file2.csv', Buffer.from('file2data')); - - const multerFile = { originalname: 'zipFile.zip', buffer: zipFile.toBuffer() } as unknown as Express.Multer.File; - - const response = media_utils.parseUnknownMulterFile(multerFile); - - expect(response).to.eql( - new ArchiveFile('zipFile.zip', 'application/zip', zipFile.toBuffer(), [ - new MediaFile('file1.txt', 'text/plain', Buffer.from('file1data')), - new MediaFile('file2.csv', 'text/csv', Buffer.from('file2data')) - ]) - ); - }); -}); - -describe('parseUnknownS3File', () => { - it('returns a MediaFile', () => { - const s3File = { - Metadata: { filename: 'file1.txt' }, - Body: Buffer.from('file1data') - } as unknown as GetObjectOutput; - - const response = media_utils.parseUnknownS3File(s3File); - - expect(response).to.eql(new MediaFile('file1.txt', 'text/plain', Buffer.from('file1data'))); - }); - - it('returns an ArchiveFile, when a zip file is provided', () => { - const zipFile = new AdmZip(); - - zipFile.addFile('file1.txt', Buffer.from('file1data')); - zipFile.addFile('folder2/', Buffer.from('')); // add folder - zipFile.addFile('folder2/file2.csv', Buffer.from('file2data')); - - const s3File = { - Metadata: { filename: 'zipFile.zip' }, - ContentType: 'application/zip', - Body: zipFile.toBuffer() - } as unknown as GetObjectOutput; - - const response = media_utils.parseUnknownS3File(s3File); - - expect(response).to.eql( - new ArchiveFile('zipFile.zip', 'application/zip', zipFile.toBuffer(), [ - new MediaFile('file1.txt', 'text/plain', Buffer.from('file1data')), - new MediaFile('file2.csv', 'text/csv', Buffer.from('file2data')) - ]) - ); - }); -}); - -describe('parseUnknownZipFile', () => { - it('returns an array of MediaFile elements', () => { - const zipFile = new AdmZip(); - - zipFile.addFile('file1.txt', Buffer.from('file1data')); - zipFile.addFile('folder2/', Buffer.from('')); // add folder - zipFile.addFile('folder2/file2.csv', Buffer.from('file2data')); - - const multerFile = { originalname: 'zipFile.zip', buffer: zipFile.toBuffer() } as unknown as Express.Multer.File; - - const response = media_utils.parseUnknownZipFile(multerFile.buffer); - - expect(response.length).to.equal(2); - expect(response[0]).to.eql(new MediaFile('file1.txt', 'text/plain', Buffer.from('file1data'))); - expect(response[1]).to.eql(new MediaFile('file2.csv', 'text/csv', Buffer.from('file2data'))); - }); - - it('returns an empty array if the zip contains no files', () => { - const zipFile = new AdmZip(); - - zipFile.addFile('folder2/', Buffer.from('')); // add folder - - const multerFile = { originalname: 'zipFile.zip', buffer: zipFile.toBuffer() } as unknown as Express.Multer.File; - - const response = media_utils.parseUnknownZipFile(multerFile.buffer); - - expect(response.length).to.equal(0); - }); -}); - -describe('parseMulterFile', () => { - it('returns a MediaFile item', () => { - const multerFile = { - originalname: 'file1.csv', - buffer: Buffer.from('file1data') - } as unknown as Express.Multer.File; - - const response = media_utils.parseMulterFile(multerFile); - - expect(response).to.eql(new MediaFile('file1.csv', 'text/csv', Buffer.from('file1data'))); - }); - - it('returns a MediaFile item when the file mime type is unknown', () => { - const multerFile = { - originalname: 'file1.notAKnownMimeTypecsv', - buffer: Buffer.from('file1data') - } as unknown as Express.Multer.File; - - const response = media_utils.parseMulterFile(multerFile); - - expect(response).to.eql(new MediaFile('file1.notAKnownMimeTypecsv', '', Buffer.from('file1data'))); - }); - - it('returns a MediaFile item when the file buffer is null', () => { - const multerFile = { - originalname: 'file1.csv', - buffer: null - } as unknown as Express.Multer.File; - - const response = media_utils.parseMulterFile(multerFile); - - expect(response).to.eql(new MediaFile('file1.csv', 'text/csv', null as unknown as Buffer)); - }); -}); - -describe('parseS3File', () => { - it('returns a MediaFile item', () => { - const s3File = { - Metadata: { filename: 'file1.csv' }, - ContentType: 'text/csv', - Body: Buffer.from('file1data') - } as unknown as GetObjectOutput; - - const response = media_utils.parseS3File(s3File); - - expect(response).to.eql(new MediaFile('file1.csv', 'text/csv', Buffer.from('file1data'))); - }); - - it('returns a MediaFile item when the file mime type is unknown', () => { - const s3File = { - Metadata: { filename: 'file1.notAKnownMimeTypecsv' }, - ContentType: 'notAKnownMimeTypecsv', - Body: Buffer.from('file1data') - } as unknown as GetObjectOutput; - - const response = media_utils.parseS3File(s3File); - - expect(response).to.eql(new MediaFile('file1.notAKnownMimeTypecsv', '', Buffer.from('file1data'))); - }); - - it('returns a MediaFile item when the file buffer is null', () => { - const s3File = { - Metadata: { filename: 'file1.csv' }, - ContentType: 'text/csv', - Body: null - } as unknown as GetObjectOutput; - - const response = media_utils.parseS3File(s3File); - - expect(response).to.eql(new MediaFile('file1.csv', 'text/csv', null as unknown as Buffer)); - }); -}); diff --git a/api/src/utils/media/media-utils.ts b/api/src/utils/media/media-utils.ts deleted file mode 100644 index 13baf490a..000000000 --- a/api/src/utils/media/media-utils.ts +++ /dev/null @@ -1,119 +0,0 @@ -import AdmZip from 'adm-zip'; -import { GetObjectOutput } from 'aws-sdk/clients/s3'; -import mime from 'mime'; -import { ArchiveFile, MediaFile } from './media-file'; - -export type UnknownMedia = Express.Multer.File | GetObjectOutput; - -export type KnownMedia = MediaFile | ArchiveFile; - -/** - * Parses an unknown file into an array of MediaFile. - * - * Note: The array will always have 1 item unless the unknown file is a zip file containing multiple files, in which - * case the array will have 1 item per file in the zip (folders ignored). - * - * @param {(UnknownMedia)} rawMedia - * @return {*} {(KnownMedia | null)} - */ -export const parseUnknownMedia = (rawMedia: UnknownMedia): KnownMedia | null => { - if ((rawMedia as Express.Multer.File).originalname) { - return parseUnknownMulterFile(rawMedia as Express.Multer.File); - } else { - return parseUnknownS3File(rawMedia as GetObjectOutput); - } -}; - -export const parseUnknownMulterFile = (rawMedia: Express.Multer.File): KnownMedia | null => { - const mimetype = mime.getType(rawMedia.originalname); - - if (isZipMimetype(mimetype || '')) { - const archiveFile = parseMulterFile(rawMedia); - const mediaFiles = parseUnknownZipFile(rawMedia.buffer); - - return new ArchiveFile(archiveFile.fileName, archiveFile.mimetype, archiveFile.buffer, mediaFiles); - } - - return parseMulterFile(rawMedia); -}; - -export const parseUnknownS3File = (rawMedia: GetObjectOutput): KnownMedia | null => { - const mimetype = rawMedia.ContentType; - - if (isZipMimetype(mimetype || '')) { - if (!rawMedia.Body) { - return null; - } - - const archiveFile = parseS3File(rawMedia); - const mediaFiles = parseUnknownZipFile(rawMedia.Body as Buffer); - - return new ArchiveFile(archiveFile.fileName, archiveFile.mimetype, archiveFile.buffer, mediaFiles); - } - - return parseS3File(rawMedia); -}; - -/** - * Parse a zip file buffer into an array of MediaFile. - * - * Note: Ignores any directory structures, flattening all nested files into a single array. - * - * @param {Buffer} zipFile - * @return {*} {ArchiveFile} - */ -export const parseUnknownZipFile = (zipFile: Buffer): MediaFile[] => { - const unzippedFile = new AdmZip(zipFile); - const entries = unzippedFile.getEntries(); - - return entries - .filter((item) => !item.isDirectory) - .map((item) => { - const fileName = item?.name; - const mimetype = mime.getType(fileName) || ''; - const buffer = item?.getData(); - - return new MediaFile(fileName, mimetype, buffer); - }); -}; - -/** - * Parse a single file into an array of MediaFile with 1 element. - * - * @param {Express.Multer.File} file - * @return {*} {MediaFile} - */ -export const parseMulterFile = (file: Express.Multer.File): MediaFile => { - const fileName = file?.originalname; - const mimetype = mime.getType(fileName) || ''; - const buffer = file?.buffer; - - return new MediaFile(fileName, mimetype, buffer); -}; - -/** - * Parse a single file into an array of MediaFile with 1 element. - * - * @param {GetObjectOutput} file - * @return {*} {MediaFile} - */ -export const parseS3File = (file: GetObjectOutput): MediaFile => { - const fileName = file?.Metadata?.filename || ''; - const mimetype = mime.getType(fileName) || ''; - const buffer = file?.Body as Buffer; - - return new MediaFile(fileName, mimetype, buffer); -}; - -export const isZipMimetype = (mimetype: string): boolean => { - if (!mimetype) { - return false; - } - - return [ - /application\/zip/, - /application\/x-zip-compressed/, - /application\/x-rar-compressed/, - /application\/octet-stream/ - ].some((regex) => regex.test(mimetype)); -}; diff --git a/api/src/utils/media/validation/file-type-and-content-validator.test.ts b/api/src/utils/media/validation/file-type-and-content-validator.test.ts deleted file mode 100644 index 786ece123..000000000 --- a/api/src/utils/media/validation/file-type-and-content-validator.test.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import xlsx from 'xlsx'; -import { DEFAULT_XLSX_SHEET, DWCArchive, DWCArchiveValidator } from '../dwc/dwc-archive-file'; -import { ArchiveFile, IMediaFile, MediaFile, MediaValidation } from '../media-file'; -import { XLSXCSV, XLSXCSVValidator } from '../xlsx/xlsx-file'; -import { - getFileEmptyValidator, - getFileMimeTypeValidator, - getRequiredFilesValidator -} from './file-type-and-content-validator'; - -describe('getFileEmptyValidator', () => { - it('adds an error when the buffer is empty', () => { - const validator = getFileEmptyValidator(); - - const mediaFile: IMediaFile = { - fileName: 'testName', - mimetype: 'testMime', - buffer: Buffer.from(''), // empty buffer - mediaValidation: new MediaValidation('testName') - }; - - validator(mediaFile); - - expect(mediaFile.mediaValidation.fileErrors).to.eql(['File is empty']); - }); - - it('adds no errors when the buffer is not empty', () => { - const validator = getFileEmptyValidator(); - - const mediaFile: IMediaFile = { - fileName: 'testName', - mimetype: 'testMime', - buffer: Buffer.from([123]), // non-empty buffer - mediaValidation: new MediaValidation('testName') - }; - - validator(mediaFile); - - expect(mediaFile.mediaValidation.fileErrors).to.eql([]); - }); -}); - -describe('getFileMimeTypeValidator', () => { - it('adds an error when the mime type is invalid', () => { - const validMimetypes = { - mimetype_validator: { - reg_exps: ['validMime'] - } - }; - - const validator = getFileMimeTypeValidator(validMimetypes) as XLSXCSVValidator; - - const mediaFile = new MediaFile('testName', 'badMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql(['File mime type is invalid, must be one of: validMime']); - }); - - it('adds no errors when the mime type is valid', () => { - const validMimetypes = { - mimetype_validator: { - reg_exps: ['validMime'] - } - }; - - const validator = getFileMimeTypeValidator(validMimetypes) as DWCArchiveValidator; - - const mediaFile = new MediaFile('otherName', 'otherMime', Buffer.from('')); - - const archiveFile = new ArchiveFile('testName', 'validMime', Buffer.from(''), [mediaFile]); - - const dwcArchive = new DWCArchive(archiveFile); - - validator(dwcArchive); - - expect(dwcArchive.mediaValidation.fileErrors).to.eql([]); - }); - - it('adds no errors when no valid mimes provided', () => { - const validMimetypes = { - mimetype_validator: { - reg_exps: [] - } - }; - - const validator = getFileMimeTypeValidator(validMimetypes) as XLSXCSVValidator; - - const mediaFile = new MediaFile('testName', 'validMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql([]); - }); - - it('adds no errors when config it not found', () => { - const validMimetypes = undefined; - - const validator = getFileMimeTypeValidator(validMimetypes) as DWCArchiveValidator; - - const mediaFile = new MediaFile('otherName', 'otherMime', Buffer.from('')); - - const archiveFile = new ArchiveFile('testName', 'validMime', Buffer.from(''), [mediaFile]); - - const dwcArchive = new DWCArchive(archiveFile); - - validator(dwcArchive); - - expect(dwcArchive.mediaValidation.fileErrors).to.eql([]); - }); -}); - -describe('getRequiredFilesValidator', () => { - it('adds no error an when the submitted file list is undefined', () => { - const submissionRequiredFilesValidatorConfig = undefined; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as XLSXCSVValidator; - - const mediaFile = new MediaFile('testName', 'validMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql([]); - }); - - it('adds no error an when the required file list is empty', () => { - const submissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - required_files: [] - } - }; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as XLSXCSVValidator; - - const mediaFile = new MediaFile('testName', 'validMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql([]); - }); - - it('checks that a submission is a valid DWCArchive, and adds an error if a required file is missing', () => { - const submissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - required_files: ['event', 'occurrence'] - } - }; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as DWCArchiveValidator; - - const archiveFile = new ArchiveFile('testName', 'validMime', Buffer.from(''), [ - new MediaFile('occurrence', 'b', Buffer.from('')) - ]); - - const xlsxCSV = new DWCArchive(archiveFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql(['Missing required file: event']); - }); - - it('checks that a submission is a valid XLSXCSV, and adds an error if a required file is missing', () => { - const submissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - required_files: ['sheet2'] - } - }; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as XLSXCSVValidator; - - const newWorkbook = xlsx.utils.book_new(); - - const worksheet = xlsx.utils.aoa_to_sheet([[]]); - - xlsx.utils.book_append_sheet(newWorkbook, worksheet, DEFAULT_XLSX_SHEET); - - const mediaFile = new MediaFile('worksheet', 'validMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql(['Missing required sheet: sheet2']); - }); -}); - -describe('checkRequiredFieldsInDWCArchive', () => { - it('checks that a submission is a valid DWCArchive with an empty mediaFile, and adds an error if a required file is missing', () => { - const submissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - required_files: ['event'] - } - }; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as DWCArchiveValidator; - - //empty media file - const archiveFile = new ArchiveFile('someFile', 'validMime', Buffer.from(''), []); - - const xlsxCSV = new DWCArchive(archiveFile); - - validator(xlsxCSV); - - expect(xlsxCSV.mediaValidation.fileErrors).to.eql(['Missing required file: event']); - }); - - it('checks that a submission is a valid XLSXCSV with an empty workbook, and adds an error if a required file is missing', () => { - const submissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - required_files: ['sheet2'] - } - }; - - const validator = getRequiredFilesValidator(submissionRequiredFilesValidatorConfig) as XLSXCSVValidator; - - const newWorkbook = xlsx.utils.book_new(); - - const worksheet = xlsx.utils.aoa_to_sheet([[]]); - - xlsx.utils.book_append_sheet(newWorkbook, worksheet, DEFAULT_XLSX_SHEET); - - const mediaFile = new MediaFile('worksheet', 'validMime', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - //force worksheets to be empty - xlsxCSV.workbook.worksheets = {}; - - validator(xlsxCSV); - expect(xlsxCSV.mediaValidation.fileErrors).to.eql(['Missing required sheet: sheet2']); - }); -}); diff --git a/api/src/utils/media/validation/file-type-and-content-validator.ts b/api/src/utils/media/validation/file-type-and-content-validator.ts deleted file mode 100644 index d697e5041..000000000 --- a/api/src/utils/media/validation/file-type-and-content-validator.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { DWCArchive, DWCArchiveValidator } from '../dwc/dwc-archive-file'; -import { MediaValidator } from '../media-file'; -import { XLSXCSV, XLSXCSVValidator } from '../xlsx/xlsx-file'; - -export const getFileEmptyValidator = (): MediaValidator => { - return (mediaFile) => { - if (!mediaFile.buffer || !mediaFile.buffer.byteLength) { - mediaFile.mediaValidation.addFileErrors(['File is empty']); - } - - return mediaFile; - }; -}; - -export type MimetypeValidatorConfig = { - mimetype_validator: { - name?: string; - description?: string; - reg_exps: string[]; - }; -}; - -export const getFileMimeTypeValidator = (config?: MimetypeValidatorConfig): DWCArchiveValidator | XLSXCSVValidator => { - return (file: any) => { - if (!config) { - return file; - } - - if (!config.mimetype_validator.reg_exps.length) { - return file; - } - - if ( - !config.mimetype_validator.reg_exps.some((regexString) => { - const regex = new RegExp(regexString, 'i'); - - return regex.test(file.rawFile.mimetype); - }) - ) { - file.mediaValidation.addFileErrors([ - `File mime type is invalid, must be one of: ${config.mimetype_validator.reg_exps.join(', ')}` - ]); - } - - return file; - }; -}; - -export type SubmissionRequiredFilesValidatorConfig = { - submission_required_files_validator: { - name?: string; - description?: string; - required_files: string[]; - }; -}; - -export const getRequiredFilesValidator = ( - config?: SubmissionRequiredFilesValidatorConfig -): DWCArchiveValidator | XLSXCSVValidator => { - return (file: any) => { - if (!config) { - // No required files specified - return file; - } - - if (!config.submission_required_files_validator.required_files.length) { - // No required files specified - return file; - } - - if (file instanceof DWCArchive) { - checkRequiredFieldsInDWCArchive(file, config); - } else if (file instanceof XLSXCSV) { - checkRequiredFieldsInXLSXCSV(file, config); - } - - return file; - }; -}; - -const checkRequiredFieldsInDWCArchive = (dwcArchive: DWCArchive, config: SubmissionRequiredFilesValidatorConfig) => { - // If there are no files in the archive, then add errors for all required files - if (!dwcArchive.rawFile.mediaFiles || !dwcArchive.rawFile.mediaFiles.length) { - dwcArchive.mediaValidation.addFileErrors( - config.submission_required_files_validator.required_files.map((requiredFile) => { - return `Missing required file: ${requiredFile}`; - }) - ); - - return dwcArchive; - } - - const fileNames = dwcArchive.rawFile.mediaFiles.map((mediaFile) => mediaFile.name); - - config.submission_required_files_validator.required_files.forEach((requiredFile) => { - if (!fileNames.includes(requiredFile.toLowerCase())) { - dwcArchive.mediaValidation.addFileErrors([`Missing required file: ${requiredFile}`]); - } - }); -}; - -const checkRequiredFieldsInXLSXCSV = (xlsxCsv: XLSXCSV, config: SubmissionRequiredFilesValidatorConfig) => { - // If there are no sheets in the excel file, then add errors for all required sheets - - if (!xlsxCsv.workbook || !xlsxCsv.workbook.worksheets || !Object.keys(xlsxCsv.workbook.worksheets).length) { - xlsxCsv.mediaValidation.addFileErrors( - config.submission_required_files_validator.required_files.map((requiredFile) => { - return `Missing required sheet: ${requiredFile}`; - }) - ); - - return xlsxCsv; - } - - const worksheetNames = Object.keys(xlsxCsv.workbook.worksheets).map((item) => item.toLowerCase()); - - config.submission_required_files_validator.required_files.forEach((requiredFile) => { - if (!worksheetNames.includes(requiredFile.toLowerCase())) { - xlsxCsv.mediaValidation.addFileErrors([`Missing required sheet: ${requiredFile}`]); - } - }); -}; diff --git a/api/src/utils/media/validation/validation-schema-parser.test.ts b/api/src/utils/media/validation/validation-schema-parser.test.ts deleted file mode 100644 index 051913603..000000000 --- a/api/src/utils/media/validation/validation-schema-parser.test.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import { getFileMimeTypeValidator } from './file-type-and-content-validator'; -import { ValidationRulesRegistry, ValidationSchemaParser } from './validation-schema-parser'; - -const sampleValidationSchema = { - files: [ - { - name: 'testFile1', - columns: [ - { - name: 'column1', - validations: [ - { - column_code_validator: {} - }, - { - not_a_known_validator: {} - } - ] - }, - { - name: 'column2', - validations: [ - { - column_format_validator: {} - }, - { - column_range_validator: {} - } - ] - } - ], - validations: [ - { - file_duplicate_columns_validator: {} - }, - { - file_required_columns_validator: {} - }, - { - not_a_known_validator: {} - } - ] - }, - { - name: 'testFile2', - columns: [ - { - name: 'column3', - validations: [] - }, - { - name: 'column4', - validations: [] - } - ], - validations: [] - } - ], - validations: [ - { - mimetype_validator: {} - }, - { - submission_required_files_validator: {} - }, - { - not_a_known_validator: {} - } - ] -}; - -describe('ValidationRulesRegistry', () => { - describe('findMatchingRule', () => { - it('returns a generator function for a known rule', () => { - const rule = ValidationRulesRegistry.findMatchingRule('mimetype_validator'); - - expect(rule).to.eql(getFileMimeTypeValidator); - }); - - it('returns undefined for an unknown rule', () => { - const rule = ValidationRulesRegistry.findMatchingRule('not_a_known_rule'); - - expect(rule).to.equal(undefined); - }); - }); -}); - -describe('ValidationSchemaParser', () => { - it('constructs', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - expect(validationSchemaParser).not.to.be.null; - }); - - describe('getSubmissionValidations', () => { - it('returns an array of validator functions', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validators = validationSchemaParser.getSubmissionValidations(); - - expect(validators.length).to.equal(2); - - expect(typeof validators[0]).to.equal('function'); - expect(typeof validators[1]).to.equal('function'); - }); - }); - - describe('getFileValidations', () => { - it('returns an array of validator functions', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validators = validationSchemaParser.getFileValidations('testFile1'); - - expect(validators.length).to.equal(2); - - expect(typeof validators[0]).to.equal('function'); - expect(typeof validators[1]).to.equal('function'); - }); - }); - - describe('getAllColumnValidations', () => { - it('returns an array of validator functions', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validators = validationSchemaParser.getAllColumnValidations('testFile1'); - - expect(validators.length).to.equal(3); - - expect(typeof validators[0]).to.equal('function'); - expect(typeof validators[1]).to.equal('function'); - expect(typeof validators[2]).to.equal('function'); - }); - }); - - describe('getColumnValidations', () => { - it('returns an array of validator functions', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validators = validationSchemaParser.getColumnValidations('testFile1', 'column1'); - - expect(validators.length).to.equal(1); - - expect(typeof validators[0]).to.equal('function'); - }); - }); - - describe('getSubmissionValidationSchemas', () => { - it('returns an array of validation schemas', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validationSchemas = validationSchemaParser.getSubmissionValidationSChemas(); - - expect(validationSchemas).to.eql([ - { mimetype_validator: {} }, - { submission_required_files_validator: {} }, - { not_a_known_validator: {} } - ]); - }); - }); - - describe('getFileValidationSchemas', () => { - it('returns an array of validation schemas', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validationSchemas = validationSchemaParser.getFileValidationSchemas('testFile1'); - - expect(validationSchemas).to.eql([ - { file_duplicate_columns_validator: {} }, - { file_required_columns_validator: {} }, - { not_a_known_validator: {} } - ]); - }); - }); - - describe('getColumnValidationSchemas', () => { - it('returns an array of validation schemas', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const validationSchemas = validationSchemaParser.getColumnValidationSchemas('testFile1', 'column1'); - - expect(validationSchemas).to.eql([{ column_code_validator: {} }, { not_a_known_validator: {} }]); - }); - }); - - describe('getColumnNames', () => { - it('returns an array of column names', () => { - const validationSchemaParser = new ValidationSchemaParser(sampleValidationSchema); - - const columnNames = validationSchemaParser.getColumnNames('testFile1'); - - expect(columnNames).to.eql(['column1', 'column2']); - }); - }); - - describe('getSubmissionValidationsJsonPath', () => { - it('returns a json path string', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const jsonPath = validationSchemaParser.getSubmissionValidationsJsonPath(); - - expect(jsonPath).to.equal('$.validations'); - }); - }); - - describe('getFileValidationsJsonPath', () => { - it('returns a json path string', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const jsonPath = validationSchemaParser.getFileValidationsJsonPath('testName'); - - expect(jsonPath).to.equal("$.files[?(@.name == 'testName')].validations"); - }); - }); - - describe('getColumnNamesJsonpath', () => { - it('returns a json path string', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const jsonPath = validationSchemaParser.getColumnNamesJsonpath('testName'); - - expect(jsonPath).to.equal("$.files[?(@.name == 'testName')].columns[*].name"); - }); - }); - - describe('getColumnValidationsJsonpath', () => { - it('returns a json path string', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const jsonPath = validationSchemaParser.getColumnValidationsJsonpath('testName', 'columnName'); - - expect(jsonPath).to.equal("$.files[?(@.name == 'testName')].columns[?(@.name == 'columnName')].validations"); - }); - }); - - describe('parseJson', () => { - it('parses a provided json string and returns a json object', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const str = '{"test":123}'; - - const json = validationSchemaParser.parseJson(str); - - expect(json).to.eql({ test: 123 }); - }); - - it('throws an error if the provided json is invalid', () => { - const validationSchemaParser = new ValidationSchemaParser({}); - - const str = 'not a json string'; - - try { - validationSchemaParser.parseJson(str); - } catch (error) { - expect((error as Error).message).to.equal('ValidationSchemaParser - provided json was not valid JSON'); - } - }); - }); -}); diff --git a/api/src/utils/media/validation/validation-schema-parser.ts b/api/src/utils/media/validation/validation-schema-parser.ts deleted file mode 100644 index 5e2bcc277..000000000 --- a/api/src/utils/media/validation/validation-schema-parser.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { JSONPath } from 'jsonpath-plus'; -import { CSVValidator } from '../csv/csv-file'; -import { - getDuplicateHeadersValidator, - getValidHeadersValidator, - hasRecommendedHeadersValidator, - hasRequiredHeadersValidator -} from '../csv/validation/csv-header-validator'; -import { - getCodeValueFieldsValidator, - getNumericFieldsValidator, - getRequiredFieldsValidator, - getValidFormatFieldsValidator, - getValidRangeFieldsValidator -} from '../csv/validation/csv-row-validator'; -import { DWCArchiveValidator } from '../dwc/dwc-archive-file'; -import { XLSXCSVValidator } from '../xlsx/xlsx-file'; -import { - getFileEmptyValidator, - getFileMimeTypeValidator, - getRequiredFilesValidator -} from './file-type-and-content-validator'; - -export const ValidationRulesRegistry = { - registry: [ - { - name: '', - generator: getFileEmptyValidator - }, - { - name: 'mimetype_validator', - generator: getFileMimeTypeValidator - }, - { - name: 'submission_required_files_validator', - generator: getRequiredFilesValidator - }, - { - name: 'file_duplicate_columns_validator', - generator: getDuplicateHeadersValidator - }, - { - name: 'file_required_columns_validator', - generator: hasRequiredHeadersValidator - }, - { - name: 'file_recommended_columns_validator', - generator: hasRecommendedHeadersValidator - }, - { - name: 'file_valid_columns_validator', - generator: getValidHeadersValidator - }, - { - name: '', - generator: getRequiredFieldsValidator - }, - { - name: 'column_code_validator', - generator: getCodeValueFieldsValidator - }, - { - name: 'column_range_validator', - generator: getValidRangeFieldsValidator - }, - { - name: 'column_format_validator', - generator: getValidFormatFieldsValidator - }, - { - name: 'column_numeric_validator', - generator: getNumericFieldsValidator - } - ], - findMatchingRule(name: string): any { - return this.registry.find((item) => item.name === name)?.generator; - } -}; - -export class ValidationSchemaParser { - validationSchema: object; - - constructor(validationSchema: string | object) { - if (typeof validationSchema === 'string') { - this.validationSchema = this.parseJson(validationSchema); - } else { - this.validationSchema = validationSchema; - } - } - - getSubmissionValidations(): (DWCArchiveValidator | XLSXCSVValidator)[] { - const validationSchemas = this.getSubmissionValidationSChemas(); - - const rules: (DWCArchiveValidator | XLSXCSVValidator)[] = []; - - validationSchemas.forEach((validationSchema) => { - const keys = Object.keys(validationSchema); - - if (keys.length !== 1) { - return; - } - - const key = keys[0]; - - const generatorFunction = ValidationRulesRegistry.findMatchingRule(key); - - if (!generatorFunction) { - return; - } - - const rule = generatorFunction(validationSchema); - - rules.push(rule); - }); - - return rules; - } - - getFileValidations(fileName: string): CSVValidator[] { - const validationSchemas = this.getFileValidationSchemas(fileName); - - const rules: CSVValidator[] = []; - - validationSchemas.forEach((validationSchema) => { - const keys = Object.keys(validationSchema); - - if (keys.length !== 1) { - return; - } - - const key = keys[0]; - - const generatorFunction = ValidationRulesRegistry.findMatchingRule(key); - - if (!generatorFunction) { - return; - } - - const rule = generatorFunction(validationSchema); - - rules.push(rule); - }); - - return rules; - } - - getAllColumnValidations(fileName: string): CSVValidator[] { - const columnNames = this.getColumnNames(fileName); - - let rules: CSVValidator[] = []; - - columnNames.forEach((columnName) => { - const columnValidators = this.getColumnValidations(fileName, columnName); - - rules = rules.concat(columnValidators); - }); - - return rules; - } - - getColumnValidations(fileName: string, columnName: string): CSVValidator[] { - const validationSchemas = this.getColumnValidationSchemas(fileName, columnName); - - const rules: CSVValidator[] = []; - - validationSchemas.forEach((validationSchema) => { - const keys = Object.keys(validationSchema); - - if (keys.length !== 1) { - return; - } - - const key = keys[0]; - - const generatorFunction = ValidationRulesRegistry.findMatchingRule(key); - - if (!generatorFunction) { - return; - } - - const rule = generatorFunction({ columnName: columnName, ...validationSchema }); - - rules.push(rule); - }); - - return rules; - } - - getSubmissionValidationSChemas(): object[] { - return JSONPath({ json: this.validationSchema, path: this.getSubmissionValidationsJsonPath() })?.[0] || []; - } - - getFileValidationSchemas(fileName: string): object[] { - let validationSchemas = - JSONPath({ json: this.validationSchema, path: this.getFileValidationsJsonPath(fileName) })?.[0] || []; - - if (!validationSchemas.length) { - validationSchemas = this.getDefaultFileValidationSchemas(); - } - return validationSchemas; - } - - getDefaultFileValidationSchemas(): object[] { - return JSONPath({ json: this.validationSchema, path: this.getDefaultFileValidationsJsonPath() })?.[0] || []; - } - - getColumnValidationSchemas(fileName: string, columnName: string): object[] { - const filevalidationSchemas = - JSONPath({ json: this.validationSchema, path: this.getFileValidationsJsonPath(fileName) })?.[0] || []; - - let columnValidationSchemas; - - if (!filevalidationSchemas.length) { - columnValidationSchemas = this.getDefaultColumnValidationSchemas(columnName); - } else { - columnValidationSchemas = - JSONPath({ json: this.validationSchema, path: this.getColumnValidationsJsonpath(fileName, columnName) })?.[0] || - []; - } - - return columnValidationSchemas; - } - - getDefaultColumnValidationSchemas(columnName: string): object[] { - return ( - JSONPath({ json: this.validationSchema, path: this.getDefaultColumnValidationsJsonpath(columnName) })?.[0] || [] - ); - } - - getColumnNames(fileName: string): string[] { - let columnNames; - - const filevalidationSchemas = - JSONPath({ json: this.validationSchema, path: this.getFileValidationsJsonPath(fileName) })?.[0] || []; - - if (!filevalidationSchemas.length) { - columnNames = JSONPath({ json: this.validationSchema, path: this.getDefaultColumnNamesJsonpath() }); - } else { - columnNames = JSONPath({ json: this.validationSchema, path: this.getColumnNamesJsonpath(fileName) }); - } - - return columnNames; - } - - getSubmissionValidationsJsonPath(): string { - return '$.validations'; - } - - getFileValidationsJsonPath(fileName: string): string { - return `$.files[?(@.name == '${fileName}')].validations`; - } - getDefaultFileValidationsJsonPath(): string { - return `$.defaultFile.validations`; - } - - getColumnNamesJsonpath(fileName: string): string { - return `$.files[?(@.name == '${fileName}')].columns[*].name`; - } - - getDefaultColumnNamesJsonpath(): string { - return `$.defaultFile.columns[*].name`; - } - - getColumnValidationsJsonpath(fileName: string, columnName: string): string { - return `$.files[?(@.name == '${fileName}')].columns[?(@.name == '${columnName}')].validations`; - } - - getDefaultColumnValidationsJsonpath(columnName: string): string { - return `$.defaultFile.columns[?(@.name == '${columnName}')].validations`; - } - - parseJson(json: any): object { - try { - return JSON.parse(json); - } catch { - throw Error('ValidationSchemaParser - provided json was not valid JSON'); - } - } -} diff --git a/api/src/utils/media/xlsx/transformation/transformation-schema-parser.ts b/api/src/utils/media/xlsx/transformation/transformation-schema-parser.ts deleted file mode 100644 index 47bb9f311..000000000 --- a/api/src/utils/media/xlsx/transformation/transformation-schema-parser.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { JSONPath } from 'jsonpath-plus'; - -export type FlattenSchema = { - fileName: string; - uniqueId: string[]; - parent?: { fileName: string; uniqueId: string[] }; -}; - -export type TransformationFieldSchema = { - columns?: string[]; - separator?: string; - value?: any; - unique?: string; - condition?: Condition; -}; - -export type TransformationFieldsSchema = { - [key: string]: TransformationFieldSchema; -}; - -export type Condition = { - if: { - columns: string[]; - not?: boolean; - }; -}; - -export type PostTransformationRelatopnshipSchema = { - condition?: Condition; - relationship: { - spreadColumn: string; - uniqueIdColumn: 'string'; - }; -}; - -export type TransformSchema = { - condition?: Condition; - transformations: { - condition?: Condition; - fields: TransformationFieldsSchema; - }[]; - postTransformations?: PostTransformationRelatopnshipSchema[]; -}; - -export type ParseColumnSchema = { source: { columns?: string[]; value?: any }; target: string }; - -export type ParseSchema = { - fileName: string; - columns: ParseColumnSchema[]; - condition?: Condition; -}; -export class TransformationSchemaParser { - transformationSchema: object; - - constructor(transformationSchema: string | object) { - if (typeof transformationSchema === 'string') { - this.transformationSchema = this.parseJson(transformationSchema); - } else { - this.transformationSchema = transformationSchema; - } - } - - getAllFlattenSchemas(): FlattenSchema[] | [] { - return JSONPath({ json: this.transformationSchema, path: this.getFlattenJsonPath() })?.[0] || []; - } - - getFlattenSchemas(fileName: string): FlattenSchema | null { - return ( - JSONPath({ json: this.transformationSchema, path: this.getFlattenJsonPathByFileName(fileName) })?.[0] || null - ); - } - - getTransformSchemas(): TransformSchema[] { - return JSONPath({ json: this.transformationSchema, path: this.getTransformationJsonPath() })?.[0] || []; - } - - getParseSchemas(): ParseSchema[] { - return JSONPath({ json: this.transformationSchema, path: this.getParseJsonPath() })?.[0] || []; - } - - getFlattenJsonPath(): string { - return `$.flatten`; - } - - getFlattenJsonPathByFileName(fileName: string): string { - return `$.flatten[?(@.fileName == '${fileName}')]`; - } - - getTransformationJsonPath(): string { - return '$.transform'; - } - - getParseJsonPath(): string { - return '$.parse'; - } - - parseJson(json: any): object { - let parsedJson; - - try { - parsedJson = JSON.parse(json); - } catch { - throw Error('TransformationSchemaParser - provided validationSchema was not valid JSON'); - } - - return parsedJson; - } -} diff --git a/api/src/utils/media/xlsx/transformation/xlsx-transformation.ts b/api/src/utils/media/xlsx/transformation/xlsx-transformation.ts deleted file mode 100644 index 71f9c8049..000000000 --- a/api/src/utils/media/xlsx/transformation/xlsx-transformation.ts +++ /dev/null @@ -1,621 +0,0 @@ -import { isEqual, uniqWith } from 'lodash'; -import xlsx from 'xlsx'; -import { CSVWorksheet } from '../../csv/csv-file'; -import { XLSXCSV } from '../xlsx-file'; -import { - Condition, - FlattenSchema, - PostTransformationRelatopnshipSchema, - TransformationFieldSchema, - TransformationSchemaParser, - TransformSchema -} from './transformation-schema-parser'; - -export type FlattenedRowPartsBySourceFile = { - sourceFile: string; - uniqueId: any; - row: object; -}; - -export type RowObject = { [key: string]: any }; - -export type RowsObjectsByFileName = { [key: string]: RowObject[] }; - -export type XLSXWorksheetByFileName = { [key: string]: xlsx.WorkSheet }; - -/** - * Applies transformations to an `XLSXCSV` instance. - * - * @export - * @class XLSXTransformation - */ -export class XLSXTransformation { - transformationSchemaParser: TransformationSchemaParser; - xlsxCsv: XLSXCSV; - - constructor(transformationSchemaParser: TransformationSchemaParser, xlsxCsv: XLSXCSV) { - this.transformationSchemaParser = transformationSchemaParser; - this.xlsxCsv = xlsxCsv; - } - - /** - * Transform the raw XLSX data. - * - * @return {*} {RowsObjectsByFileName} - * @memberof XLSXTransformation - */ - transform(): RowsObjectsByFileName { - const flattenedData = this._flattenData(); - - const mergedFlattenedData = this._mergedFlattenedRows(flattenedData); - - const transformedMergedFlattenedData = this._transformFlattenedData(mergedFlattenedData); - - const parsedTransformedMergedFlattenedData = this._parseTransformedData(transformedMergedFlattenedData); - - return this._mergeParsedData(parsedTransformedMergedFlattenedData); - } - - /** - * Flattens the worksheet data into arrays of objects. - * - * @return {*} {FlattenedRowPartsBySourceFile[][]} - * @memberof XLSXTransformation - */ - _flattenData(): FlattenedRowPartsBySourceFile[][] { - let rowsBySourceFileArray: FlattenedRowPartsBySourceFile[][] = []; - - // Get all flatten schemas - const flattenSchemas = this.transformationSchemaParser.getAllFlattenSchemas(); - - // Build an array of [worksheetName, worksheet] based on the order of the flatten schemas. This is necessary - // because the flattening process requires parsing the worksheets in a specific order, as specified by the flatten - // section of the transformation schema. - const orderedWorksheetsByFlattenSchema: [string, CSVWorksheet][] = []; - flattenSchemas.forEach((flattenSchema) => { - const worksheet = this.xlsxCsv.workbook.worksheets[flattenSchema.fileName]; - - if (worksheet) { - orderedWorksheetsByFlattenSchema.push([flattenSchema.fileName, worksheet]); - } - }); - - // Iterate over each worksheet in the ordered array of worksheets - orderedWorksheetsByFlattenSchema.forEach(([worksheetName, worksheet]) => { - // Get the flatten file structure schema for the worksheet, based on the worksheet name - const flattenSchema = this.transformationSchemaParser.getFlattenSchemas(worksheetName); - - if (!flattenSchema) { - // No schema for this worksheet, skip it - return; - } - - // Get all worksheet rows as an array of objects - const rowObjects = worksheet.getRowObjects(); - - if (!flattenSchema.parent) { - // Handle root records, that have no parent record - const flattenedRootRecords = this._flattenRootRecords(flattenSchema, worksheet, rowObjects); - rowsBySourceFileArray = rowsBySourceFileArray.concat(flattenedRootRecords); - } else { - // Handle child records, that have a parent record - const parentFileName = flattenSchema.parent.fileName.toLowerCase(); - const parentUniqueIdColumns = flattenSchema.parent.uniqueId; - - const childFileName = flattenSchema.fileName; - const childUniqueIdColumns = flattenSchema.uniqueId; - - rowObjects.forEach((rowObject, rowIndex) => { - const parentUniqueId = this._buildMultiColumnID(worksheet, rowIndex, parentUniqueIdColumns).toLowerCase(); - - const uniqueId = this._buildMultiColumnID(worksheet, rowIndex, childUniqueIdColumns); - - const newRecord = { - sourceFile: childFileName, - uniqueId: uniqueId, - row: rowObject - }; - - // An array of indexes that tracks which records to add `newRecord` to, and which records should be duplicated - // before adding `newRecord` to them. - const recordsToModify: { matchingParentIndex: number; matchingChildIndex: number }[] = []; - let recordsToModifyIndex = 0; - - let foundRowToModify = false; - - // For each parent array of child arrays of objects - rowsBySourceFileArray.forEach((rowsBySourceFile, rowsBySourceFileIndex) => { - if (foundRowToModify) { - return; - } - - let foundRecordToModify = false; - - /* - * Compare the `newRecord` to each object in the child array - * If a matching parent record is found - * - mark this parent array index - * If a matching child record from the same sourceFile is found - * - mark this child array index - */ - rowsBySourceFile.forEach((rowBySourceFile, rowBySourceFileIndex) => { - const existingRowFileName = rowBySourceFile.sourceFile.toLowerCase(); - const existingRowUniqueId = rowBySourceFile.uniqueId.toLowerCase(); - - if (existingRowFileName === parentFileName && existingRowUniqueId === parentUniqueId) { - // This array may need a copy of `newRecord` - recordsToModify[recordsToModifyIndex] = { - ...recordsToModify[recordsToModifyIndex], - matchingParentIndex: rowsBySourceFileIndex - }; - - foundRecordToModify = true; - } else if (existingRowFileName === childFileName.toLowerCase()) { - // This array already contains a record from the same file as `newRecord` and will need to be duplicated - recordsToModify[recordsToModifyIndex] = { - ...recordsToModify[recordsToModifyIndex], - matchingChildIndex: rowBySourceFileIndex - }; - - foundRecordToModify = true; - } - }); - - if (foundRecordToModify) { - if ( - recordsToModify[recordsToModifyIndex].matchingParentIndex >= 0 && - recordsToModify[recordsToModifyIndex].matchingChildIndex >= 0 - ) { - // A matching parent row with matching child was found, don't continue checking other rows - foundRowToModify = true; - } - // A record was found after iterating over the previous array, increase the index before we loop over - // the next array. - recordsToModifyIndex++; - } - }); - - // For each `recordsToModify` - // Apply updates to the existing records based on the `recordsToModify` array. - recordsToModify.forEach((recordToModify) => { - if (recordToModify.matchingParentIndex >= 0 && recordToModify.matchingChildIndex >= 0) { - /* - * `recordToModify` indicates that a matching parent was found AND a matching child from the same - * sourceFile was found. Duplicate the array, and in the duplicated array, overwrite the existing - * matching child with the `newRecord`. - * - * Example: - * - * Initial state: - * - * newRecord = {sourceFile: 'file2', uniqueId: 3, row: {...}}; - * - * rowsBySourceFileArray = [ - * [ - * {sourceFile: 'file1', uniqueId: 1, row: {...}}, // matching parent of `newRecord` - * {sourceFile: 'file2', uniqueId: 2, row: {...}} // matching child from same sourceFile as `newRecord` - * ] - * ] - * - * Final state: - * - * rowsBySourceFileArray = [ - * [ - * {sourceFile: 'file1', uniqueId: 1, row: {...}}, - * {sourceFile: 'file2', uniqueId: 2, row: {...}} - * ], - * [ - * {sourceFile: 'file1', uniqueId: 1, row: {...}}, - * {sourceFile: 'file2', uniqueId: 3, row: {...}} - * ] - * ] - */ - - // Copy the existing items into a new array - const newRowRecord = [...rowsBySourceFileArray[recordToModify.matchingParentIndex]]; - - // Overwrite the matching item at index `matchingChildIndex` with our new record - newRowRecord[recordToModify.matchingChildIndex] = newRecord; - - // Append this new duplicated record to the parent array - rowsBySourceFileArray.push(newRowRecord); - } else if (recordToModify.matchingParentIndex >= 0) { - /* - * `recordToModify` indicates that a matching parent was found. Add the `newRecord` to this existing - * array. - * - * Example: - * - * Initial state: - * - * newRecord = {sourceFile: 'file2', uniqueId: 3, row: {...}}; - * - * rowsBySourceFileArray = [ - * [ - * {sourceFile: 'file1', uniqueId: 1, row: {...}} // matching parent of `newRecord` - * ] - * ] - * - * Final state: - * - * rowsBySourceFileArray = [ - * [ - * {sourceFile: 'file1', uniqueId: 1, row: {...}}, - * {sourceFile: 'file2', uniqueId: 3, row: {...}} - * ] - * ] - */ - rowsBySourceFileArray[recordToModify.matchingParentIndex].push(newRecord); - } - }); - }); - } - }); - - return rowsBySourceFileArray; - } - - _flattenRootRecords( - flattenSchema: FlattenSchema, - worksheet: CSVWorksheet, - rowObjects: object[] - ): FlattenedRowPartsBySourceFile[][] { - const newRecords: FlattenedRowPartsBySourceFile[][] = []; - - rowObjects.forEach((rowObject, rowIndex) => { - const uniqueId = this._buildMultiColumnID(worksheet, rowIndex, flattenSchema.uniqueId); - - const newRecord = { - sourceFile: flattenSchema.fileName, - uniqueId: uniqueId, - row: rowObject - }; - - newRecords.push([newRecord]); - }); - - return newRecords; - } - - _buildMultiColumnID(worksheet: CSVWorksheet, rowIndex: number, columnNames: string[]) { - return this._buildMultiColumnValue(worksheet, rowIndex, columnNames, ':'); - } - - _buildMultiColumnValue(worksheet: CSVWorksheet, rowIndex: number, columnNames: string[], separator?: string) { - return columnNames.map((columnName) => worksheet.getCell(columnName, rowIndex)).join(separator || ' '); - } - - /** - * Merges the arrays of objects into an array of objects. - * - * @param {FlattenedRowPartsBySourceFile[][]} flattenedData - * @return {*} {object[][]} - * @memberof XLSXTransformation - */ - _mergedFlattenedRows(flattenedData: FlattenedRowPartsBySourceFile[][]): object[] { - const mergedAndFlattenedRows: object[] = []; - - flattenedData.forEach((rowsBySourceFile, index) => { - rowsBySourceFile.forEach((rowPart) => { - mergedAndFlattenedRows[index] = { ...mergedAndFlattenedRows[index], ...rowPart.row }; - }); - }); - - return mergedAndFlattenedRows; - } - - /** - * Applies transformation logic to the flattened array of objects, creating a new array of objects (which may contain - * duplicate items) - * - * @param {object[]} mergedFlattenedData - * @return {*} {object[]} - * @memberof XLSXTransformation - */ - _transformFlattenedData(mergedFlattenedData: object[]): object[] { - const transformSchemasArray = this.transformationSchemaParser.getTransformSchemas(); - - let transformedDWCData: object[] = []; - - mergedFlattenedData.forEach((rowObject, rowObjectIndex) => { - transformSchemasArray.forEach((transformationSchema, transformationSchemaIndex) => { - const newDWCRowObjects = this._applyFileTransformations( - rowObject, - transformationSchema, - rowObjectIndex, - transformationSchemaIndex - ); - - transformedDWCData = transformedDWCData.concat(newDWCRowObjects); - }); - }); - - return transformedDWCData; - } - - _applyFileTransformations( - rowObject: object, - transformationSchema: TransformSchema, - rowObjectIndex: number, - transformationSchemaIndex: number - ): object[] { - if (!this._isConditionMet(rowObject, transformationSchema?.condition)) { - // condition not met, return an empty array (contains no new records) - return []; - } - - let newDWCRowObjects: object[] = []; - - transformationSchema.transformations.forEach((transformation) => { - const newDWCRowObject = {}; - - if (!this._isConditionMet(rowObject, transformation?.condition)) { - return; - } - - Object.entries(transformation.fields).forEach(([fieldName, config]) => { - if (!this._isConditionMet(rowObject, config?.condition)) { - return; - } - - let columnValue = this._getColumnValue(rowObject, config); - - if (config.unique) { - // Append `config.unique` + indexes to ensure this column value is unique - columnValue = `${columnValue}:${config.unique}-${rowObjectIndex}-${transformationSchemaIndex}`; - } - - newDWCRowObject[fieldName] = columnValue; - }); - - newDWCRowObjects.push(newDWCRowObject); - }); - - transformationSchema?.postTransformations?.forEach((postTransformation) => { - if (!this._isConditionMet(rowObject, postTransformation.condition)) { - return; - } - - if (postTransformation.relationship) { - newDWCRowObjects = this._postTransformRelationships( - postTransformation as PostTransformationRelatopnshipSchema, - newDWCRowObjects - ); - } - }); - - return newDWCRowObjects; - } - - _postTransformRelationships = ( - postTransformRelationshipSchema: PostTransformationRelatopnshipSchema, - originalDWCRowObjects: object[] - ) => { - // Spread the parent/child row objects and update relationship fields - - const spreadColumn = postTransformRelationshipSchema.relationship.spreadColumn; - const uniqueIdColumn = postTransformRelationshipSchema.relationship.uniqueIdColumn; - - let spreadDWCRowObjects: object[] = []; - - if (spreadColumn) { - const originalParentRecord = originalDWCRowObjects[0]; - const originalChildRecord = originalDWCRowObjects[1]; - - const spreadColumnValue = Number(originalParentRecord[spreadColumn]); - - if (spreadColumnValue) { - for (let i = 0; i < spreadColumnValue; i++) { - const newParentRecord = { - ...originalParentRecord, - [spreadColumn]: 1, - [uniqueIdColumn]: `${originalParentRecord[uniqueIdColumn]}-${i}-0` - }; - const newChildRecord = { - ...originalChildRecord, - [uniqueIdColumn]: `${originalChildRecord[uniqueIdColumn]}-${i}-1` - }; - - newParentRecord['resourceID'] = newParentRecord[uniqueIdColumn]; - newParentRecord['relatedResourceID'] = newChildRecord[uniqueIdColumn]; - - newChildRecord['resourceID'] = newChildRecord[uniqueIdColumn]; - newChildRecord['relatedResourceID'] = newParentRecord[uniqueIdColumn]; - - spreadDWCRowObjects = spreadDWCRowObjects.concat([newParentRecord, newChildRecord]); - } - } - } - - return spreadDWCRowObjects; - }; - - /** - * Builds a new value from the `rowObject`, based on the config. - * - * This may involve returning a single `rowObject` value, concatenating multiple `rowObject` values together, or - * returning a static value. - * - * @param {object} rowObject - * @param {(TransformationFieldSchema | undefined)} config - * @return {*} {*} - * @memberof XLSXTransformation - */ - _getColumnValue(rowObject: object, config: TransformationFieldSchema | undefined): any { - if (!config) { - return; - } - - let columnValue = undefined; - - if (config.columns) { - const columnsValues = this._getColumnValueParts(rowObject, config.columns); - - if (columnsValues && columnsValues.length) { - columnValue = columnsValues.join(config?.separator || ' '); - } - } - - if (config.value) { - columnValue = config.value; - } - - return columnValue; - } - - /** - * Given an array of column names, return an array of matching column values. - * - * @param {object} rowObject - * @param {string[]} columnNames - * @return {*} {((string | number)[] | undefined)} - * @memberof XLSXTransformation - */ - _getColumnValueParts(rowObject: object, columnNames: string[]): (string | number)[] | undefined { - if (!rowObject || !columnNames || !columnNames.length) { - return undefined; - } - - const columnValueParts: any[] = []; - - columnNames.forEach((columnName) => { - const columnValue = rowObject[columnName]; - - if (columnValue !== undefined && columnValue !== null && columnValue !== '') { - columnValueParts.push(columnValue); - } - }); - - return columnValueParts; - } - - /** - * Returns true if the `condition` is met. - * - * @param {object} rowObject - * @param {(Condition | undefined)} condition - * @return {*} {boolean} - * @memberof XLSXTransformation - */ - _isConditionMet(rowObject: object, condition?: Condition): boolean { - if (!condition) { - // no conditions specified - return true; - } - - const columnValueParts = this._getColumnValueParts(rowObject, condition.if.columns); - - if (!columnValueParts || !columnValueParts.length) { - if (condition.if.not) { - // return true if no condition column values are defined, when condition is inverted - return true; - } - - // return false if no condition column values are defined - return false; - } - - if (condition.if.not) { - // return false if any condition column values are defined, when condition is inverted - return false; - } - - // return true if all condition columns are defined - return !columnValueParts.every( - (columnValuePart) => columnValuePart === undefined || columnValuePart === null || columnValuePart === '' - ); - } - - /** - * Parses the array of objects into separate arrays of objects (which may contain duplicate items), based on fileName. - * - * @param {object[]} transformedMergedFlattenedData - * @return {*} {RowsObjectsByFileName} - * @memberof XLSXTransformation - */ - _parseTransformedData(transformedMergedFlattenedData: object[]): RowsObjectsByFileName { - const parseSchemas = this.transformationSchemaParser.getParseSchemas(); - - const parsedDWCData: RowsObjectsByFileName = {}; - - parseSchemas.forEach((parseSchema) => { - const fileName = parseSchema.fileName; - const columns = parseSchema.columns; - - if (!parsedDWCData[fileName]) { - // initialize an empty array for the current fileName if one does not yet exist - parsedDWCData[fileName] = []; - } - - transformedMergedFlattenedData.forEach((rowObject) => { - if (!this._isConditionMet(rowObject, parseSchema?.condition)) { - // A conditional field was undefined or null, skip this record - return; - } - - const newRowObject = {}; - - for (const column of columns) { - if (column.source.columns && column.source.columns?.length) { - // iterate over source columns - for (const sourceColumn of column.source.columns) { - const sourceValue = rowObject[sourceColumn]; - - if (sourceValue) { - // use the first source column that has a defined value - newRowObject[column.target] = sourceValue; - break; - } - } - } else if (column.source.value) { - newRowObject[column.target] = column.source.value; - } - } - - if (!Object.keys(newRowObject).length) { - // row object is empty, skip - return; - } - - parsedDWCData[fileName].push(newRowObject); - }); - }); - - return parsedDWCData; - } - - /** - * Merges the array of objects for each fileName, removing duplicate items. - * - * @param {RowsObjectsByFileName} parsedDWCData - * @return {*} {RowsObjectsByFileName} - * @memberof XLSXTransformation - */ - _mergeParsedData(parsedTransformedMergedFlattenedData: RowsObjectsByFileName): RowsObjectsByFileName { - // For each entry (based on fileName), do a deep equality check on each of its row objects, removing any duplicates. - Object.entries(parsedTransformedMergedFlattenedData).forEach(([fileName, rowObjects]) => { - parsedTransformedMergedFlattenedData[fileName] = uniqWith(rowObjects, isEqual); - }); - - return parsedTransformedMergedFlattenedData; - } - - /** - * Converts an object (whose keys are file names, and whose value is an array of objects) into a new object (whose - * keys are file names, and whose value is an `xlsx.Worksheet`). - * - * @param {RowsObjectsByFileName} mergedParsedData - * @return {*} {XLSXWorksheetByFileName} - * @memberof XLSXTransformation - */ - dataToSheet(mergedParsedTransformedData: RowsObjectsByFileName): XLSXWorksheetByFileName { - const sheets: XLSXWorksheetByFileName = {}; - - Object.entries(mergedParsedTransformedData).forEach(([fileName, rowObjects]) => { - const worksheet = xlsx.utils.json_to_sheet(rowObjects); - sheets[fileName] = worksheet; - }); - - return sheets; - } -} diff --git a/api/src/utils/media/xlsx/xlsx-file.test.ts b/api/src/utils/media/xlsx/xlsx-file.test.ts deleted file mode 100644 index 3d0819d7d..000000000 --- a/api/src/utils/media/xlsx/xlsx-file.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { expect } from 'chai'; -import { describe } from 'mocha'; -import { MediaFile } from '../media-file'; -import { XLSXCSV } from './xlsx-file'; - -describe('XLSXCSV', () => { - it('constructs', () => { - const mediaFile: MediaFile = new MediaFile('fileName', 'mimetype', Buffer.from('')); - - const xlsxCSV = new XLSXCSV(mediaFile); - - expect(xlsxCSV).not.to.be.null; - }); -}); diff --git a/api/src/utils/media/xlsx/xlsx-file.ts b/api/src/utils/media/xlsx/xlsx-file.ts deleted file mode 100644 index 2b867d87b..000000000 --- a/api/src/utils/media/xlsx/xlsx-file.ts +++ /dev/null @@ -1,88 +0,0 @@ -import xlsx from 'xlsx'; -import { CSVWorkBook, CSVWorksheet, ICsvState } from '../csv/csv-file'; -import { DEFAULT_XLSX_SHEET } from '../dwc/dwc-archive-file'; -import { IMediaState, MediaFile, MediaValidation } from '../media-file'; -import { ValidationSchemaParser } from '../validation/validation-schema-parser'; - -/** - * Supports XLSX CSV files. - * - * @export - * @class XLSXCSV - */ -export class XLSXCSV { - rawFile: MediaFile; - - mediaValidation: MediaValidation; - - workbook: CSVWorkBook; - - constructor(file: MediaFile, options?: xlsx.ParsingOptions) { - this.rawFile = file; - - this.mediaValidation = new MediaValidation(this.rawFile.fileName); - - // See https://www.npmjs.com/package/xlsx#parsing-options for details on parsing options - const parsingOptions: xlsx.ParsingOptions = { cellDates: true, cellNF: true, cellHTML: false, ...options }; - - this.workbook = new CSVWorkBook(xlsx.read(this.rawFile.buffer, parsingOptions)); - } - - isMediaValid(validationSchemaParser: ValidationSchemaParser): IMediaState { - const validators = validationSchemaParser.getSubmissionValidations(); - - const mediaValidation = this.validate(validators as XLSXCSVValidator[]); - - return mediaValidation.getState(); - } - - isContentValid(validationSchemaParser: ValidationSchemaParser): ICsvState[] { - const csvStates: ICsvState[] = []; - - Object.keys(this.workbook.worksheets).forEach((fileName) => { - const fileValidators = validationSchemaParser.getFileValidations(fileName); - - const columnValidators = validationSchemaParser.getAllColumnValidations(fileName); - - const validators = [...fileValidators, ...columnValidators]; - - const worksheet: CSVWorksheet = this.workbook.worksheets[fileName]; - - if (!worksheet) { - return; - } - - const csvValidation = worksheet.validate(validators); - - csvStates.push(csvValidation.getState()); - }); - - return csvStates; - } - - worksheetToBuffer(worksheet: xlsx.WorkSheet): Buffer { - const newWorkbook = xlsx.utils.book_new(); - - xlsx.utils.book_append_sheet(newWorkbook, worksheet, DEFAULT_XLSX_SHEET); - - return xlsx.write(newWorkbook, { type: 'buffer', bookType: 'csv' }); - } - - /** - * Executes each validator function in the provided `validators` against this instance, returning - * `this.mediaValidation` - * - * @param {XLSXCSVValidator[]} validators - * @return {*} {MediaValidation} - * @memberof XLSXCSV - */ - validate(validators: XLSXCSVValidator[]): MediaValidation { - validators.forEach((validator) => validator(this)); - - return this.mediaValidation; - } -} - -export type XLSXCSVValidator = (xlsxCsv: XLSXCSV) => XLSXCSV; - -export type XLSXCSVTransformer = { pivot: string; transform: (xlsxCsv: XLSXCSV, modifiers?: object) => XLSXCSV }; diff --git a/api/src/utils/media/xlsx/xlsx-utils.ts b/api/src/utils/media/xlsx/xlsx-utils.ts deleted file mode 100644 index 8def99bf7..000000000 --- a/api/src/utils/media/xlsx/xlsx-utils.ts +++ /dev/null @@ -1,89 +0,0 @@ -import xlsx, { CellObject } from 'xlsx'; -import { safeTrim } from '../../string-utils'; - -/** - * Get a worksheet by name. - * - * @export - * @param {xlsx.WorkBook} workbook - * @param {string} sheetName - * @return {*} {xlsx.WorkSheet} - */ -export function getWorksheetByName(workbook: xlsx.WorkBook, sheetName: string): xlsx.WorkSheet { - return workbook.Sheets[sheetName]; -} - -/** - * Get a worksheets decoded range object, or return undefined if the worksheet is missing range information. - * - * @export - * @param {xlsx.WorkSheet} worksheet - * @return {*} {(xlsx.Range | undefined)} - */ -export function getWorksheetRange(worksheet: xlsx.WorkSheet): xlsx.Range | undefined { - const ref = worksheet['!ref']; - - if (!ref) { - return undefined; - } - - return xlsx.utils.decode_range(ref); -} - -/** - * Iterates over the cells in the worksheet and: - * - Trims whitespace from cell values. - * - Converts `Date` objects to ISO strings. - * - * https://stackoverflow.com/questions/61789174/how-can-i-remove-all-the-spaces-in-the-cells-of-excelsheet-using-nodejs-code - * @param worksheet - */ -export function prepareWorksheetCells(worksheet: xlsx.WorkSheet) { - const range = getWorksheetRange(worksheet); - - if (!range) { - return undefined; - } - - for (let r = range.s.r; r < range.e.r; r++) { - for (let c = range.s.c; c < range.e.c; c++) { - const coord = xlsx.utils.encode_cell({ r, c }); - let cell: CellObject = worksheet[coord]; - - if (!cell || !cell.v) { - continue; - } - - cell = replaceCellDates(cell); - - cell = trimCellWhitespace(cell); - } - } -} - -export function trimCellWhitespace(cell: CellObject) { - // check and clean raw strings - if (cell.t === 's') { - cell.v = safeTrim(cell.v); - } - - // check and clean formatted strings - if (cell.w) { - cell.w = safeTrim(cell.w); - } - - return cell; -} - -export function replaceCellDates(cell: CellObject) { - if (cell.t === 'd' && cell.v instanceof Date) { - cell.v = (cell.v as Date).toISOString(); - } - - return cell; -} - -export function getCellValue(cell: CellObject) { - // See https://www.npmjs.com/package/xlsx#cell-object for details on cell fields - return cell.v; -} diff --git a/app/package-lock.json b/app/package-lock.json index 928206e65..ca5135e93 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@adobe/css-tools": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", - "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", "dev": true }, "@alloc/quick-lru": { @@ -17,12 +17,12 @@ "dev": true }, "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "@apideck/better-ajv-errors": { @@ -37,49 +37,50 @@ } }, "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, "@babel/compat-data": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", - "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==" + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==" }, "@babel/core": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", - "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.22.0", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-module-transforms": "^7.22.1", - "@babel/helpers": "^7.22.0", - "@babel/parser": "^7.22.0", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" } }, "@babel/eslint-parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.8.tgz", - "integrity": "sha512-HLhI+2q+BP3sf78mFUZNCGc10KEmoUqtUT1OCdMZsN+qr4qFeLUod62/zAnF3jNQstwyasDkZnVXwfK2Bml7MQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.5.tgz", + "integrity": "sha512-gsUcqS/fPlgAw1kOtpss7uhY6E9SFFANQ6EFX5GTvzUwaV0+sGaZWk6xq22MOdeT9wfxyokW3ceCUvOiRtZciQ==", "dev": true, "requires": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "dependencies": { "eslint-visitor-keys": { @@ -91,278 +92,290 @@ } }, "@babel/generator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", - "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "requires": { - "@babel/types": "^7.22.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" } }, "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz", - "integrity": "sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", "dev": true, "requires": { - "@babel/types": "^7.22.3" + "@babel/types": "^7.22.15" } }, "@babel/helper-compilation-targets": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", - "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "requires": { - "@babel/compat-data": "^7.22.0", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz", - "integrity": "sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz", + "integrity": "sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.22.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.22.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.24.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "semver": "^6.3.1" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz", - "integrity": "sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "semver": "^6.3.1" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz", - "integrity": "sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" + "resolve": "^1.14.2" } }, "@babel/helper-environment-visitor": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", - "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz", - "integrity": "sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz", + "integrity": "sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==", "dev": true, "requires": { - "@babel/types": "^7.22.3" + "@babel/types": "^7.24.5" } }, "@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "requires": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.24.0" } }, "@babel/helper-module-transforms": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", - "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", "requires": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" } }, "@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" } }, "@babel/helper-replace-supers": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz", - "integrity": "sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-member-expression-to-functions": "^7.22.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5" } }, "@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", "requires": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.24.5" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "requires": { - "@babel/types": "^7.20.0" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.24.5" } }, "@babel/helper-string-parser": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", - "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==" + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==" }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==" }, "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==" + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" }, "@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.5.tgz", + "integrity": "sha512-/xxzuNvgRl4/HLNKvnFwdhdgN3cpLxgLROeLDl83Yx0AJ1SGvq1ak0OszTOjDfiB8Vx03eJbeDWh9r+jCCWttw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/helper-function-name": "^7.23.0", + "@babel/template": "^7.24.0", + "@babel/types": "^7.24.5" } }, "@babel/helpers": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", - "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", "requires": { - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.3" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/parser": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", - "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==" + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==" + }, + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz", + "integrity": "sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.5" + } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz", - "integrity": "sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-transform-optional-chaining": "^7.22.3" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.24.1" + } + }, + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-proposal-class-properties": { @@ -376,16 +389,14 @@ } }, "@babel/plugin-proposal-decorators": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.3.tgz", - "integrity": "sha512-XjTKH3sHr6pPqG+hR1NCdVupwiosfdKM2oSMyKQVQ5Bym9l/p7BuLAqT5U32zZzRCfPq/TPRPzMiiTE9bOXU4w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.1.tgz", + "integrity": "sha512-zPEvzFijn+hRvJuX2Vu3KbEBN39LN3f7tW3MQO2LsIs57B26KU+kUc82BdAktS1VCM6libzh45eKGI65lg0cpA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-replace-supers": "^7.22.1", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.22.3" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-decorators": "^7.24.1" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { @@ -441,16 +452,6 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -488,12 +489,12 @@ } }, "@babel/plugin-syntax-decorators": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.3.tgz", - "integrity": "sha512-R16Zuge73+8/nLcDjkIpyhi5wIbN7i7fiuLJR8yQX7vPAa/ltUKtd3iLbb4AgP5nrLi91HnNUNosELIGUGH1bg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz", + "integrity": "sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-dynamic-import": { @@ -515,30 +516,30 @@ } }, "@babel/plugin-syntax-flow": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", - "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", + "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-import-attributes": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz", - "integrity": "sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-import-meta": { @@ -560,12 +561,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -641,12 +642,12 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-syntax-unicode-sets-regex": { @@ -660,595 +661,596 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz", - "integrity": "sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", + "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", "dev": true, "requires": { - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", + "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-module-imports": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz", + "integrity": "sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.5" } }, "@babel/plugin-transform-class-properties": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz", - "integrity": "sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-class-static-block": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz", - "integrity": "sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz", + "integrity": "sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-split-export-declaration": "^7.24.5", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" } }, "@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz", + "integrity": "sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.5" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-dynamic-import": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz", - "integrity": "sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz", - "integrity": "sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz", + "integrity": "sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-flow": "^7.24.1" } }, "@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" } }, "@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", "dev": true, "requires": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-json-strings": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz", - "integrity": "sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz", - "integrity": "sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-simple-access": "^7.22.5" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz", - "integrity": "sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz", - "integrity": "sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-transform-new-target": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz", - "integrity": "sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz", - "integrity": "sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-transform-numeric-separator": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz", - "integrity": "sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz", - "integrity": "sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.5.tgz", + "integrity": "sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA==", "dev": true, "requires": { - "@babel/compat-data": "^7.22.3", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.3" + "@babel/plugin-transform-parameters": "^7.24.5" } }, "@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" } }, "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz", - "integrity": "sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-transform-optional-chaining": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz", - "integrity": "sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz", + "integrity": "sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-transform-parameters": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz", - "integrity": "sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz", + "integrity": "sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.5" } }, "@babel/plugin-transform-private-methods": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz", - "integrity": "sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz", - "integrity": "sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.5.tgz", + "integrity": "sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.5", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.3.tgz", - "integrity": "sha512-b5J6muxQYp4H7loAQv/c7GO5cPuRA6H5hx4gO+/Hn+Cu9MRQU0PNiUoWq1L//8sq6kFSNxGXFb2XTaUfa9y+Pg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz", + "integrity": "sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", + "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz", - "integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.22.3" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", "dev": true, "requires": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/plugin-transform-react-jsx": "^7.22.5" } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", + "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.24.0", + "regenerator-transform": "^0.15.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-runtime": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.4.tgz", - "integrity": "sha512-Urkiz1m4zqiRo17klj+l3nXgiRTFQng91Bc1eiLF7BMQu1e7wE5Gcq9xSv062IF068NHjcutSbIMev60gXxAvA==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", + "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "babel-plugin-polyfill-corejs2": "^0.4.3", - "babel-plugin-polyfill-corejs3": "^0.8.1", - "babel-plugin-polyfill-regenerator": "^0.5.0", - "semver": "^6.3.0" + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-plugin-utils": "^7.24.0", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz", + "integrity": "sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.5" } }, "@babel/plugin-transform-typescript": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.3.tgz", - "integrity": "sha512-pyjnCIniO5PNaEuGxT28h0HbMru3qCVrMqVgVOz/krComdIrY9W6FCLBq9NWHY8HDGaUlan+UhmZElDENIfCcw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.5.tgz", + "integrity": "sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-typescript": "^7.21.4" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.5", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/plugin-syntax-typescript": "^7.24.1" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz", - "integrity": "sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz", - "integrity": "sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" } }, "@babel/preset-env": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.4.tgz", - "integrity": "sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.3", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.3", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.5.tgz", + "integrity": "sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.24.4", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.5", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-import-attributes": "^7.22.3", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -1260,88 +1262,93 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.3", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-class-properties": "^7.22.3", - "@babel/plugin-transform-class-static-block": "^7.22.3", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-dynamic-import": "^7.22.1", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-export-namespace-from": "^7.22.3", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-json-strings": "^7.22.3", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.3", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.3", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.3", - "@babel/plugin-transform-new-target": "^7.22.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", - "@babel/plugin-transform-numeric-separator": "^7.22.3", - "@babel/plugin-transform-object-rest-spread": "^7.22.3", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-optional-catch-binding": "^7.22.3", - "@babel/plugin-transform-optional-chaining": "^7.22.3", - "@babel/plugin-transform-parameters": "^7.22.3", - "@babel/plugin-transform-private-methods": "^7.22.3", - "@babel/plugin-transform-private-property-in-object": "^7.22.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.3", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.3", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.4", - "babel-plugin-polyfill-corejs2": "^0.4.3", - "babel-plugin-polyfill-corejs3": "^0.8.1", - "babel-plugin-polyfill-regenerator": "^0.5.0", - "core-js-compat": "^3.30.2", - "semver": "^6.3.0" + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.5", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.5", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.5", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.5", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.5", + "@babel/plugin-transform-parameters": "^7.24.5", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.5", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.5", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "dependencies": { + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true + } } }, "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" } }, "@babel/preset-react": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.3.tgz", - "integrity": "sha512-lxDz1mnZ9polqClBCVBjIVUypoB4qV3/tZUDb/IlYbW1kiiLaXaX+bInbRjl+lNQ/iUZraQ3+S8daEmoELMWug==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.22.3", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" } }, "@babel/preset-typescript": { @@ -1355,248 +1362,6 @@ "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@babel/plugin-transform-typescript": "^7.23.3" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", - "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", - "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", - "dev": true - }, - "@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.4.tgz", - "integrity": "sha512-39hCCOl+YUAyMOu6B9SmUTiHUU0t/CxJNUmY3qRdJujbqi+lrQcL11ysYUsAvFWPBdhihrv1z0oRG84Yr3dODQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/types": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", - "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } } }, "@babel/regjsgen": { @@ -1606,57 +1371,57 @@ "dev": true }, "@babel/runtime": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz", - "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", "requires": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/runtime-corejs3": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.22.3.tgz", - "integrity": "sha512-6bdmknScYKmt8I9VjsJuKKGr+TwUb555FTf6tT1P/ANlCjTHCiYLhiQ4X/O7J731w5NOqu8c1aYHEVuOwPz7jA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.5.tgz", + "integrity": "sha512-GWO0mgzNMLWaSYM4z4NVIuY0Cd1fl8cPnuetuddu5w/qGuvt5Y7oUi/kvvQGK9xgOkFJDQX2heIvTRn/OQ1XTg==", "dev": true, "requires": { "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" } }, "@babel/template": { - "version": "7.21.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", - "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/parser": "^7.21.9", - "@babel/types": "^7.21.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/traverse": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", - "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", - "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.22.3", - "@babel/helper-environment-visitor": "^7.22.1", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.22.4", - "@babel/types": "^7.22.4", - "debug": "^4.1.0", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "requires": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "requires": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" } }, @@ -1672,9 +1437,9 @@ "dev": true }, "@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", + "integrity": "sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==", "dev": true }, "@csstools/postcss-cascade-layers": { @@ -1829,6 +1594,11 @@ "stylis": "4.2.0" }, "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1854,9 +1624,9 @@ "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" }, "@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", "requires": { "@emotion/memoize": "^0.8.1" } @@ -1867,14 +1637,14 @@ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "@emotion/react": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.0.tgz", - "integrity": "sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==", + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", "requires": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", + "@emotion/serialize": "^1.1.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1", "@emotion/weak-memoize": "^0.3.1", @@ -1882,9 +1652,9 @@ } }, "@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", "requires": { "@emotion/hash": "^0.9.1", "@emotion/memoize": "^0.8.1", @@ -1899,14 +1669,14 @@ "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" }, "@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", "requires": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", "@emotion/utils": "^1.2.1" } @@ -1941,20 +1711,20 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true }, "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1964,9 +1734,9 @@ }, "dependencies": { "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -1990,19 +1760,49 @@ } }, "@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true }, + "@floating-ui/core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz", + "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==", + "requires": { + "@floating-ui/utils": "^0.2.0" + } + }, + "@floating-ui/dom": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.4.tgz", + "integrity": "sha512-0G8R+zOvQsAG1pg2Q99P21jiqxqGBW1iRe/iXHsBRBxnpXKFI8QwbB4x5KmYLggNO5m34IQgOIu9SCRfR/WWiQ==", + "requires": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "@floating-ui/react-dom": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz", + "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==", + "requires": { + "@floating-ui/dom": "^1.0.0" + } + }, + "@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" } }, @@ -2013,11 +1813,61 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2105,22 +1955,25 @@ "dev": true }, "@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "dependencies": { "@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==" + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } }, "ansi-styles": { "version": "4.3.0", @@ -2248,9 +2101,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2430,12 +2283,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true } } }, @@ -2465,9 +2312,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2561,9 +2408,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2707,9 +2554,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -2935,9 +2782,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -3082,11 +2929,11 @@ } }, "@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "requires": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" } }, "@jest/source-map": { @@ -3109,12 +2956,12 @@ } }, "@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "requires": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } @@ -3171,9 +3018,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -3329,9 +3176,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -3371,6 +3218,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3421,11 +3274,11 @@ } }, "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "requires": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -3434,9 +3287,12 @@ }, "dependencies": { "@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==" + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } }, "ansi-styles": { "version": "4.3.0", @@ -3484,33 +3340,33 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" }, "@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "@jridgewell/sourcemap-codec": { @@ -3519,25 +3375,18 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - } + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, "@mdi/js": { @@ -3551,136 +3400,124 @@ "integrity": "sha512-OUH9RhfDJPhybQL3owwrSDIXz2yVKXg5lYeOZjyRCiT9wqywNK0FeYyDByOwNIZnnIQoQYmuSrMv+pOX0Uqkmw==" }, "@mui/base": { - "version": "5.0.0-beta.4", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.4.tgz", - "integrity": "sha512-ejhtqYJpjDgHGEljjMBQWZ22yEK0OzIXNa7toJmmXsP4TT3W7xVy8bTJ0TniPDf+JNjrsgfgiFTDGdlEhV1E+g==", - "requires": { - "@babel/runtime": "^7.21.0", - "@emotion/is-prop-valid": "^1.2.1", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", + "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==", + "requires": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", "@popperjs/core": "^2.11.8", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "clsx": "^2.1.0", + "prop-types": "^15.8.1" }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" } } }, "@mui/core-downloads-tracker": { - "version": "5.13.4", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.4.tgz", - "integrity": "sha512-yFrMWcrlI0TqRN5jpb6Ma9iI7sGTHpytdzzL33oskFHNQ8UgrtPas33Y1K7sWAMwCrr1qbWDrOHLAQG4tAzuSw==" + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz", + "integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg==" }, "@mui/icons-material": { - "version": "5.11.16", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.16.tgz", - "integrity": "sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==", + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz", + "integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==", "requires": { - "@babel/runtime": "^7.21.0" + "@babel/runtime": "^7.23.9" } }, "@mui/lab": { - "version": "5.0.0-alpha.133", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.133.tgz", - "integrity": "sha512-pPL5/f6si8eCXlsnOZrO+/zg5Yv6qKa9OpI6nGP77Mpn7iYHm9qrcsWFIBs6YjgxqJf6dA2IqtHaSNOSndrXDw==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-beta.4", - "@mui/system": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "version": "5.0.0-alpha.170", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.170.tgz", + "integrity": "sha512-0bDVECGmrNjd3+bLdcLiwYZ0O4HP5j5WSQm5DV6iA/Z9kr8O6AnvZ1bv9ImQbbX7Gj3pX4o43EKwCutj3EQxQg==", + "requires": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" } } }, "@mui/material": { - "version": "5.13.4", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.4.tgz", - "integrity": "sha512-Yq+4f1KLPa/Szd3xqra2hbOAf2Usl8GbubncArM6LIp40mBLtXIdPE29MNtHsbtuzz4g+eidrETgoi3wdbEYfQ==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-beta.4", - "@mui/core-downloads-tracker": "^5.13.4", - "@mui/system": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "@types/react-transition-group": "^4.4.6", - "clsx": "^1.2.1", - "csstype": "^3.1.2", + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz", + "integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==", + "requires": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.16", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "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==" } } }, "@mui/private-theming": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.1.tgz", - "integrity": "sha512-HW4npLUD9BAkVppOUZHeO1FOKUJWAwbpy0VQoGe3McUYTlck1HezGHQCfBQ5S/Nszi7EViqiimECVl9xi+/WjQ==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", + "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", "requires": { - "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.13.1", + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.14", "prop-types": "^15.8.1" } }, "@mui/styled-engine": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.13.2.tgz", - "integrity": "sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", "requires": { - "@babel/runtime": "^7.21.0", + "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" } }, "@mui/styles": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.13.2.tgz", - "integrity": "sha512-gKNkVyk6azQ8wfCamh3yU/wLv1JscYrsQX9huQBwfwtE7kUTq2rgggdfJjRADjbcmT6IPX+oCHYjGfqqFgDIQQ==", + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.15.16.tgz", + "integrity": "sha512-WDv0M69fdfG8yJ5bmR8K81u+a7N/akSyVswKUavz//09EGS2cjPtInJspskMQNpZl2ReC5x/YEmkL+i5/PnmtA==", "requires": { - "@babel/runtime": "^7.21.0", + "@babel/runtime": "^7.23.9", "@emotion/hash": "^0.9.1", - "@mui/private-theming": "^5.13.1", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "clsx": "^1.2.1", - "csstype": "^3.1.2", + "@mui/private-theming": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "hoist-non-react-statics": "^3.3.2", "jss": "^10.10.0", "jss-plugin-camel-case": "^10.10.0", @@ -3694,74 +3531,73 @@ }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" } } }, "@mui/system": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.2.tgz", - "integrity": "sha512-TPyWmRJPt0JPVxacZISI4o070xEJ7ftxpVtu6LWuYVOUOINlhoGOclam4iV8PDT3EMQEHuUrwU49po34UdWLlw==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.13.1", - "@mui/styled-engine": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "clsx": "^1.2.1", - "csstype": "^3.1.2", + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", + "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "requires": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.14", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" } } }, "@mui/types": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", - "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==" + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==" }, "@mui/utils": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", - "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", + "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", "requires": { - "@babel/runtime": "^7.21.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.0", + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, "dependencies": { "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "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==" } } }, "@mui/x-data-grid": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.6.0.tgz", - "integrity": "sha512-RCAdQM4D0RWLnFCtuv6pJEFqUtH3WmlTU2Av+p3Ir2Utw03FQzc81oOpOZNv/6SXv+rMa+i42pb6fW0U5Xz0AQ==", + "version": "6.19.11", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.19.11.tgz", + "integrity": "sha512-QsUp2cPkjUm8vyTR5gYWuCFqxspljOzElbCm412wzvMTJSKaB0kz7CEecFhxjlsMjQ8B7kY8oDF3LXjjucFcPQ==", "requires": { - "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.13.1", - "clsx": "^1.2.1", + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16", + "clsx": "^2.0.0", "prop-types": "^15.8.1", "reselect": "^4.1.8" }, "dependencies": { "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" } } }, @@ -3800,66 +3636,28 @@ "fastq": "^1.6.0" } }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.13.tgz", + "integrity": "sha512-odZVYXly+JwzYri9rKqqUAk0cY6zLpv4dxoKinhoJNShV36Gpxf+CyDIILJ4tYsJ1ZxIWs233Y39iVnynvDA/g==", "dev": true, "requires": { "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", "core-js-pure": "^3.23.3", "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", "html-entities": "^2.1.0", "loader-utils": "^2.0.4", "schema-utils": "^3.0.0", "source-map": "^0.7.3" }, "dependencies": { - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -3958,15 +3756,15 @@ } }, "@rushstack/eslint-patch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", - "integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz", + "integrity": "sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==", "dev": true }, "@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" }, "@sinonjs/commons": { "version": "1.8.6", @@ -4167,9 +3965,9 @@ } }, "@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", + "version": "15.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.19.tgz", + "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -4255,9 +4053,9 @@ } }, "@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "dev": true, "requires": { "@adobe/css-tools": "^4.0.1", @@ -4479,9 +4277,9 @@ "dev": true }, "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "requires": { "@babel/parser": "^7.20.7", @@ -4492,18 +4290,18 @@ } }, "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "requires": { "@babel/types": "^7.0.0" } }, "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -4511,18 +4309,18 @@ } }, "@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "requires": { "@babel/types": "^7.20.7" } }, "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "requires": { "@types/connect": "*", @@ -4530,27 +4328,27 @@ } }, "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "requires": { "@types/node": "*" } }, "@types/connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "requires": { "@types/express-serve-static-core": "*", @@ -4567,9 +4365,9 @@ } }, "@types/eslint": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.0.tgz", - "integrity": "sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, "requires": { "@types/estree": "*", @@ -4577,9 +4375,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "requires": { "@types/eslint": "*", @@ -4587,15 +4385,15 @@ } }, "@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "requires": { "@types/body-parser": "*", @@ -4605,9 +4403,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", "dev": true, "requires": { "@types/node": "*", @@ -4617,15 +4415,15 @@ } }, "@types/geojson": { - "version": "7946.0.10", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", "dev": true }, "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "requires": { "@types/node": "*" @@ -4643,32 +4441,38 @@ "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", "dev": true }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, "@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "requires": { "@types/node": "*" } }, "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" }, "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "requires": { "@types/istanbul-lib-coverage": "*" } }, "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "requires": { "@types/istanbul-lib-report": "*" } @@ -4717,17 +4521,17 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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==", "dev": true } } }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/json5": { @@ -4737,18 +4541,18 @@ "dev": true }, "@types/leaflet": { - "version": "1.9.8", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz", - "integrity": "sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==", + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", "dev": true, "requires": { "@types/geojson": "*" } }, "@types/leaflet-draw": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@types/leaflet-draw/-/leaflet-draw-1.0.10.tgz", - "integrity": "sha512-1tV0QW5qAcTCmuZZwH19qngHayLxTE9vyDGicfMoASnrFf0oo3+lFOnn/ZRKSskyHKB7T8s6DTk7Henaq4ueyg==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/leaflet-draw/-/leaflet-draw-1.0.11.tgz", + "integrity": "sha512-dyedtNm3aSmnpi6FM6VSl28cQuvP+MD7pgpXyO3Q1ZOCvrJKmzaDq0P3YZTnnBs61fQCKSnNYmbvCkDgFT9FHQ==", "dev": true, "requires": { "@types/leaflet": "*" @@ -4773,23 +4577,23 @@ } }, "@types/lodash": { - "version": "4.14.195", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", - "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==" }, "@types/lodash-es": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz", - "integrity": "sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==", + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "dev": true, "requires": { "@types/lodash": "*" } }, "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "@types/node": { @@ -4798,19 +4602,28 @@ "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==", "dev": true }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node-sass": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@types/node-sass/-/node-sass-4.11.3.tgz", - "integrity": "sha512-wXPCn3t9uu5rR4zXNSLasZHQMuRzUKBsdi4MsgT8uq4Lp1gQQo+T2G23tGj4SSgDHeNBle6vGseZtM2XV/X9bw==", + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/@types/node-sass/-/node-sass-4.11.7.tgz", + "integrity": "sha512-QY0sXZGPRzJ2obo66f9zB6S0Uo9PRdcoPKPbyftSoKXub90s4ut/JK3fYHOqmhYhRRVEB3P5o5rEnq2/bWBdeg==", "dev": true, "requires": { "@types/node": "*" } }, "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "@types/prettier": { "version": "2.7.3", @@ -4819,61 +4632,52 @@ "dev": true }, "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "@types/pug": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.6.tgz", - "integrity": "sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", "dev": true }, "@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", "dev": true }, "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", "dev": true }, "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, "@types/react": { - "version": "18.2.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.8.tgz", - "integrity": "sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", + "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", "requires": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "@types/react-dom": { - "version": "18.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", - "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "requires": { "@types/react": "*" } }, - "@types/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", - "requires": { - "@types/react": "*" - } - }, "@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -4896,17 +4700,17 @@ } }, "@types/react-transition-group": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", - "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "requires": { "@types/react": "*" } }, "@types/react-window": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz", - "integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==", + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", "dev": true, "requires": { "@types/react": "*" @@ -4927,21 +4731,16 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, - "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "requires": { "@types/mime": "^1", @@ -4949,28 +4748,29 @@ } }, "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "requires": { "@types/express": "*" } }, "@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, "requires": { - "@types/mime": "*", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" } }, "@types/shapefile": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@types/shapefile/-/shapefile-0.6.1.tgz", - "integrity": "sha512-3WqvyHZ5qjTWzrC92eDT+sJXHtq82YI1UReJ37p0lP7xWxAhlpCDGtoZWZOgKApS7YJES2pp/NnqdesYriHQTQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@types/shapefile/-/shapefile-0.6.4.tgz", + "integrity": "sha512-xZubzHAy4n/OQo32u4l8qx8OGVe9nG258otq4389npOKwXbTXoQSVfqUqKVVrgeR/+FfJt/YSweiVE4kqLqFTA==", "dev": true, "requires": { "@types/geojson": "*", @@ -4978,9 +4778,9 @@ } }, "@types/shpjs": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@types/shpjs/-/shpjs-3.4.2.tgz", - "integrity": "sha512-i7wZAEM6QdtDLlqk3E7uW5Ru610ctnCsA5lNLwW9b2ZxnnTho/eLhPerpU9XLuEx1bBlDmpttEe8uBU51q2VPA==", + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/@types/shpjs/-/shpjs-3.4.7.tgz", + "integrity": "sha512-/6PjggpFsq9NFxar6ZpXsnYZ+nQJR8Cv03Gne1enIJuMZ/eFVOpu0orHxL9D7RT3ciJElzF2H6l+49US23ydUw==", "dev": true, "requires": { "@types/geojson": "*", @@ -4988,32 +4788,32 @@ } }, "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "requires": { "@types/node": "*" } }, "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, "@types/testing-library__jest-dom": { - "version": "5.14.6", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz", - "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==", + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", "dev": true, "requires": { "@types/jest": "*" } }, "@types/trusted-types": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", - "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "dev": true }, "@types/uuid": { @@ -5023,39 +4823,39 @@ "dev": true }, "@types/ws": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", - "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "requires": { "@types/node": "*" } }, "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "requires": { "@types/yargs-parser": "*" } }, "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "@typescript-eslint/eslint-plugin": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", - "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "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==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/type-utils": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -5072,9 +4872,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5089,62 +4889,62 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.59.9.tgz", - "integrity": "sha512-eZTK/Ci0QAqNc/q2MqMwI2+QI5ZI9HM12FcfGwbEvKif5ev/CIIYLmrlckvgPrC8XSbl39HtErR5NJiQkRkvWg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.59.9" + "@typescript-eslint/utils": "5.62.0" } }, "@typescript-eslint/parser": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", - "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", - "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" } }, "@typescript-eslint/type-utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", - "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", - "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", - "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5162,9 +4962,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5179,17 +4979,17 @@ } }, "@typescript-eslint/utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", - "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -5204,9 +5004,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -5221,19 +5021,25 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", - "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "requires": { "@webassemblyjs/helper-numbers": "1.11.6", @@ -5253,9 +5059,9 @@ "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true }, "@webassemblyjs/helper-numbers": { @@ -5276,15 +5082,15 @@ "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "@webassemblyjs/ieee754": { @@ -5312,28 +5118,28 @@ "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -5341,24 +5147,24 @@ } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -5367,12 +5173,12 @@ } }, "@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -5487,15 +5293,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "json-schema-traverse": { @@ -5518,12 +5324,9 @@ "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==" }, "ansi-escapes": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", - "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", - "requires": { - "type-fest": "^3.0.0" - } + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==" }, "ansi-html-community": { "version": "0.0.8", @@ -5587,56 +5390,22 @@ "dev": true }, "aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "requires": { - "deep-equal": "^2.0.5" - }, - "dependencies": { - "deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "dequal": "^2.0.3" } }, "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" } }, "array-find-index": { @@ -5650,15 +5419,16 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" } }, @@ -5673,54 +5443,112 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + } + }, "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" } }, "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" } }, "array.prototype.reduce": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", - "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz", + "integrity": "sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", "es-array-method-boxes-properly": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "is-string": "^1.0.7" } }, - "array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.tosorted": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", + "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.1.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" } }, "asap": { @@ -5737,15 +5565,16 @@ } }, "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "assert-never": { @@ -5759,15 +5588,15 @@ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" }, "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, "async-foreach": { @@ -5792,24 +5621,27 @@ "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==" }, "autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "dev": true, "requires": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" } }, "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } }, "aws-sign2": { "version": "0.7.0", @@ -5822,9 +5654,9 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, "axe-core": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", - "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", "dev": true }, "axios": { @@ -5846,46 +5678,12 @@ } }, "axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "dev": true, "requires": { - "deep-equal": "^2.0.5" - }, - "dependencies": { - "deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "dequal": "^2.0.3" } }, "babel-jest": { @@ -5918,9 +5716,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -5995,6 +5793,15 @@ "schema-utils": "^2.6.5" }, "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, "schema-utils": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", @@ -6050,33 +5857,33 @@ "dev": true }, "babel-plugin-polyfill-corejs2": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz", - "integrity": "sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dev": true, "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.4.0", - "semver": "^6.1.1" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz", - "integrity": "sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", + "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.0", - "core-js-compat": "^3.30.1" + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz", - "integrity": "sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.0" + "@babel/helper-define-polyfill-provider": "^0.6.2" } }, "babel-plugin-transform-react-remove-prop-types": { @@ -6137,21 +5944,6 @@ "@babel/runtime": "^7.16.3", "babel-plugin-macros": "^3.1.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - }, - "dependencies": { - "@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" - } - } } }, "babel-walk": { @@ -6187,14 +5979,15 @@ } }, "bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", + "integrity": "sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==", "dev": true, "requires": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", + "bluebird": "^3.7.2", + "check-types": "^11.2.3", "hoopy": "^0.1.4", + "jsonpath": "^1.1.1", "tryer": "^1.0.1" } }, @@ -6205,9 +5998,9 @@ "dev": true }, "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, "block-stream": { @@ -6257,23 +6050,13 @@ } }, "bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - } } }, "boolbase": { @@ -6306,14 +6089,14 @@ "dev": true }, "browserslist": { - "version": "4.21.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", - "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "requires": { - "caniuse-lite": "^1.0.30001489", - "electron-to-chromium": "^1.4.411", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" } }, "bser": { @@ -6352,12 +6135,15 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -6376,9 +6162,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -6416,9 +6202,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001495", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", - "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==" + "version": "1.0.30001614", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", + "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -6455,15 +6241,15 @@ } }, "check-types": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", - "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==", + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", + "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", "dev": true }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -6483,20 +6269,20 @@ "dev": true }, "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==" + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==" }, "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, "clean-css": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", - "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dev": true, "requires": { "source-map": "~0.6.0" @@ -6578,9 +6364,9 @@ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==" }, "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" }, "color-convert": { "version": "1.9.3", @@ -6620,12 +6406,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, "common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -6736,9 +6516,9 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "cookie": { "version": "0.4.2", @@ -6751,24 +6531,24 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "core-js": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.2.tgz", - "integrity": "sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg==", + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.0.tgz", + "integrity": "sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==", "dev": true }, "core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.0.tgz", + "integrity": "sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==", "dev": true, "requires": { - "browserslist": "^4.21.5" + "browserslist": "^4.23.0" } }, "core-js-pure": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.30.2.tgz", - "integrity": "sha512-p/npFUJXXBkCCTIlEGBdghofn00jWG6ZOtdoIXSJmAu2QBvN0IqpZXWweOytcwE6cfx8ZvVUy1vw8zxhe4Y2vg==", + "version": "3.37.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.0.tgz", + "integrity": "sha512-d3BrpyFr5eD4KcbRvQ3FTUx/KWmaDesr7+a3+1+P46IUnNoEt+oiLijPINZMEon7w9oGkIINWxrBAU9DEciwFQ==", "dev": true }, "core-util-is": { @@ -6829,9 +6609,9 @@ } }, "css-declaration-sorter": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", - "integrity": "sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", "dev": true }, "css-has-pseudo": { @@ -6844,19 +6624,19 @@ } }, "css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dev": true, "requires": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "dependencies": { "lru-cache": { @@ -6869,9 +6649,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -6900,15 +6680,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "ajv-keywords": { @@ -6927,9 +6707,9 @@ "dev": true }, "schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -7010,9 +6790,9 @@ "dev": true }, "cssdb": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.6.0.tgz", - "integrity": "sha512-Nna7rph8V0jC6+JBY4Vk4ndErUmfJfV6NJCaZdurL0omggabiy+QB2HCQtu5c/ACLZ0I7REv7A4QyPIoYzZx0w==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", "dev": true }, "cssesc": { @@ -7132,9 +6912,9 @@ } }, "csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "currently-unhandled": { "version": "0.4.1", @@ -7169,10 +6949,43 @@ "whatwg-url": "^8.0.0" } }, + "data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, "dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" }, "debug": { "version": "4.3.4", @@ -7200,16 +7013,16 @@ "dev": true }, "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "regexp.prototype.flags": "^1.5.1" } }, "deep-is": { @@ -7232,6 +7045,16 @@ "execa": "^5.0.0" } }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -7239,10 +7062,11 @@ "dev": true }, "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } @@ -7262,6 +7086,12 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true + }, "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", @@ -7333,16 +7163,10 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, "dns-packet": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", - "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "requires": { "@leichtgewicht/ip-codec": "^2.0.1" @@ -7445,9 +7269,9 @@ } }, "dompurify": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", - "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.2.tgz", + "integrity": "sha512-5vSyvxRAb45EoWwAktUT3AYqAwXK4FL7si22Cgj46U6ICsj/YJczCN+Bk7WNABIQmpWRymGfslMhrRUZkQNnqA==" }, "domutils": { "version": "1.7.0", @@ -7470,9 +7294,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -7495,6 +7319,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -7510,18 +7340,18 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "requires": { "jake": "^10.8.5" } }, "electron-to-chromium": { - "version": "1.4.420", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.420.tgz", - "integrity": "sha512-BpPy2KXZc+UPbI8NGo2QdHU1Mkq11pO/zaNDHY57L09K/0ytrPw+IiLOUvZ1NjI5BlAVF5DkNr1UBUS76Tc4ow==" + "version": "1.4.752", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz", + "integrity": "sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q==" }, "emittery": { "version": "0.13.1", @@ -7545,9 +7375,9 @@ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "enhanced-resolve": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", - "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -7578,45 +7408,57 @@ } }, "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.15" } }, "es-array-method-boxes-properly": { @@ -7625,55 +7467,74 @@ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", "dev": true }, - "es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" } }, "es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", "dev": true }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" } }, "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "es-to-primitive": { @@ -7687,16 +7548,10 @@ "is-symbol": "^1.0.2" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" }, "escape-html": { "version": "1.0.3", @@ -7709,15 +7564,14 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "requires": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { @@ -7727,76 +7581,38 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } } } }, "eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7806,7 +7622,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -7816,9 +7631,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -7874,9 +7688,9 @@ "dev": true }, "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -7909,9 +7723,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8029,14 +7843,14 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "requires": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" }, "dependencies": { "debug": { @@ -8051,9 +7865,9 @@ } }, "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", "dev": true, "requires": { "debug": "^3.2.7" @@ -8081,26 +7895,28 @@ } }, "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" }, "dependencies": { "debug": { @@ -8142,27 +7958,27 @@ } }, "eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", - "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" }, "dependencies": { "emoji-regex": { @@ -8192,26 +8008,29 @@ } }, "eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "version": "7.34.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", + "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", "dev": true, "requires": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", + "array-includes": "^3.1.7", + "array.prototype.findlast": "^1.2.4", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.17", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7", + "object.hasown": "^1.1.3", + "object.values": "^1.1.7", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.10" }, "dependencies": { "doctrine": { @@ -8239,12 +8058,12 @@ } }, "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -8252,15 +8071,15 @@ } }, "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true }, "eslint-plugin-testing-library": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", - "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.1.tgz", + "integrity": "sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==", "dev": true, "requires": { "@typescript-eslint/utils": "^5.58.0" @@ -8277,9 +8096,9 @@ } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "eslint-webpack-plugin": { @@ -8296,15 +8115,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "ajv-keywords": { @@ -8340,9 +8159,9 @@ "dev": true }, "schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -8363,20 +8182,20 @@ } }, "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, "dependencies": { "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } @@ -8635,9 +8454,9 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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==", "dev": true }, "slash": { @@ -8731,9 +8550,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -8755,9 +8574,9 @@ "dev": true }, "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -8809,9 +8628,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -8904,6 +8723,17 @@ "commondir": "^1.0.1", "make-dir": "^3.0.2", "pkg-dir": "^4.1.0" + }, + "dependencies": { + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + } } }, "find-root": { @@ -8921,12 +8751,13 @@ } }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "dependencies": { @@ -8942,15 +8773,15 @@ } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-each": { "version": "0.3.3", @@ -8961,6 +8792,44 @@ "is-callable": "^1.1.3" } }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -9079,9 +8948,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9147,9 +9016,9 @@ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true }, "fresh": { @@ -9175,9 +9044,9 @@ } }, "fs-monkey": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", - "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", "dev": true }, "fs.realpath": { @@ -9186,9 +9055,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, @@ -9204,20 +9073,20 @@ } }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" } }, "functions-have-names": { @@ -9287,14 +9156,15 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-own-enumerable-property-symbols": { @@ -9321,13 +9191,14 @@ "dev": true }, "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" } }, "getpass": { @@ -9402,12 +9273,13 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "requires": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" } }, "globby": { @@ -9461,7 +9333,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.3" } @@ -9471,12 +9342,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -9498,25 +9363,6 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -9537,14 +9383,6 @@ "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -9572,17 +9410,17 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" } }, "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" }, "has-symbols": { "version": "1.0.3", @@ -9590,11 +9428,11 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" } }, "has-unicode": { @@ -9602,6 +9440,14 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -9662,9 +9508,9 @@ } }, "html-entities": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.4.tgz", - "integrity": "sha512-TtiHkpRBqP40OzizVWjwBPBsiqchEZxAg/nys6D6lIpdoVLo7sWZ/5Sf/s4UaBHQ6pzUzEr3NiItvEoO46sPtQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true }, "html-escaper": { @@ -9697,9 +9543,9 @@ } }, "html-webpack-plugin": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.1.tgz", - "integrity": "sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "dev": true, "requires": { "@types/html-minifier-terser": "^6.0.0", @@ -9876,9 +9722,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "immediate": { @@ -9951,13 +9797,13 @@ "dev": true }, "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" } }, @@ -9976,14 +9822,13 @@ } }, "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" } }, "is-arrayish": { @@ -9991,6 +9836,15 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, + "is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -10032,11 +9886,20 @@ "dev": true }, "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, "requires": { - "has": "^1.0.3" + "is-typed-array": "^1.1.13" } }, "is-date-object": { @@ -10068,8 +9931,17 @@ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, - "is-finite": { - "version": "1.1.0", + "is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-finite": { + "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" }, @@ -10111,9 +9983,9 @@ "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" }, "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true }, "is-module": { @@ -10133,9 +10005,9 @@ } }, "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, "is-number": { @@ -10203,18 +10075,18 @@ "dev": true }, "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true }, "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" } }, "is-stream": { @@ -10242,16 +10114,12 @@ } }, "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.14" } }, "is-typedarray": { @@ -10265,9 +10133,9 @@ "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" }, "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true }, "is-weakref": { @@ -10280,13 +10148,13 @@ } }, "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" } }, "is-wsl": { @@ -10314,9 +10182,9 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true }, "istanbul-lib-instrument": { @@ -10333,13 +10201,13 @@ } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "dependencies": { @@ -10380,15 +10248,38 @@ } }, "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "requires": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" } }, + "iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "requires": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -10512,9 +10403,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -10759,9 +10650,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -10885,9 +10776,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11100,9 +10991,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11310,9 +11201,9 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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==", "dev": true }, "supports-color": { @@ -11362,9 +11253,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11495,9 +11386,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11596,9 +11487,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11710,9 +11601,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -11854,9 +11745,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12152,9 +12043,9 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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==", "dev": true }, "supports-color": { @@ -12169,17 +12060,17 @@ } }, "jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -12258,9 +12149,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12324,9 +12215,9 @@ "dev": true }, "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==" + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-resolve": { "version": "27.5.1", @@ -12360,9 +12251,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12464,9 +12355,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12598,9 +12489,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12795,9 +12686,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -12982,9 +12873,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -13144,9 +13035,9 @@ "dev": true }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -13185,11 +13076,11 @@ } }, "jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "requires": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -13198,9 +13089,12 @@ }, "dependencies": { "@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==" + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } }, "ansi-styles": { "version": "4.3.0", @@ -13275,9 +13169,9 @@ } }, "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -13386,31 +13280,34 @@ }, "dependencies": { "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" } } }, "jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "requires": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "dependencies": { "@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==" + "version": "20.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", + "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "requires": { + "undici-types": "~5.26.4" + } }, "ansi-escapes": { "version": "4.3.2", @@ -13479,11 +13376,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" } } }, @@ -13516,9 +13408,9 @@ } }, "jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "dev": true }, "js-base64": { @@ -13591,9 +13483,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true }, "form-data": { @@ -13608,9 +13500,9 @@ } }, "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, "requires": { "psl": "^1.1.33", @@ -13632,6 +13524,12 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13673,6 +13571,25 @@ "universalify": "^2.0.0" } }, + "jsonpath": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", + "dev": true, + "requires": { + "esprima": "1.2.2", + "static-eval": "2.0.2", + "underscore": "1.12.1" + }, + "dependencies": { + "esprima": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", + "dev": true + } + } + }, "jsonpointer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", @@ -13778,13 +13695,15 @@ } }, "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" } }, "jszip": { @@ -13812,6 +13731,15 @@ "js-sha256": "^0.9.0" } }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -13837,22 +13765,22 @@ "dev": true }, "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "requires": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" } }, "launch-editor": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", - "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", + "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", "dev": true, "requires": { "picocolors": "^1.0.0", - "shell-quote": "^1.7.3" + "shell-quote": "^1.8.1" } }, "leaflet": { @@ -14037,9 +13965,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -14068,12 +13996,38 @@ } }, "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^6.0.0" + "semver": "^7.5.3" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "makeerror": { @@ -14102,12 +14056,12 @@ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memfs": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.2.tgz", - "integrity": "sha512-4kbWXbVZ+LU4XFDS2CuA7frnwz2HxCMB/0yOXc86q7aCQrfWKkL11t6al1e2CsVC7uhnBNTQ1TfUsAxVauO9IQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "requires": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" } }, "memoize-one": { @@ -14199,24 +14153,25 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, "requires": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "ajv-keywords": { @@ -14235,9 +14190,9 @@ "dev": true }, "schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -14267,6 +14222,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -14302,9 +14263,9 @@ } }, "nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==" }, "nanoclone": { "version": "0.2.1", @@ -14312,9 +14273,9 @@ "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true }, "natural-compare": { @@ -14337,7 +14298,8 @@ "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, "no-case": { "version": "3.0.4", @@ -14350,9 +14312,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -14396,9 +14358,9 @@ "dev": true }, "node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, "node-sass": { "version": "4.14.1", @@ -14481,9 +14443,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" } } }, @@ -14540,9 +14502,9 @@ "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==" }, "nwsapi": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", - "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", + "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==", "dev": true }, "oauth-sign": { @@ -14562,18 +14524,18 @@ "dev": true }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" } }, "object-keys": { @@ -14582,71 +14544,86 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "object.getownpropertydescriptors": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", - "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz", + "integrity": "sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==", "dev": true, "requires": { - "array.prototype.reduce": "^1.0.5", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.21.2", - "safe-array-concat": "^1.0.0" + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "gopd": "^1.0.1", + "safe-array-concat": "^1.1.2" + } + }, + "object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" } }, "object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "dev": true, "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "obuf": { @@ -14656,9 +14633,9 @@ "dev": true }, "oidc-client-ts": { - "version": "3.0.0-rc.0", - "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.0-rc.0.tgz", - "integrity": "sha512-xJNUNrx+o21+IQjSw6rih3xM1B2wnILA/Sv6TEQrA+ghU4gNwpMHPnT4L2I/tH0KIIs86zca8/8wvoZ7HGOtjw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.0.1.tgz", + "integrity": "sha512-xX8unZNtmtw3sOz4FPSqDhkLFnxCDsdo2qhFEH2opgWnF/iXMFoYdBQzkwCxAZVgt3FT3DnuBY3k80EZHT0RYg==", "requires": { "jwt-decode": "^4.0.0" } @@ -14706,9 +14683,9 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -14716,7 +14693,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" } }, "os-homedir": { @@ -14785,9 +14762,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -14842,9 +14819,9 @@ }, "dependencies": { "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true } } @@ -14879,6 +14856,24 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true + } + } + }, "path-source": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/path-source/-/path-source-0.1.3.tgz", @@ -14932,9 +14927,9 @@ } }, "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true }, "pkg-dir": { @@ -15003,22 +14998,29 @@ } }, "polygon-clipping": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.3.tgz", - "integrity": "sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz", + "integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==", "requires": { + "robust-predicates": "^3.0.2", "splaytree": "^3.1.0" } }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true + }, "postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "requires": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" } }, "postcss-attribute-case-insensitive": { @@ -15265,19 +15267,25 @@ } }, "postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, "requires": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "dependencies": { + "lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true + }, "yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", "dev": true } } @@ -15303,9 +15311,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -15394,15 +15402,15 @@ } }, "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true }, "postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dev": true, "requires": { "icss-utils": "^5.0.0", @@ -15411,9 +15419,9 @@ } }, "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.4" @@ -15679,9 +15687,9 @@ } }, "postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -15858,11 +15866,11 @@ } }, "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "requires": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -15873,9 +15881,9 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "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==" } } }, @@ -15885,12 +15893,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "proj4": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.9.0.tgz", - "integrity": "sha512-BoDXEzCVnRJVZoOKA0QHTFtYoE8lUxtX1jST38DJ8U+v1ixY70Kpwi0Llu6YqSWEH2xqu4XMEBNGcgeRIEywoA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.11.0.tgz", + "integrity": "sha512-SasuTkAx8HnWQHfIyhkdUNJorSJqINHAN3EyMWYiQRVorftz9DHz650YraFgczwgtHOxqnfuDxSNv3C8MUnHeg==", "requires": { "mgrs": "1.0.0", - "wkt-parser": "^1.3.1" + "wkt-parser": "^1.3.3" } }, "promise": { @@ -15922,9 +15930,9 @@ } }, "property-expr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", - "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" }, "proxy-addr": { "version": "2.0.7", @@ -16058,9 +16066,9 @@ "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "q": { "version": "1.5.1", @@ -16150,6 +16158,12 @@ "requires": { "asap": "~2.0.6" } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true } } }, @@ -16163,9 +16177,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -16399,9 +16413,9 @@ } }, "react-oidc-context": { - "version": "3.0.0-beta.0", - "resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-3.0.0-beta.0.tgz", - "integrity": "sha512-Y3WjJ+2qBpNqkX7DTnYc2WRLhXajX2tCv2S5rSB05J9IOjAOBYPXArIlHbjp6UWnSBW8rGbobmoRB9clrqIcWg==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-oidc-context/-/react-oidc-context-3.1.0.tgz", + "integrity": "sha512-ceQztvDfdl28mbr0So31XF/tCJamyF1+nm4AQNIE/nub+Xs9PLtDqLy/+75Yx1ahI0/n3nsq0R2qcP0R2Laa3Q==" }, "react-refresh": { "version": "0.11.0", @@ -16761,15 +16775,15 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "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==", "dev": true }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -16790,12 +16804,6 @@ "has-flag": "^4.0.0" } }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -16816,9 +16824,9 @@ } }, "react-window": { - "version": "1.8.9", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.9.tgz", - "integrity": "sha512-+Eqx/fj1Aa5WnhRfj9dJg4VYATGwIUP2ItwItiJ6zboKWA6EX3lYDAXfGF2hyNqplEprhbtjbipiADEcwQ823Q==", + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", + "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", "requires": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" @@ -16912,6 +16920,21 @@ "strip-indent": "^1.0.1" } }, + "reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -16919,42 +16942,43 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" } }, "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", "dev": true }, "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" } }, "regexpu-core": { @@ -17158,11 +17182,11 @@ "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -17207,6 +17231,12 @@ "source-map": "0.6.1" }, "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "picocolors": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", @@ -17257,6 +17287,11 @@ "glob": "^7.1.3" } }, + "robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "rollup": { "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", @@ -17325,13 +17360,13 @@ } }, "safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -17350,13 +17385,13 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" } }, @@ -17417,9 +17452,9 @@ } }, "schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "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==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -17453,18 +17488,19 @@ "dev": true }, "selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "requires": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "send": { "version": "0.17.2", @@ -17509,9 +17545,9 @@ } }, "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -17589,6 +17625,30 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -17661,14 +17721,15 @@ } }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" } }, "signal-exit": { @@ -17715,9 +17776,9 @@ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" }, "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true }, "source-map-loader": { @@ -17776,9 +17837,9 @@ } }, "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" }, "spdx-expression-parse": { "version": "3.0.1", @@ -17790,9 +17851,9 @@ } }, "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" }, "spdy": { "version": "4.0.2", @@ -17846,9 +17907,9 @@ "dev": true }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -17888,6 +17949,76 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "dev": true }, + "static-eval": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", + "dev": true, + "requires": { + "escodegen": "^1.8.1" + }, + "dependencies": { + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -17901,15 +18032,6 @@ "readable-stream": "^2.0.1" } }, - "stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "requires": { - "internal-slot": "^1.0.4" - } - }, "stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -17985,53 +18107,92 @@ } } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" } }, "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string_decoder": { @@ -18075,6 +18236,15 @@ } } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -18110,9 +18280,9 @@ "dev": true }, "style-loader": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", - "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", "dev": true }, "stylehacks": { @@ -18131,20 +18301,29 @@ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, "sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "7.1.6", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -18152,17 +18331,25 @@ "dev": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" } } } @@ -18262,9 +18449,9 @@ "dev": true }, "tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", + "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", "dev": true, "requires": { "@alloc/quick-lru": "^5.2.0", @@ -18272,10 +18459,10 @@ "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -18287,7 +18474,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -18363,19 +18549,13 @@ "requires": { "type-fest": "^0.21.3" } - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true } } }, "terser": { - "version": "5.17.7", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz", - "integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==", + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, "requires": { "@jridgewell/source-map": "^0.3.3", @@ -18385,24 +18565,24 @@ }, "dependencies": { "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } }, "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" + "terser": "^5.26.0" } }, "test-exclude": { @@ -18463,9 +18643,9 @@ "dev": true }, "tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, "tiny-warning": { "version": "1.0.3", @@ -18555,9 +18735,9 @@ "dev": true }, "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "requires": { "@types/json5": "^0.0.29", @@ -18626,9 +18806,9 @@ "dev": true }, "type-fest": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.11.1.tgz", - "integrity": "sha512-aCuRNRERRVh33lgQaJRlUxZqzfhzwTrsE98Mc3o3VXqmiaQdHacgUtJ0esp+7MvZ92qhtzKPeusaX6vIEcoreA==" + "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-is": { "version": "1.6.18", @@ -18639,15 +18819,56 @@ "mime-types": "~2.1.24" } }, + "typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" } }, "typedarray-to-buffer": { @@ -18664,12 +18885,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.6.tgz", "integrity": "sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==" }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true - }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -18682,6 +18897,17 @@ "which-boxed-primitive": "^1.0.2" } }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "dev": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -18720,9 +18946,9 @@ } }, "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true }, "unpipe": { @@ -18743,11 +18969,11 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.14.tgz", + "integrity": "sha512-JixKH8GR2pWYshIPUg/NujK3JO7JiqEEUiNArE86NQyrgUuZeTlZQN3xuS/yiV5Kb48ev9K6RqNkaJjXsdg7Jw==", "requires": { - "escalade": "^3.1.1", + "escalade": "^3.1.2", "picocolors": "^1.0.0" } }, @@ -18826,6 +19052,12 @@ "source-map": "^0.7.3" }, "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, "source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -18903,9 +19135,9 @@ } }, "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -18928,49 +19160,49 @@ "dev": true }, "webpack": { - "version": "5.85.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.85.1.tgz", - "integrity": "sha512-xTb7MRf4LY8Z5rzn7aIx4TDrwYJrjcHnIfU1TqtyZOoObyuGSpAUwIvVuqq5wPnv7WEgQr8UvO1q/dgoGG4HjA==", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.14.1", + "enhanced-resolve": "^5.16.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.2", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "dependencies": { "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } }, "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "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==", "dev": true, "requires": { "colorette": "^2.0.10", @@ -18981,15 +19213,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "ajv-keywords": { @@ -19008,9 +19240,9 @@ "dev": true }, "schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -19022,9 +19254,9 @@ } }, "webpack-dev-server": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz", - "integrity": "sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", @@ -19033,7 +19265,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -19055,20 +19287,20 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", + "webpack-dev-middleware": "^5.3.4", "ws": "^8.13.0" }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "ajv-keywords": { @@ -19081,9 +19313,9 @@ } }, "ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true }, "json-schema-traverse": { @@ -19102,9 +19334,9 @@ } }, "schema-utils": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", - "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", @@ -19114,9 +19346,9 @@ } }, "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", "dev": true } } @@ -19182,9 +19414,9 @@ } }, "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "dev": true }, "whatwg-mimetype": { @@ -19225,16 +19457,44 @@ "is-symbol": "^1.0.3" } }, + "which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "requires": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" } }, "which-module": { @@ -19243,17 +19503,16 @@ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.2" } }, "wide-align": { @@ -19281,16 +19540,11 @@ "integrity": "sha512-ZnV3yH8/k58ZPACOXeiHaMuXIiaTk1t0hSUVisbO0t4RjA5wPpUytcxeyiN2h+LZRrmuHIh/1UlrR9e7DHDvTw==" }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, "workbox-background-sync": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", @@ -19356,15 +19610,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "fs-extra": { @@ -19617,6 +19871,75 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/app/package.json b/app/package.json index cba542b85..50837ea35 100644 --- a/app/package.json +++ b/app/package.json @@ -52,7 +52,6 @@ "express": "~4.17.1", "formik": "~2.2.6", "fuse.js": "^7.0.0", - "handlebars": "^4.7.7", "jest-watch-typeahead": "^2.2.2", "jszip": "^3.10.1", "keycloak-js": "^20.0.2", diff --git a/app/src/components/map/DatasetPopup.tsx b/app/src/components/map/DatasetPopup.tsx deleted file mode 100644 index 19af5e11f..000000000 --- a/app/src/components/map/DatasetPopup.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import Box from '@mui/material/Box'; -import CircularProgress from '@mui/material/CircularProgress'; -import Collapse from '@mui/material/Collapse'; -import Link from '@mui/material/Link'; -import Typography from '@mui/material/Typography'; -import { makeStyles } from '@mui/styles'; -import { SPATIAL_COMPONENT_TYPE } from 'constants/spatial'; -import { Feature } from 'geojson'; -import { useApi } from 'hooks/useApi'; -import useDataLoader from 'hooks/useDataLoader'; -import { useHistory } from 'react-router'; - -export type BoundaryCentroidFeature = Feature & { properties: BoundaryCentroidFeatureProperties }; - -export type BoundaryCentroidFeatureProperties = { - type: SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID; - datasetTitle: string; - datasetID: string; -}; - -const useStyles = makeStyles(() => ({ - modalContent: { - position: 'relative', - width: 300, - minHeight: 36 - }, - loading: { - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)' - }, - pointType: { - lineHeight: 'unset' - }, - date: { - margin: 0, - lineHeight: 'unset' - }, - table: { - marginTop: 16 - }, - tableCell: { - padding: 8, - fontSize: '0.625rem' - } -})); - -const DatasetPopup: React.FC> = ({ - submissionSpatialComponentIds -}) => { - const classes = useStyles(); - const api = useApi(); - const history = useHistory(); - - const dataLoader = useDataLoader(() => { - return api.search.getSpatialMetadata(submissionSpatialComponentIds); - }); - - dataLoader.load(); - - const { isLoading, isReady } = dataLoader; - const data = (dataLoader.data || []) as BoundaryCentroidFeatureProperties[]; - - const ModalContentWrapper: React.FC = ({ children }) => ( -
{children}
- ); - - const MetadataHeader: React.FC> = (props) => ( - - - Dataset - - {props.title && ( - - {props.title} - - )} - - ); - - const NoMetadataAvailable: React.FC = () => ( - - No metadata available. - - ); - - if (isLoading) { - return ( - -
- -
-
- ); - } - - if (!data) { - return ( - - - - ); - } - - return ( - <> - {data.map((metadata) => { - const datasetTitle = metadata.datasetTitle; - const datasetID = metadata.datasetID; - - return ( - - - - history.push(`/datasets/${datasetID}/details`)}> - Go to Dataset - - - - ); - })} - - ); -}; - -export default DatasetPopup; diff --git a/app/src/components/map/FeaturePopup.tsx b/app/src/components/map/FeaturePopup.tsx deleted file mode 100644 index 9e0eeb8ee..000000000 --- a/app/src/components/map/FeaturePopup.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { mdiChevronLeft, mdiChevronRight } from '@mdi/js'; -import Icon from '@mdi/react'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import CircularProgress from '@mui/material/CircularProgress'; -import Collapse from '@mui/material/Collapse'; -import Divider from '@mui/material/Divider'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableRow from '@mui/material/TableRow'; -import Typography from '@mui/material/Typography'; -import { makeStyles } from '@mui/styles'; -import { DATE_FORMAT } from 'constants/dateTimeFormats'; -import { SPATIAL_COMPONENT_TYPE } from 'constants/spatial'; -import { Feature, GeoJsonProperties } from 'geojson'; -import { useApi } from 'hooks/useApi'; -import useDataLoader from 'hooks/useDataLoader'; -import { useState } from 'react'; -import { getFormattedDate, makeCsvObjectUrl } from 'utils/Utils'; - -export type OccurrenceFeature = Feature & { properties: OccurrenceFeatureProperties }; - -export type OccurrenceFeatureProperties = { - type: SPATIAL_COMPONENT_TYPE.OCCURRENCE; -}; - -export type BoundaryFeature = Feature & { properties: BoundaryFeatureProperties }; - -export type BoundaryFeatureProperties = { - type: SPATIAL_COMPONENT_TYPE.BOUNDARY; -}; - -export type BoundaryCentroidFeature = Feature & { properties: BoundaryCentroidFeatureProperties }; - -export type BoundaryCentroidFeatureProperties = { - type: SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID; - datasetID: string; - datasetTitle: string; -}; - -export enum COMMON_METADATA_PROPERTIES { - 'vernacularName' = 'Species', - 'individualCount' = 'Count', - 'eventDate' = 'Date', - 'lifeStage' = 'Life Stage', - 'sex' = 'Sex' -} - -export enum UNCOMMON_METADATA_PROPERTIES { - 'type' = 'Type', - 'datasetID' = 'Dataset ID', - 'verbatimSRS' = 'Verbatim SRS', - 'occurrenceID' = 'Occurrence ID', - 'basisOfRecord' = 'Basis of Record', - 'taxonID' = 'Taxon ID', - 'verbatimCoordinates' = 'Verbatim Coordinates' -} - -export const ALL_METADATA_PROPERTIES = { - ...COMMON_METADATA_PROPERTIES, - ...UNCOMMON_METADATA_PROPERTIES -}; - -export const formatMetadataProperty: Partial< - Record string> -> = { - eventDate: (dateString: string) => getFormattedDate(DATE_FORMAT.ShortMediumDateFormat, dateString) -}; - -interface IMetadataHeaderProps { - type?: string; - date?: string; - index?: number; - length?: number; -} - -type IDwCMetadata = ({ dwc: Record } & GeoJsonProperties) | null; - -const useStyles = makeStyles(() => ({ - modalContent: { - position: 'relative', - width: 300, - minHeight: 36 - }, - loading: { - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)' - }, - pointType: { - lineHeight: 'unset' - }, - date: { - margin: 0, - lineHeight: 'unset' - } -})); - -const FeaturePopup: React.FC> = (props) => { - const { submissionSpatialComponentIds } = props; - - const classes = useStyles(); - const api = useApi(); - const [currentIndex, setCurrentIndex] = useState(0); - - const metadataLoader = useDataLoader(() => { - return api.search.getSpatialMetadata(submissionSpatialComponentIds); - }); - - metadataLoader.load(); - - const data = metadataLoader.data || []; - const isEmpty = data.length === 0; - const metadataObjectUrl = isEmpty ? undefined : makeCsvObjectUrl(data.map((row) => row?.dwc || {})); - - const handleNext = () => { - if (isEmpty) { - return; - } - - setCurrentIndex((currentIndex + 1) % data.length); - }; - - const handlePrev = () => { - if (isEmpty) { - return; - } - if (currentIndex === 0) { - setCurrentIndex(data.length - 1); - } else { - setCurrentIndex(currentIndex - 1); - } - }; - - const ModalContentWrapper: React.FC = ({ children }) => ( -
{children}
- ); - - const MetadataHeader: React.FC> = (headerProps) => { - const { date, index, length } = headerProps; - - return ( - - - {length && length > 1 ? `Records (${(index || 0) + 1} of ${length})` : 'Record'} - - {date && ( - - {getFormattedDate(DATE_FORMAT.ShortMediumDateFormat, date)} - - )} - - ); - }; - - const NoMetadataAvailable: React.FC = () => ( - - No metadata available. - - ); - - if (metadataLoader.isLoading) { - return ( - -
- -
-
- ); - } - - if (data.length === 0) { - return ( - - - - ); - } - - const metadata = data[currentIndex]; - const type = metadata?.type; - const dwc: Record = metadata?.dwc || {}; - const filteredMetadata = Object.entries(COMMON_METADATA_PROPERTIES).filter(([key]) => Boolean(dwc[key])); - - if (!dwc || !Object.keys(dwc).length) { - return ( - - - - - ); - } - - return ( - - - - - - - {filteredMetadata.map(([key, propertyName]) => { - return ( - - {propertyName} - {(formatMetadataProperty[key] || String)(dwc[key])} - - ); - })} - -
-
- - {!isEmpty && data.length > 1 && ( - - - - - )} - {metadataObjectUrl && ( - - - - )} - -
- ); -}; - -export default FeaturePopup; diff --git a/app/src/components/map/MapContainer.test.tsx b/app/src/components/map/MapContainer.test.tsx deleted file mode 100644 index 3e3571740..000000000 --- a/app/src/components/map/MapContainer.test.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import bbox from '@turf/bbox'; -import { LatLngBoundsExpression } from 'leaflet'; -import { cleanup, render } from 'test-helpers/test-utils'; -import { IMarkerLayer } from './components/MarkerClusterControls'; -import { IStaticLayer } from './components/StaticLayersControls'; -import MapContainer from './MapContainer'; - -describe('MapContainer', () => { - afterEach(() => { - cleanup(); - }); - - it('renders with minimal settings', () => { - const { container } = render(); - - expect(container.querySelector('#myMap')).toBeInTheDocument(); - }); - - it('renders with static geometries', () => { - const staticLayers: IStaticLayer[] = [ - { - visible: true, - layerName: 'test layer', - features: [ - { - geoJSON: { - id: 'nonEditableGeo', - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [125.6, 10.1] - }, - properties: { - name: 'Biodiversity Land' - } - } - } - ] - } - ]; - - const { container } = render(); - - expect(container.querySelector('#myMap')).toBeInTheDocument(); - }); - - it('renders with bounds', () => { - const bboxCoords = bbox({ - id: 'myGeo', - type: 'Feature', - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-128, 55], - [-128, 55.5], - [-128, 56], - [-126, 58], - [-128, 55] - ] - ] - }, - properties: { - name: 'Restoration Islands' - } - }); - - const bounds: LatLngBoundsExpression = [ - [bboxCoords[1], bboxCoords[0]], - [bboxCoords[3], bboxCoords[2]] - ]; - - const { container } = render(); - - expect(container.querySelector('#myMap')).toBeInTheDocument(); - }); - - it('renders with scrollWheelZoom', () => { - const { container } = render(); - - expect(container.querySelector('#myMap')).toBeInTheDocument(); - }); - - it('renders with markers', () => { - const markerLayers: IMarkerLayer[] = [ - { - visible: true, - layerName: 'test-marker-layer', - markers: [{ position: [55, 128], key: 'key', count: 1 }] - } - ]; - - const { container } = render(); - - expect(container.querySelector('#myMap')).toBeInTheDocument(); - }); -}); diff --git a/app/src/components/map/MapContainer.tsx b/app/src/components/map/MapContainer.tsx deleted file mode 100644 index 5796b6139..000000000 --- a/app/src/components/map/MapContainer.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { makeStyles } from '@mui/styles'; -import { MAP_DEFAULT_ZOOM, MAP_MAX_ZOOM, MAP_MIN_ZOOM } from 'constants/spatial'; -import L, { LatLngBoundsExpression, LeafletEventHandlerFnMap } from 'leaflet'; -import 'leaflet-draw/dist/leaflet.draw.css'; -import 'leaflet-fullscreen/dist/leaflet.fullscreen.css'; -import 'leaflet-fullscreen/dist/Leaflet.fullscreen.js'; -import iconRetina from 'leaflet/dist/images/marker-icon-2x.png'; -import icon from 'leaflet/dist/images/marker-icon.png'; -import iconShadow from 'leaflet/dist/images/marker-shadow.png'; -import 'leaflet/dist/leaflet.css'; -import { FeatureGroup, LayersControl, MapContainer as LeafletMapContainer } from 'react-leaflet'; -import BaseLayer from './components/BaseLayer'; -import BaseLayerControls from './components/BaseLayerControls'; -import { GetMapBounds, IMapBoundsOnChange, SetMapBounds } from './components/Bounds'; -import DrawControls, { IDrawControlsOnChange, IDrawControlsProps } from './components/DrawControls'; -import EventHandler from './components/EventHandler'; -import FullScreenScrollingEventHandler from './components/FullScreenScrollingEventHandler'; -import MarkerCluster from './components/MarkerCluster'; -import MarkerClusterControls, { IMarkerLayer } from './components/MarkerClusterControls'; -import StaticLayers from './components/StaticLayers'; -import StaticLayersControls, { IStaticLayer } from './components/StaticLayersControls'; - -const useStyles = makeStyles(() => ({ - map: { - height: '100%' - } -})); - -// @ts-ignore -delete L.Icon.Default.prototype._getIconUrl; -L.Icon.Default.mergeOptions({ - iconRetinaUrl: iconRetina, - iconUrl: icon, - shadowUrl: iconShadow -}); - -export interface IMapContainerProps { - mapId: string; - staticLayers?: IStaticLayer[]; - drawControls?: IDrawControlsProps; - zoomControlEnabled?: boolean; - doubleClickZoomEnabled?: boolean; - draggingEnabled?: boolean; - layerControlEnabled?: boolean; - onDrawChange?: IDrawControlsOnChange; - scrollWheelZoom?: boolean; - fullScreenControl?: boolean; - markerLayers?: IMarkerLayer[]; - bounds?: LatLngBoundsExpression; - zoom?: number; - eventHandlers?: LeafletEventHandlerFnMap; - LeafletMapContainerProps?: Partial>; - onBoundsChange?: IMapBoundsOnChange; -} - -const MapContainer: React.FC> = (props) => { - const classes = useStyles(); - - const { - mapId, - staticLayers, - drawControls, - onDrawChange, - scrollWheelZoom, - fullScreenControl, - markerLayers, - bounds, - zoom, - eventHandlers, - LeafletMapContainerProps, - onBoundsChange, - zoomControlEnabled, - doubleClickZoomEnabled, - draggingEnabled, - layerControlEnabled - } = props; - - const fullscreenControlProp = (fullScreenControl && { pseudoFullscreen: true }) || undefined; - - return ( - - - - - onBoundsChange?.(newBounds, newZoom)} /> - - {drawControls && ( - - - - )} - - - {layerControlEnabled ? ( - - - - - - ) : ( - // each of these layer components was duplicated and had any 'control' components removed - // without the and any of the internal control components these layers - // can properly render without the control - <> - - - - - )} - - ); -}; - -export default MapContainer; diff --git a/app/src/components/map/components/BaseLayer.tsx b/app/src/components/map/components/BaseLayer.tsx deleted file mode 100644 index dbc275cc4..000000000 --- a/app/src/components/map/components/BaseLayer.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { TileLayer } from 'react-leaflet'; - -// Compare this with BaseLayerControls.tsx -// Without the parent control component this component can only return a single Tile Layer -const BaseLayer: React.FC = () => { - return ( - <> - - - ); -}; - -export default BaseLayer; diff --git a/app/src/components/map/components/BaseLayerControls.tsx b/app/src/components/map/components/BaseLayerControls.tsx deleted file mode 100644 index 717e6ac05..000000000 --- a/app/src/components/map/components/BaseLayerControls.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { LayersControl, TileLayer } from 'react-leaflet'; - -const BaseLayerControls: React.FC = () => { - return ( - <> - - - - - - - - ); -}; - -export default BaseLayerControls; diff --git a/app/src/components/map/components/Bounds.tsx b/app/src/components/map/components/Bounds.tsx deleted file mode 100644 index d2a5821fd..000000000 --- a/app/src/components/map/components/Bounds.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Feature, Polygon } from 'geojson'; -import { LatLngBoundsExpression } from 'leaflet'; -import { useMap, useMapEvents } from 'react-leaflet'; -import { getFeatureObjectFromLatLngBounds } from 'utils/Utils'; - -export interface ISetMapBoundsProps { - bounds?: LatLngBoundsExpression; - zoom?: number; -} - -export const SetMapBounds: React.FC> = (props) => { - const map = useMap(); - - // Set bounds if provided, ignore zoom - if (props.bounds) { - map.fitBounds(props.bounds); - - return null; - } - - // Set zoom if provided - if (props.zoom) { - map.setZoom(props.zoom); - - return null; - } - - return null; -}; - -export type IMapBoundsOnChange = (bounds: Feature, zoom: number) => void; - -export interface IGetMapBoundsProps { - onChange: IMapBoundsOnChange; -} - -export const GetMapBounds: React.FC> = (props) => { - const { onChange } = props; - - const map = useMapEvents({ - zoomend() { - const latLngBounds = map.getBounds(); - map.closePopup(); - - const featureBounds = getFeatureObjectFromLatLngBounds(latLngBounds); - - onChange(featureBounds, map.getZoom()); - }, - moveend() { - const latLngBounds = map.getBounds(); - map.closePopup(); - - const featureBounds = getFeatureObjectFromLatLngBounds(latLngBounds); - - onChange(featureBounds, map.getZoom()); - } - }); - - return null; -}; diff --git a/app/src/components/map/components/DrawControls.tsx b/app/src/components/map/components/DrawControls.tsx deleted file mode 100644 index 334bb2a5b..000000000 --- a/app/src/components/map/components/DrawControls.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useLeafletContext } from '@react-leaflet/core'; -import { Feature } from 'geojson'; -import { useDeepCompareEffect } from 'hooks/useDeepCompareEffect'; -import * as L from 'leaflet'; -import 'leaflet-draw'; -import 'leaflet/dist/leaflet.css'; -import { useEffect, useRef } from 'react'; - -/* - * Supported draw events. - */ -const eventHandlers = { - onCreated: 'draw:created', - onEdited: 'draw:edited', - onDrawStart: 'draw:drawstart', - onDrawStop: 'draw:drawstop', - onDrawVertex: 'draw:drawvertex', - onEditStart: 'draw:editstart', - onEditMove: 'draw:editmove', - onEditResize: 'draw:editresize', - onEditVertex: 'draw:editvertex', - onEditStop: 'draw:editstop', - onDeleted: 'draw:deleted', - onDeleteStart: 'draw:deletestart', - onDeleteStop: 'draw:deletestop', - onMounted: 'draw:mounted' -}; - -/** - * Custom subset of `L.Control.DrawConstructorOptions` that omits `edit.Feature` as this will be added automatically - * by `DrawControls`. - * - * @export - * @interface IDrawControlsOptions - */ -export interface IDrawControlsOptions { - position?: L.Control.DrawConstructorOptions['position']; - draw?: L.Control.DrawConstructorOptions['draw']; - edit?: Omit; -} - -export type IDrawControlsOnChange = (features: Feature[]) => void; - -export interface IDrawControlsProps { - /** - * Initial features to add to the map. These features will be editable. - * - * @type {Feature[]} - * @memberof IDrawControlsProps - */ - initialFeatures?: Feature[]; - /** - * Options to control the draw/edit UI controls. - * - * @type {IDrawControlsOptions} - * @memberof IDrawControlsProps - */ - options?: IDrawControlsOptions; - /** - * Callback triggered anytime a feature is added or updated or removed. - * - * @type {IDrawControlsOnChange} - * @memberof IDrawControlsProps - */ - onChange?: IDrawControlsOnChange; - /** - * Clear any previously drawn features (layers) before drawing the next one. - * The result is that only 1 feature will be shown at a time. - * - * @type {boolean} - * @memberof IDrawControlsProps - */ - clearOnDraw?: boolean; -} - -const DrawControls: React.FC> = (props) => { - const context = useLeafletContext(); - - /** - * Fetch the layer used by the draw controls. - * - * @return {*} {L.FeatureGroup} - */ - const getFeatureGroup = () => { - const container = context.layerContainer; - - if (!container || !(container instanceof L.FeatureGroup)) { - throw new Error('Failed to get map layer'); - } - - return container; - }; - - /** - * Collects all current feature layers, and calls `props.onChange`. - * Adds `radius` to the properties if the source feature is a circle type. - */ - const handleFeatureUpdate = () => { - const container = getFeatureGroup(); - - const features: Feature[] = []; - - container.getLayers().forEach((layer: any) => { - const geoJSON = layer.toGeoJSON(); - - if (layer._mRadius) { - geoJSON.properties.radius = layer.getRadius(); - } - - features.push(geoJSON); - }); - - props.onChange?.([...features]); - }; - - /** - * Build and return a drawing map control. - * - * @return {*} {L.Control.Draw} - */ - const getDrawControls = (): L.Control.Draw => { - const options: L.Control.DrawConstructorOptions = { - edit: { - ...props.options?.edit, - // Add FeatureGroup automatically - featureGroup: getFeatureGroup() - } - }; - - options.draw = { ...props.options?.draw }; - - options.position = props.options?.position || 'topright'; - - return new L.Control.Draw(options); - }; - - /** - * Handle create events. - * - * @param {L.DrawEvents.Created} event - */ - const onDrawCreate = (event: L.DrawEvents.Created) => { - const container = getFeatureGroup(); - - if (props.clearOnDraw) { - // Clear previous layers - container.clearLayers(); - } - - container.addLayer(event.layer); - handleFeatureUpdate(); - }; - - /** - * Handle edit/delete events. - */ - const onDrawEditDelete = () => { - handleFeatureUpdate(); - }; - - /** - * Registers/draws features. - * - * @param {Feature[]} [features] - * @return {*} - */ - const drawFeatures = (features?: Feature[]) => { - if (!features) { - return; - } - - const container = getFeatureGroup(); - - container.clearLayers(); - - features?.forEach((item: Feature) => { - L.geoJSON(item, { - pointToLayer: (feature, latlng) => { - if (feature.properties?.radius) { - return new L.Circle([latlng.lat, latlng.lng], feature.properties.radius); - } - - return new L.Marker([latlng.lat, latlng.lng]); - }, - onEachFeature: function (_feature, layer) { - container.addLayer(layer); - } - }); - }); - }; - - useEffect(() => { - const { map } = context; - - // Remove any existing event handlers - map.removeEventListener(eventHandlers.onCreated); - map.removeEventListener(eventHandlers.onEdited); - map.removeEventListener(eventHandlers.onDeleted); - - // Register draw event handlers - map.on(eventHandlers.onCreated, onDrawCreate as L.LeafletEventHandlerFn); - map.on(eventHandlers.onEdited, onDrawEditDelete); - map.on(eventHandlers.onDeleted, onDrawEditDelete); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.options, props.onChange, props.clearOnDraw]); - - useDeepCompareEffect(() => { - drawFeatures(props.initialFeatures); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.initialFeatures]); - - const drawControlsRef = useRef(getDrawControls()); - - useDeepCompareEffect(() => { - const { map } = context; - // Update draw control - drawControlsRef.current.remove(); - drawControlsRef.current = getDrawControls(); - drawControlsRef.current.addTo(map); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.options]); - - return null; -}; - -export default DrawControls; diff --git a/app/src/components/map/components/EventHandler.tsx b/app/src/components/map/components/EventHandler.tsx deleted file mode 100644 index 97639797f..000000000 --- a/app/src/components/map/components/EventHandler.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { LeafletEventHandlerFnMap } from 'leaflet'; -import { useMapEvents } from 'react-leaflet'; - -export interface IEventHandlerProps { - eventHandlers?: LeafletEventHandlerFnMap; -} - -const EventHandler = (props: IEventHandlerProps) => { - useMapEvents({ - ...props.eventHandlers - }); - - return null; -}; - -export default EventHandler; diff --git a/app/src/components/map/components/FullScreenScrollingEventHandler.tsx b/app/src/components/map/components/FullScreenScrollingEventHandler.tsx deleted file mode 100644 index 146e8e9d3..000000000 --- a/app/src/components/map/components/FullScreenScrollingEventHandler.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { LatLngBoundsExpression } from 'leaflet'; -import { useMap } from 'react-leaflet'; - -export interface IFullScreenScrollingEventHandlerProps { - /** - * Whether or not scroll wheel zooming is enabled. - * - * Usage: - * - If `true`, scroll wheel zooming will always be enabled regardless of mode. - * - If `false`, scroll wheel zooming will only be enabled in full screen mode. - * - * @type {boolean} - * @memberof IFullScreenScrollingEventHandlerProps - */ - scrollWheelZoom?: boolean; - /** - * Bounds to set when exiting full screen mode. - * - * @type {LatLngBoundsExpression} - * @memberof IFullScreenScrollingEventHandlerProps - */ - bounds?: LatLngBoundsExpression; -} - -/** - * Special event handler that triggers when the map enters and exists full screen mode. - * - * @param {*} props - * @return {*} - */ -const FullScreenScrollingEventHandler: React.FC> = ( - props -) => { - const map = useMap(); - - if (props.scrollWheelZoom) { - map.scrollWheelZoom.enable(); - } else { - map.scrollWheelZoom.disable(); - } - - map.on('fullscreenchange', function () { - if (map.isFullscreen()) { - if (!props.scrollWheelZoom) { - // don't change scroll wheel zoom settings if it was enabled by default via props - map.scrollWheelZoom.enable(); - } - } else { - if (!props.scrollWheelZoom) { - // don't change scroll wheel zoom settings if it was enabled by default via props - map.scrollWheelZoom.disable(); - } - - if (props.bounds) { - // reset bounds, if provided, on exit fullscreen - map.fitBounds(props.bounds); - } - } - }); - - return null; -}; - -export default FullScreenScrollingEventHandler; diff --git a/app/src/components/map/components/MarkerCluster.tsx b/app/src/components/map/components/MarkerCluster.tsx deleted file mode 100644 index 4fb5dec9d..000000000 --- a/app/src/components/map/components/MarkerCluster.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import 'leaflet.markercluster'; -import { ReactElement } from 'react'; -import { FeatureGroup, Popup, Tooltip } from 'react-leaflet'; -import { IMarkerLayersProps, Marker } from './MarkerClusterControls'; - -// Compare with MarkerClusterControls.tsx -// See that is removed here -// This allows the marker data to render properly without the component visible on the map -const MarkerCluster: React.FC> = (props) => { - if (!props.layers?.length) { - return null; - } - - const layerControls: ReactElement[] = []; - - props.layers.forEach((layer, index) => { - if (!layer.markers?.length) { - return; - } - - layerControls.push( - - {layer.markers.map((item) => { - const id = item.key; - return ( - - {item.tooltip && ( - - {item.tooltip} - - )} - {item.popup && ( - - {item.popup} - - )} - - ); - })} - - ); - }); - - return <>{layerControls}; -}; - -export default MarkerCluster; diff --git a/app/src/components/map/components/MarkerClusterControls.tsx b/app/src/components/map/components/MarkerClusterControls.tsx deleted file mode 100644 index 9f9edfe16..000000000 --- a/app/src/components/map/components/MarkerClusterControls.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { createLayerComponent } from '@react-leaflet/core'; -import L, { LatLngExpression } from 'leaflet'; -import 'leaflet.markercluster'; -import { ReactElement } from 'react'; -import { FeatureGroup, LayersControl, MarkerProps, Popup, PopupProps, Tooltip, TooltipProps } from 'react-leaflet'; - -export interface IMarker { - position: LatLngExpression; - count: number; - key: string | number; - MarkerProps?: Partial; - popup?: ReactElement; - PopupProps?: Partial; - tooltip?: ReactElement; - TooltipProps?: Partial; -} - -export interface IMarkerLayer { - visible: boolean; - layerName: string; - markers: IMarker[]; -} - -export interface IMarkerLayersProps { - layers?: IMarkerLayer[]; -} - -export const makeCountIcon = (count: number) => { - return L.divIcon({ - html: `
${count}
`, - className: 'marker-cluster marker-cluster-small', - iconSize: new L.Point(24, 24) - }); -}; - -export const CountMarker: any = L.Marker.extend({ - options: { - count: 1 - }, - - setCount(s: number) { - this.options.count = s; - }, - - initialize(latlng: number[], { count, ...options }: { count: number }) { - L.Util.setOptions(this, { - count, - ...options - }); - - (L.CircleMarker.prototype as any).initialize.call(this, latlng, { - count, - ...options, - icon: makeCountIcon(count) - }); - } -}); - -export const Marker = createLayerComponent< - L.Marker & { setCount: (count: number) => void }, - MarkerProps & { count: number } ->( - ({ position, ...options }: MarkerProps & { count: number }, ctx) => { - const instance = new CountMarker(position, options); - return { - instance, - context: { ...ctx, overlayContainer: instance } - }; - }, - (marker, props, prevProps) => { - if (props.count !== prevProps.count) { - marker.setCount(props.count); - } - if (props.position !== prevProps.position) { - marker.setLatLng(props.position); - } - - if (props.icon != null && props.icon !== prevProps.icon) { - marker.setIcon(props.icon); - } - - if (props.zIndexOffset != null && props.zIndexOffset !== prevProps.zIndexOffset) { - marker.setZIndexOffset(props.zIndexOffset); - } - - if (props.opacity != null && props.opacity !== prevProps.opacity) { - marker.setOpacity(props.opacity); - } - - if (marker.dragging != null && props.draggable !== prevProps.draggable) { - if (props.draggable === true) { - marker.dragging.enable(); - } else { - marker.dragging.disable(); - } - } - } -); - -const MarkerClusterControls: React.FC> = (props) => { - if (!props.layers?.length) { - return null; - } - - const layerControls: ReactElement[] = []; - - props.layers.forEach((layer, index) => { - if (!layer.markers?.length) { - return; - } - - layerControls.push( - - - {layer.markers.map((item) => { - const id = item.key; - return ( - - {item.tooltip && ( - - {item.tooltip} - - )} - {item.popup && ( - - {item.popup} - - )} - - ); - })} - - - ); - }); - - return <>{layerControls}; -}; - -export default MarkerClusterControls; diff --git a/app/src/components/map/components/StaticLayers.tsx b/app/src/components/map/components/StaticLayers.tsx deleted file mode 100644 index 30c9bc783..000000000 --- a/app/src/components/map/components/StaticLayers.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as L from 'leaflet'; -import { ReactElement } from 'react'; -import { FeatureGroup, GeoJSON, Popup, Tooltip } from 'react-leaflet'; -import { IStaticLayersProps } from './StaticLayersControls'; - -// Compare with StaticLayersControl.tsx -// See that is removed here -// This allows the static layer data to render properly without the component visible on the map -const StaticLayers: React.FC> = (props) => { - if (!props.layers?.length) { - return null; - } - - const layerControls: ReactElement[] = []; - - props.layers.forEach((layer) => { - if (!layer.features?.length) { - return; - } - - layerControls.push( - - {layer.features.map((item, index) => { - const id = item.key || item.geoJSON.id || index; - - return ( - { - if (feature.properties?.radius) { - return new L.Circle([latlng.lat, latlng.lng], feature.properties.radius); - } - - return new L.Marker([latlng.lat, latlng.lng]); - }} - data={item.geoJSON} - {...item.GeoJSONProps}> - {item.tooltip && ( - - {item.tooltip} - - )} - {item.popup && ( - - {item.popup} - - )} - - ); - })} - - ); - }); - - return <>{layerControls}; -}; - -export default StaticLayers; diff --git a/app/src/components/map/components/StaticLayersControls.tsx b/app/src/components/map/components/StaticLayersControls.tsx deleted file mode 100644 index 883755ff0..000000000 --- a/app/src/components/map/components/StaticLayersControls.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Feature } from 'geojson'; -import * as L from 'leaflet'; -import { ReactElement } from 'react'; -import { - FeatureGroup, - GeoJSON, - GeoJSONProps, - LayersControl, - Popup, - PopupProps, - Tooltip, - TooltipProps -} from 'react-leaflet'; - -export interface IStaticLayerFeature { - geoJSON: Feature; - key?: string | number; - GeoJSONProps?: Partial; - popup?: ReactElement; - PopupProps?: Partial; - tooltip?: ReactElement; - TooltipProps?: Partial; -} - -export interface IStaticLayer { - visible: boolean; - layerName: string; - features: IStaticLayerFeature[]; -} - -export interface IStaticLayersProps { - layers?: IStaticLayer[]; -} - -const StaticLayersControls: React.FC> = (props) => { - if (!props.layers?.length) { - return null; - } - - const layerControls: ReactElement[] = []; - - props.layers.forEach((layer) => { - if (!layer.features?.length) { - return; - } - - layerControls.push( - - - {layer.features.map((item, index) => { - const id = item.key || item.geoJSON.id || index; - - return ( - { - if (feature.properties?.radius) { - return new L.Circle([latlng.lat, latlng.lng], feature.properties.radius); - } - - return new L.Marker([latlng.lat, latlng.lng]); - }} - data={item.geoJSON} - {...item.GeoJSONProps}> - {item.tooltip && ( - - {item.tooltip} - - )} - {item.popup && ( - - {item.popup} - - )} - - ); - })} - - - ); - }); - - return <>{layerControls}; -}; - -export default StaticLayersControls; diff --git a/app/src/components/map/components/UploadAreaControls.tsx b/app/src/components/map/components/UploadAreaControls.tsx deleted file mode 100644 index 4ab151f44..000000000 --- a/app/src/components/map/components/UploadAreaControls.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { mdiTrayArrowUp } from '@mdi/js'; -import Icon from '@mdi/react'; -import { Button } from '@mui/material'; -import EditDialog from 'components/dialog/EditDialog'; -import UploadArea, { - AreaUploadYupSchema, - FormikAreaUploadInitialValues, - IFormikAreaUpload -} from 'components/upload/UploadArea'; -import { IDatasetSearchForm } from 'features/datasets/components/DatasetSearchForm'; -import { useFormikContext } from 'formik'; -import React, { useState } from 'react'; - -const UploadAreaControls: React.FC> = (props) => { - const formikProps = useFormikContext(); - - const [openUploadArea, setOpenUploadArea] = useState(false); - - const submitArea = (values: IFormikAreaUpload) => { - formikProps.setFieldValue('area', [...formikProps.values.area, values]); - setOpenUploadArea(false); - }; - - return ( - <> - , - initialValues: FormikAreaUploadInitialValues, - validationSchema: AreaUploadYupSchema - }} - onCancel={() => setOpenUploadArea(false)} - onSave={submitArea} - /> - - - ); -}; - -export default UploadAreaControls; diff --git a/app/src/components/security/SecurityRuleForm.tsx b/app/src/components/security/SecurityRuleForm.tsx index 433a6ee33..1a9a2e2c3 100644 --- a/app/src/components/security/SecurityRuleForm.tsx +++ b/app/src/components/security/SecurityRuleForm.tsx @@ -97,7 +97,12 @@ const SecurityRuleForm = () => { return ruleGroups; }, []) ); - }, [initialAppliedSecurityRules, submissionFeatureGroupsDataLoader.data]); + }, [ + allSecurityRules, + formikProps.initialValues.submissionFeatureIds, + initialAppliedSecurityRules, + submissionFeatureGroupsDataLoader.data + ]); const toggleStageApply = (securityRule: ISecurityRuleAndCategory) => { if ( diff --git a/app/src/contexts/submissionContext.tsx b/app/src/contexts/submissionContext.tsx index 9b0587ef0..c7ae727b7 100644 --- a/app/src/contexts/submissionContext.tsx +++ b/app/src/contexts/submissionContext.tsx @@ -120,6 +120,7 @@ export const SubmissionContextProvider: React.FC = (pro */ useEffect(() => { submissionFeaturesAppliedRulesDataLoader.refresh(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [allSubmissionFeatureIds]); const submissionContext: ISubmissionContext = useMemo(() => { diff --git a/app/src/features/datasets/DatasetPage.test.tsx b/app/src/features/datasets/DatasetPage.test.tsx deleted file mode 100644 index 3ea9f61bc..000000000 --- a/app/src/features/datasets/DatasetPage.test.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { ThemeProvider } from '@mui/styles'; -import { cleanup, render, waitFor } from '@testing-library/react'; -import { FeatureCollection } from 'geojson'; -import { createMemoryHistory } from 'history'; -import { useApi } from 'hooks/useApi'; -import { Router } from 'react-router'; -import appTheme from 'themes/appTheme'; -import DatasetPage from './DatasetPage'; - -const history = createMemoryHistory(); - -const renderContainer = () => { - return render( - - - - - - ); -}; - -jest.mock('../../hooks/useApi'); - -const mockUseApi = { - dataset: { - getDatasetEML: jest.fn(), - getDatasetArtifacts: jest.fn(), - getHandleBarsTemplateByDatasetId: jest.fn() - }, - search: { - getSpatialData: jest.fn(), - getSpatialDataFile: jest.fn() - } -}; - -const mockBiohubApi = useApi as jest.Mock; - -describe('DatasetPage', () => { - beforeEach(() => { - mockBiohubApi.mockImplementation(() => mockUseApi); - }); - - afterEach(() => { - cleanup(); - }); - - it('renders the empty page content correctly', async () => { - mockUseApi.search.getSpatialDataFile.mockResolvedValue([]); - - mockUseApi.search.getSpatialData.mockResolvedValue([]); - - mockUseApi.dataset.getDatasetEML.mockResolvedValue([]); - - mockUseApi.dataset.getDatasetArtifacts.mockResolvedValue([]); - - mockUseApi.dataset.getHandleBarsTemplateByDatasetId.mockResolvedValue({ header: 'header', details: 'details' }); - - const { getByTestId } = renderContainer(); - - await waitFor(() => { - expect(getByTestId('MapContainer')).toBeVisible(); - }); - }); - - it.skip('shows eml metadata as well as map points', async () => { - const validFeatureCollection: FeatureCollection = { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [-129.083298513, 55.40257311] - }, - properties: { - type: 'Occurrence', - eventDate: 'string' - } - } - ] - }; - - const hexDoc = ''; - - mockUseApi.search.getSpatialDataFile.mockResolvedValue(hexDoc); - - mockUseApi.search.getSpatialData.mockReturnValue(validFeatureCollection); - - mockUseApi.dataset.getDatasetEML.mockResolvedValue({ - 'eml:eml': { - dataset: { - contact: { - organizationName: 'organization name' - } - } - } - }); - - mockUseApi.dataset.getHandleBarsTemplateByDatasetId.mockResolvedValue({ - header: 'header', - details: '{{eml:eml.dataset.contact.organizationName}}' - }); - - mockUseApi.dataset.getDatasetArtifacts.mockResolvedValue([]); - - const { getByTestId, getByText } = renderContainer(); - - await waitFor(() => { - expect(getByTestId('MapContainer')).toBeVisible(); - expect(getByText('organization name')).toBeVisible(); - }); - }); -}); diff --git a/app/src/features/datasets/DatasetPage.tsx b/app/src/features/datasets/DatasetPage.tsx index 0f360dc8a..5fc4eb842 100644 --- a/app/src/features/datasets/DatasetPage.tsx +++ b/app/src/features/datasets/DatasetPage.tsx @@ -1,195 +1,16 @@ -import { Theme } from '@mui/material'; import Box from '@mui/material/Box'; import Container from '@mui/material/Container'; import Paper from '@mui/material/Paper'; -import { makeStyles } from '@mui/styles'; -import { ActionToolbar } from 'components/toolbar/ActionToolbars'; - -const useStyles = makeStyles((theme: Theme) => ({ - datasetTitleContainer: { - paddingBottom: theme.spacing(5), - background: '#f7f8fa', - '& h1': { - marginTop: '-4px' - } - }, - datasetDetailsLabel: { - borderBottom: '1pt solid #dadada' - }, - datasetDetailsContainer: {}, - datasetMapContainer: { - minHeight: '400px' - } -})); const DatasetPage: React.FC = () => { - const classes = useStyles(); - // const biohubApi = useApi(); - // const urlParams = useParams(); - // const dialogContext = useDialogContext(); - // const history = useHistory(); - - // const datasetId = urlParams['id']; - - // const datasetDataLoader = useDataLoader(() => biohubApi.dataset.getDataset(datasetId)); - // const templateDataLoader = useDataLoader(() => biohubApi.dataset.getHandleBarsTemplateByDatasetId(datasetId)); - - // const fileDataLoader = useDataLoader((searchBoundary: Feature, searchType: string[], searchZoom: number) => - // biohubApi.search.getSpatialDataFile({ - // boundary: [searchBoundary], - // type: searchType, - // zoom: searchZoom, - // datasetID: datasetId - // }) - // ); - - // useDataLoaderError(datasetDataLoader, () => { - // return { - // dialogTitle: 'Error Loading Dataset', - // dialogText: - // 'An error has occurred while attempting to load the dataset, please try again. If the error persists, please contact your system administrator.', - // onOk: () => { - // datasetDataLoader.clear(); - // dialogContext.setErrorDialog({ open: false }); - // history.replace('/search'); - // }, - // onClose: () => { - // datasetDataLoader.clear(); - // dialogContext.setErrorDialog({ open: false }); - // history.replace('/search'); - // } - // }; - // }); - - // datasetDataLoader.load(); - // console.log('datasetDataLoader.data', datasetDataLoader.data); - // // templateDataLoader.load(); - - // const mapDataLoader = useDataLoader((searchBoundary: Feature, searchType: string[], searchZoom: number) => - // biohubApi.search.getSpatialData({ - // boundary: [searchBoundary], - // type: searchType, - // zoom: searchZoom, - // datasetID: datasetId - // }) - // ); - - // useDataLoaderError(fileDataLoader, () => { - // return { - // dialogTitle: 'Error Exporting Data', - // dialogText: - // 'An error has occurred while attempting to archive and download occurrence data, please try again. If the error persists, please contact your system administrator.' - // }; - // }); - - // useDataLoaderError(mapDataLoader, () => { - // return { - // dialogTitle: 'Error Loading Map Data', - // dialogText: - // 'An error has occurred while attempting to load the map data, please try again. If the error persists, please contact your system administrator.' - // }; - // }); - - // const [markerLayers, setMarkerLayers] = useState([]); - // const [staticLayers, setStaticLayers] = useState([]); - // const [mapBoundary, setMapBoundary] = useState(undefined); - - // useEffect(() => { - // if (!fileDataLoader.data) { - // return; - // } - - // const data = fileDataLoader.data; - - // const content = Buffer.from(data, 'hex'); - - // const blob = new Blob([content], { type: 'application/zip' }); - - // const link = document.createElement('a'); - - // link.download = `${datasetId}.zip`; - - // link.href = URL.createObjectURL(blob); - - // link.click(); - - // URL.revokeObjectURL(link.href); - // }, [datasetId, fileDataLoader.data]); - - // useEffect(() => { - // if (!mapDataLoader.data) { - // return; - // } - - // const result = parseSpatialDataByType(mapDataLoader.data); - // if (result.staticLayers[0]?.features[0]?.geoJSON) { - // const bounds = calculateUpdatedMapBounds([result.staticLayers[0].features[0].geoJSON]); - // if (bounds) { - // const newBounds = new LatLngBounds(bounds[0] as LatLngTuple, bounds[1] as LatLngTuple); - // setMapBoundary(newBounds); - // } - // } - // setStaticLayers(result.staticLayers); - // setMarkerLayers(result.markerLayers); - // }, [mapDataLoader.data]); - - // mapDataLoader.load( - // ALL_OF_BC_BOUNDARY, - // [SPATIAL_COMPONENT_TYPE.BOUNDARY, SPATIAL_COMPONENT_TYPE.OCCURRENCE], - // MAP_DEFAULT_ZOOM - // ); - - // if (!datasetDataLoader.data) { - // // || !templateDataLoader.data) { - // return ; - // } - return ( - - - - - {/* */} - - - - - - - - - - {/* */} - - - {/* */} - - - - - {/* */} - - - {/* */} - - - - + + + + TBD + + + ); }; diff --git a/app/src/features/datasets/DatasetsRouter.tsx b/app/src/features/datasets/DatasetsRouter.tsx index a7c016027..fd615a94e 100644 --- a/app/src/features/datasets/DatasetsRouter.tsx +++ b/app/src/features/datasets/DatasetsRouter.tsx @@ -13,10 +13,6 @@ const DatasetsRouter: React.FC = () => { - {/* - - */} - diff --git a/app/src/features/datasets/components/AttachmentItemMenuButton.tsx b/app/src/features/datasets/components/AttachmentItemMenuButton.tsx deleted file mode 100644 index 713bfd0b0..000000000 --- a/app/src/features/datasets/components/AttachmentItemMenuButton.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { mdiDotsVertical, mdiLockOutline, mdiLockPlus, mdiTrashCanOutline, mdiTrayArrowDown } from '@mdi/js'; -import Icon from '@mdi/react'; -import { MenuList } from '@mui/material'; -import Box from '@mui/material/Box'; -import IconButton from '@mui/material/IconButton'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import Menu from '@mui/material/Menu'; -import MenuItem from '@mui/material/MenuItem'; -import { IArtifact, SECURITY_APPLIED_STATUS } from 'interfaces/useDatasetApi.interface'; -import { useState } from 'react'; - -interface IAttachmentItemMenuButtonProps { - artifact: IArtifact; - onDownload: (artifact: IArtifact) => void; - onDelete: (artifactUUID: string[]) => void; - onRequestAccess: (artifact: IArtifact) => void; - onApplySecurity: (artifact: IArtifact) => void; - hasAdministrativePermissions: boolean; - isPendingReview: boolean; -} - -const AttachmentItemMenuButton = (props: IAttachmentItemMenuButtonProps) => { - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const canDownload = - props.hasAdministrativePermissions || - props.artifact.supplementaryData.persecutionAndHarmStatus === SECURITY_APPLIED_STATUS.UNSECURED; - - const handleClick = (event: any) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <> - - - - - - - - {canDownload ? ( - { - props.onDownload(props.artifact); - handleClose(); - }} - data-testid="attachment-action-menu-download"> - - - - Download Document - - ) : ( - { - props.onRequestAccess(props.artifact); - handleClose(); - }} - data-testid="attachment-action-menu-request-access"> - - - - Request Access - - )} - - {props.hasAdministrativePermissions && ( - <> - { - props.onApplySecurity(props.artifact); - setAnchorEl(null); - }} - data-testid="attachment-action-menu-download"> - - - - Apply Security to Document - - - { - props.onDelete([props.artifact.uuid]); - handleClose(); - }} - data-testid="attachment-action-menu-delete"> - - - - Delete Document - - - )} - - - - - - ); -}; - -export default AttachmentItemMenuButton; diff --git a/app/src/features/datasets/components/DatasetArtifacts.tsx b/app/src/features/datasets/components/DatasetArtifacts.tsx deleted file mode 100644 index 1d9e0ea22..000000000 --- a/app/src/features/datasets/components/DatasetArtifacts.tsx +++ /dev/null @@ -1,326 +0,0 @@ -import { mdiLockPlus, mdiTrayArrowDown } from '@mdi/js'; -import Icon from '@mdi/react'; -import Alert from '@mui/material/Alert'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import IconButton from '@mui/material/IconButton'; -import Typography from '@mui/material/Typography'; -import { DataGrid, GridColDef, GridRowSelectionModel } from '@mui/x-data-grid'; -import { ErrorDialog } from 'components/dialog/ErrorDialog'; -import YesNoDialog from 'components/dialog/YesNoDialog'; -import { ActionToolbar } from 'components/toolbar/ActionToolbars'; -import { DATE_FORMAT } from 'constants/dateTimeFormats'; -import { SYSTEM_ROLE } from 'constants/roles'; -import { ISnackbarProps } from 'contexts/dialogContext'; -import { useApi } from 'hooks/useApi'; -import { useAuthStateContext } from 'hooks/useAuthStateContext'; -import { useDialogContext } from 'hooks/useContext'; -import useDataLoader from 'hooks/useDataLoader'; -import { IArtifact, SECURITY_APPLIED_STATUS } from 'interfaces/useDatasetApi.interface'; -import { useState } from 'react'; -import { hasAtLeastOneValidValue } from 'utils/authUtils'; -import { downloadFile, getFormattedDate, getFormattedFileSize, pluralize as p } from 'utils/Utils'; -import SecureDataAccessRequestDialog from '../security/SecureDataAccessRequestDialog'; -import AttachmentItemMenuButton from './AttachmentItemMenuButton'; -import ApplySecurityDialog from './security/ApplySecurityDialog'; - -const VALID_SYSTEM_ROLES: SYSTEM_ROLE[] = [SYSTEM_ROLE.DATA_ADMINISTRATOR, SYSTEM_ROLE.SYSTEM_ADMIN]; - -export interface IDatasetAttachmentsProps { - datasetId: string; -} - -const NoArtifactRowsOverlay = () => ( - - - No Artifacts - - -); - -/** - * Dataset attachments content for a dataset. - * - * @return {*} - */ -const DatasetAttachments: React.FC = (props) => { - const { datasetId } = props; - const [showAlert, setShowAlert] = useState(true); - const [initialSecureDataAccessRequestSelection, setInitialSecureDataAccessRequestSelection] = useState( - null - ); - - const [rowSelectionModel, setRowSelectionModel] = useState([]); - - const [openApplySecurity, setOpenApplySecurity] = useState(false); - const [selectedArtifacts, setSelectedArtifacts] = useState([]); - const [showDeleteFileDialog, setShowDeleteFileDialog] = useState(false); - const [artifactToDelete, setArtifactToDelete] = useState(undefined); - const [showErrorDialog, setShowErrorDialog] = useState(false); - - const authStateContext = useAuthStateContext(); - const biohubApi = useApi(); - const dialogContext = useDialogContext(); - - const artifactsDataLoader = useDataLoader(() => biohubApi.dataset.getDatasetArtifacts(datasetId)); - artifactsDataLoader.load(); - - const artifactsList = artifactsDataLoader.data || []; - - const numPendingDocuments = artifactsList.filter( - (artifact) => artifact.supplementaryData.persecutionAndHarmStatus === SECURITY_APPLIED_STATUS.PENDING - ).length; - - const hasAdministrativePermissions = hasAtLeastOneValidValue( - VALID_SYSTEM_ROLES, - authStateContext.biohubUserWrapper.roleNames - ); - - const handleApplySecurity = (artifact: IArtifact) => { - setSelectedArtifacts([artifact]); - setOpenApplySecurity(true); - }; - - const handleDownloadAttachment = async (attachment: IArtifact) => { - const signedUrl = await biohubApi.dataset.getArtifactSignedUrl(attachment.artifact_id); - if (!signedUrl) { - return; - } - - await downloadFile(signedUrl); - }; - - const handleDeleteArtifact = async (artifactUUIDs: string[]): Promise => { - const data = await biohubApi.artifact.deleteArtifacts(artifactUUIDs); - artifactsDataLoader.refresh(); - - return data; - }; - - const showSnackBar = (textDialogProps?: Partial) => { - dialogContext.setSnackbar({ ...textDialogProps, open: true }); - }; - - const handleShowSnackBar = (message: string) => { - showSnackBar({ - snackbarMessage: ( - <> - - {message} - - - ), - open: true - }); - }; - - const columns: GridColDef[] = [ - { - field: 'file_name', - headerName: 'Title', - flex: 2, - disableColumnMenu: true - }, - { - field: 'file_type', - headerName: 'Type', - flex: 1 - }, - { - field: 'create_date', - headerName: 'Submitted', - valueGetter: ({ value }) => value && new Date(value), - valueFormatter: ({ value }) => getFormattedDate(DATE_FORMAT.ShortDateFormat, value), - flex: 1 - }, - { - field: 'file_size', - headerName: 'Size', - flex: 0, - valueFormatter: ({ value }) => getFormattedFileSize(value) - }, - { - field: 'status', - headerName: 'Status', - flex: 1, - renderCell: (params) => { - const { supplementaryData } = params.row; - if (supplementaryData.persecutionAndHarmStatus === SECURITY_APPLIED_STATUS.UNSECURED) { - return ; - } - - if (hasAdministrativePermissions) { - if (supplementaryData.persecutionAndHarmStatus === SECURITY_APPLIED_STATUS.PENDING) { - return ; - } - } - return ; - } - }, - { - field: 'action', - headerName: 'Action', - sortable: false, - renderCell: (params) => { - return ( - { - setArtifactToDelete(params.row); - setShowDeleteFileDialog(true); - }} - onRequestAccess={(artifact) => setInitialSecureDataAccessRequestSelection(artifact.artifact_id)} - onApplySecurity={handleApplySecurity} - isPendingReview={!params.row.security_review_timestamp} - hasAdministrativePermissions={hasAdministrativePermissions} - /> - ); - } - } - ]; - - return ( - <> - setInitialSecureDataAccessRequestSelection(null)} - artifacts={artifactsList} - initialArtifactSelection={ - initialSecureDataAccessRequestSelection ? [initialSecureDataAccessRequestSelection] : [] - } - /> - - { - setOpenApplySecurity(false); - artifactsDataLoader.refresh(); - - setSelectedArtifacts([]); - setRowSelectionModel([]); - }} - /> - - setShowErrorDialog(false)} - onOk={() => setShowErrorDialog(false)} - /> - - setShowDeleteFileDialog(false)} - onYes={async () => { - if (artifactToDelete) { - const response = await handleDeleteArtifact([artifactToDelete.uuid]); - if (response) { - handleShowSnackBar(`You successfully deleted ${artifactToDelete.file_name}.`); - } else { - setShowErrorDialog(true); - } - setShowDeleteFileDialog(false); - setArtifactToDelete(undefined); - } - }} - onNo={() => { - setShowDeleteFileDialog(false); - setArtifactToDelete(undefined); - }} - dialogTitle="Delete document?" - dialogText="Are you sure you want to permanently delete this document? This action cannot be undone." - yesButtonProps={{ color: 'error' }} - /> - - - - - - - - - - - - {hasAdministrativePermissions && numPendingDocuments > 0 && showAlert && ( - - setShowAlert(false)} severity="info"> - - {`You have ${numPendingDocuments} project ${p(numPendingDocuments, 'document')} to review.`} - - - - )} - - row.artifact_id} - autoHeight - rows={artifactsList} - columns={columns} - pageSizeOptions={[5]} - checkboxSelection - disableRowSelectionOnClick - disableColumnSelector - disableColumnFilter - disableColumnMenu - sortingOrder={['asc', 'desc']} - initialState={{ - sorting: { - sortModel: [{ field: 'create_date', sort: 'desc' }] - }, - pagination: { - paginationModel: { - pageSize: 5 - } - } - }} - slots={{ - noRowsOverlay: NoArtifactRowsOverlay - }} - rowSelectionModel={rowSelectionModel} - onRowSelectionModelChange={(newRowSelectionModel) => { - const selectedArtifacts = newRowSelectionModel.map((rowId) => { - const findArtifact = artifactsList.find((artifact) => artifact.artifact_id === rowId); - if (findArtifact === undefined) { - throw Error('Artifact not found'); - } else { - return findArtifact; - } - }); - - if (selectedArtifacts) { - setSelectedArtifacts(selectedArtifacts); - } - - setRowSelectionModel(newRowSelectionModel); - }} - /> - - - - ); -}; - -export default DatasetAttachments; diff --git a/app/src/features/datasets/components/DatasetSearchForm.tsx b/app/src/features/datasets/components/DatasetSearchForm.tsx deleted file mode 100644 index e5afbd692..000000000 --- a/app/src/features/datasets/components/DatasetSearchForm.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { InputLabel } from '@mui/material'; -import Box from '@mui/material/Box'; -import FormControl from '@mui/material/FormControl'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import Typography from '@mui/material/Typography'; -import MultiAutocompleteField, { - IMultiAutocompleteFieldOption, - sortAutocompleteOptions -} from 'components/fields/MultiAutocompleteField'; -import MultiSelectList from 'components/fields/MultiSelectList'; -import UploadAreaControls from 'components/map/components/UploadAreaControls'; -import { IFormikAreaUpload } from 'components/upload/UploadArea'; -import { useFormikContext } from 'formik'; -import { useApi } from 'hooks/useApi'; -import React, { useEffect, useState } from 'react'; -import yup from 'utils/YupSchema'; - -export interface IDatasetSearchForm { - dataset: string; - species_list: IMultiAutocompleteFieldOption[]; - area: IFormikAreaUpload[]; -} - -export const DatasetSearchFormInitialValues: IDatasetSearchForm = { - dataset: 'Occurrence', - species_list: [], - area: [] -}; - -export const DatasetSearchFormYupSchema = yup.object().shape({ - dataset: yup.string(), - species_list: yup.mixed(), - area: yup.mixed() -}); - -export interface IDatasetSearchFormProps { - onAreaUpdate: (area: IFormikAreaUpload[]) => void; - toggleShowForm: () => void; - hasResults: boolean; -} - -/** - * Create project - Partnerships section - * - * @return {*} - */ -const DatasetSearchForm: React.FC = (props) => { - const api = useApi(); - const formikProps = useFormikContext(); - const [speciesList, setSpeciesList] = useState([]); - - const convertOptions = (value: any): IMultiAutocompleteFieldOption[] => - value.map((item: any) => { - return { value: item.code, label: item.label }; - }); - - const handleGetSpeciesList = async (value: string) => { - const response = await api.taxonomy.searchSpecies(value); - - const convertedOptions = convertOptions(response.searchResponse); - - setSpeciesList(convertedOptions); - }; - - useEffect(() => { - props.onAreaUpdate(formikProps.values.area); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [formikProps.values.area]); - - return ( - <> - - - What do you want to find? - - - Dataset - - - - - - - - - - - Refine Search Area - - - - Refine your search to a custom boundary by importing either KML files or Shapefiles. - - - - - - - ); -}; - -export default DatasetSearchForm; diff --git a/app/src/features/datasets/components/RelatedDatasets.tsx b/app/src/features/datasets/components/RelatedDatasets.tsx deleted file mode 100644 index 77097ebca..000000000 --- a/app/src/features/datasets/components/RelatedDatasets.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { Link, Typography } from '@mui/material'; -import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; -import Divider from '@mui/material/Divider'; -import { DataGrid, GridColDef, GridRenderCellParams, GridTreeNodeWithRender } from '@mui/x-data-grid'; -import { ActionToolbar } from 'components/toolbar/ActionToolbars'; -import { SYSTEM_ROLE } from 'constants/roles'; -import { useApi } from 'hooks/useApi'; -import { useAuthStateContext } from 'hooks/useAuthStateContext'; -import useDataLoader from 'hooks/useDataLoader'; -import { IRelatedDataset } from 'interfaces/useDatasetApi.interface'; -import { hasAtLeastOneValidValue } from 'utils/authUtils'; -import { ensureProtocol } from 'utils/Utils'; - -const VALID_SYSTEM_ROLES: SYSTEM_ROLE[] = [SYSTEM_ROLE.DATA_ADMINISTRATOR, SYSTEM_ROLE.SYSTEM_ADMIN]; - -export interface IRelatedDatasetsProps { - datasetId: string; -} - -const NoDatasetRowsOverlay = () => ( - - - No Related Datasets - - -); - -/** - * Dataset attachments content for a dataset. - * - * @return {*} - */ -const RelatedDatasets: React.FC = (props) => { - const { datasetId } = props; - - const biohubApi = useApi(); - const authStateContext = useAuthStateContext(); - - const relatedDatasetsDataLoader = useDataLoader(() => biohubApi.dataset.getRelatedDatasets(datasetId)); - relatedDatasetsDataLoader.load(); - - const relatedDatasetsList: IRelatedDataset[] = relatedDatasetsDataLoader.data?.datasetsWithSupplementaryData ?? []; - - const hasAdministrativePermissions = hasAtLeastOneValidValue( - VALID_SYSTEM_ROLES, - authStateContext.biohubUserWrapper.roleNames - ); - - const columns: GridColDef[] = [ - { - field: 'title', - headerName: 'Title', - flex: 1, - disableColumnMenu: true, - renderCell: (params: GridRenderCellParams) => { - return {params.row.title}; - } - }, - { - field: 'status', - headerName: 'Status', - flex: 1, - renderCell: (params) => { - const { supplementaryData } = params.row; - - if (hasAdministrativePermissions) { - if (supplementaryData.isPendingReview === true) { - return ; - } - } - return <>; - } - } - ]; - - return ( - <> - - - - - row.datasetId} - autoHeight - disableVirtualization - rows={relatedDatasetsList} - slots={{ - noRowsOverlay: NoDatasetRowsOverlay - }} - columns={columns} - pageSizeOptions={[5]} - disableRowSelectionOnClick - disableColumnSelector - disableColumnFilter - disableColumnMenu - sortingOrder={['asc', 'desc']} - initialState={{ - sorting: { - sortModel: [{ field: 'create_date', sort: 'desc' }] - }, - pagination: { - paginationModel: { - pageSize: 5 - } - } - }} - /> - - - - ); -}; - -export default RelatedDatasets; diff --git a/app/src/features/datasets/components/RenderWithHandlebars.test.tsx b/app/src/features/datasets/components/RenderWithHandlebars.test.tsx deleted file mode 100644 index 417407e62..000000000 --- a/app/src/features/datasets/components/RenderWithHandlebars.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { cleanup, render } from 'test-helpers/test-utils'; -import useHandlebars from 'utils/handlebarsUtils'; -import RenderWithHandlebars from './RenderWithHandlebars'; - -jest.mock('utils/handlebarsUtils'); - -const mockHandlebars = useHandlebars as jest.Mock; - -const HandlebarsComponent = () => { - const content = { - datasetEML: { - data: { - 'eml:eml': {} - } - }, - rawTemplate: {} - }; - - return ; -}; - -const mockHandlebarsImplementation = { - compileFromRawTemplate: (dataset: any) => { - return (dataset: any) => { - return '
ABCD
'; - }; - } -}; - -describe('RenderWithHandlebars', () => { - beforeEach(() => { - mockHandlebars.mockImplementation(() => mockHandlebarsImplementation); - }); - - afterEach(() => { - cleanup(); - }); - - it('renders a component', () => { - const { getByText } = render(); - - expect(getByText('ABCD')).toBeVisible(); - }); -}); diff --git a/app/src/features/datasets/components/RenderWithHandlebars.tsx b/app/src/features/datasets/components/RenderWithHandlebars.tsx deleted file mode 100644 index 460152df8..000000000 --- a/app/src/features/datasets/components/RenderWithHandlebars.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import * as DOMPurify from 'dompurify'; -import 'styles/handlebar.scss'; -import { useHandlebars } from 'utils/handlebarsUtils'; - -export interface IRenderWithHandlebarsProps { - datasetEML: { - data: { - 'eml:eml': any; - }; - }; - rawTemplate: TemplateSpecification; -} - -const RenderWithHandlebars: React.FC = (props) => { - const dataset = props.datasetEML.data; - const rawTemplate = props.rawTemplate; - - const template = useHandlebars().compileFromRawTemplate(rawTemplate); - - const result = template(dataset); - - return
; -}; - -export default RenderWithHandlebars; diff --git a/app/src/features/datasets/components/security/ApplySecurityDialog.tsx b/app/src/features/datasets/components/security/ApplySecurityDialog.tsx deleted file mode 100644 index 42ea0a47d..000000000 --- a/app/src/features/datasets/components/security/ApplySecurityDialog.tsx +++ /dev/null @@ -1,292 +0,0 @@ -import { mdiInformationOutline, mdiLock } from '@mdi/js'; -import Icon from '@mdi/react'; -import LoadingButton from '@mui/lab/LoadingButton'; -import { Box, Divider, Typography } from '@mui/material'; -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; -import DialogTitle from '@mui/material/DialogTitle'; -import { useTheme } from '@mui/material/styles'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import YesNoDialog from 'components/dialog/YesNoDialog'; -import { ApplySecurityRulesI18N } from 'constants/i18n'; -import { ISnackbarProps } from 'contexts/dialogContext'; -import { Formik, FormikProps } from 'formik'; -import { useApi } from 'hooks/useApi'; -import { useDialogContext } from 'hooks/useContext'; -import useDataLoader from 'hooks/useDataLoader'; -import { IArtifact, IPersecutionAndHarmRule } from 'interfaces/useDatasetApi.interface'; -import React, { useRef, useState } from 'react'; -import { pluralize as p } from 'utils/Utils'; -import yup from 'utils/YupSchema'; -import { ISecurityReason } from './SecurityReasonCategory'; -import SecurityReasonSelector from './SecurityReasonSelector'; -import SelectedDocumentsDataset from './SelectedDocumentsDataset'; - -export interface IApplySecurityDialog { - selectedArtifacts: IArtifact[]; - open: boolean; - onClose: () => void; -} - -export interface ISelectSecurityReasonForm { - securityReasons: ISecurityReason[]; -} - -export const SecurityReasonsYupSchema = yup.object().shape({ - securityReasons: yup.array().of( - yup.object().shape({ - name: yup.string(), - description: yup.string(), - category: yup.string(), - id: yup.number(), - type_id: yup.number(), - wldtaxonomic_units_id: yup.number() - }) - ) -}); - -/** - * Publish button. - * - * @return {*} - */ -const ApplySecurityDialog: React.FC = (props) => { - const { selectedArtifacts, open, onClose } = props; - - const [yesNoDialogOpen, setYesNoDialogOpen] = useState(false); - const [isPendingApplySecurity, setIsPendingApplySecurity] = useState(false); - const [isPendingUnapplySecurity, setIsPendingUnapplySecurity] = useState(false); - const [formikRef] = useState(useRef>(null)); - - const biohubApi = useApi(); - const theme = useTheme(); - const fullScreen = useMediaQuery(theme.breakpoints.down('xl')); - const dialogContext = useDialogContext(); - - const persecutionHarmDataLoader = useDataLoader(() => biohubApi.security.listPersecutionHarmRules()); - persecutionHarmDataLoader.load(); - - const persecutionHarmRules: ISecurityReason[] = (persecutionHarmDataLoader.data ?? []).map((rule) => { - return { - category: 'Persecution or Harm', - name: rule.name, - description: rule.description, - id: rule.persecution_or_harm_id, - type_id: rule.persecution_or_harm_type_id, - wldtaxonomic_units_id: rule.wldtaxonomic_units_id - }; - }); - - const initialSecurityReasons: ISecurityReason[] = props.selectedArtifacts - // Find IDs of all applied security reasons - .reduce((securityRuleIds: number[], artifact: IArtifact) => { - const appliedSecurityReasons = artifact.supplementaryData.persecutionAndHarmRules.map( - (rule: IPersecutionAndHarmRule) => rule.persecution_or_harm_id - ); - - return [...securityRuleIds, ...appliedSecurityReasons]; - }, []) - - // Filter duplicate IDs - .filter((value, index, array) => array.indexOf(value) === index) - - // Map IDs to security rules - .map((securityRuleId: number) => { - return persecutionHarmRules.find((persecutionAndHarmRule) => persecutionAndHarmRule.id === securityRuleId); - }) - - // Filter missing security rules - .filter((value): value is ISecurityReason => Boolean(value)); - - const showSnackBar = (textDialogProps?: Partial) => { - dialogContext.setSnackbar({ ...textDialogProps, open: true }); - }; - - const handleSubmit = async (values: { securityReasons: ISecurityReason[] }) => { - if (values.securityReasons.length > 0) { - setIsPendingApplySecurity(true); - } else { - setIsPendingUnapplySecurity(true); - } - - return biohubApi.security.applySecurityReasonsToArtifacts(selectedArtifacts, values.securityReasons).finally(() => { - setIsPendingApplySecurity(false); - setIsPendingUnapplySecurity(false); - }); - }; - - const handleShowSnackBar = (message: string) => { - showSnackBar({ - snackbarMessage: ( - <> - - {message} - - - ), - open: true - }); - }; - - return ( - <> - - - innerRef={formikRef} - initialValues={{ - securityReasons: initialSecurityReasons - }} - validationSchema={SecurityReasonsYupSchema} - validateOnBlur={true} - validateOnChange={false} - onSubmit={async (values) => { - return handleSubmit(values) - .then(() => { - handleShowSnackBar( - `You successfully applied security reasons to ${selectedArtifacts.length} ${p( - selectedArtifacts.length, - 'file' - )}.` - ); - onClose(); - }) - .catch((apiError) => { - dialogContext.setErrorDialog({ - open: true, - dialogTitle: ApplySecurityRulesI18N.applySecurityRulesErrorTitle, - dialogText: ApplySecurityRulesI18N.applySecurityRulesErrorText, - dialogError: apiError.message, - dialogErrorDetails: apiError.errors, - onClose: () => { - dialogContext.setErrorDialog({ open: false }); - }, - onOk: () => { - dialogContext.setErrorDialog({ open: false }); - } - }); - }); - }}> - {(formikProps) => ( - <> - setYesNoDialogOpen(false)} - onYes={() => { - handleSubmit({ securityReasons: [] }) - .then(() => { - handleShowSnackBar( - `You successfully unsecured ${selectedArtifacts.length} ${p(selectedArtifacts.length, 'file')}.` - ); - onClose(); - }) - .catch((apiError) => { - dialogContext.setErrorDialog({ - open: true, - dialogTitle: ApplySecurityRulesI18N.unapplySecurityRulesErrorTitle, - dialogText: ApplySecurityRulesI18N.unapplySecurityRulesErrorText, - dialogError: apiError.message, - dialogErrorDetails: apiError.errors, - onClose: () => { - dialogContext.setErrorDialog({ open: false }); - }, - onOk: () => { - dialogContext.setErrorDialog({ open: false }); - } - }); - }) - .finally(() => { - setYesNoDialogOpen(false); - }); - }} - yesButtonProps={{ loading: isPendingUnapplySecurity }} - onNo={() => setYesNoDialogOpen(false)} - dialogTitle="" - dialogText="" - dialogContent={ - - - - - - Warning - - - You are going to make this document available to the public. This document can be downloaded. - Also, if there are any security reasons, they will be removed. - - - Are you sure you would like to proceed? - - - } - /> - - Apply Security Reasons - - - {`Search for the security reasons and apply them to the selected ${p( - selectedArtifacts.length, - 'document' - )}`} - - - - - - - - - } - onClick={formikProps.submitForm}> - Apply Security - - - - - - - )} - - - - ); -}; - -export default ApplySecurityDialog; diff --git a/app/src/features/datasets/components/security/SecurityReasonCategory.tsx b/app/src/features/datasets/components/security/SecurityReasonCategory.tsx deleted file mode 100644 index 67e8c7969..000000000 --- a/app/src/features/datasets/components/security/SecurityReasonCategory.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { mdiArrowDown, mdiArrowUp, mdiMinus, mdiPlus } from '@mdi/js'; -import Icon from '@mdi/react'; -import { Collapse, Divider, IconButton, Paper, Typography } from '@mui/material'; -import Box from '@mui/material/Box'; -import { FieldArray } from 'formik'; -import { useState } from 'react'; - -export interface ISecurityReason { - name: string; - id: number; - type_id: number; - wldtaxonomic_units_id: number; - description: string | null; - category: string; -} - -export interface ISecurityReasonProps { - securityReason: ISecurityReason; - onClick: (securityReason: ISecurityReason) => void; - isSelected: boolean; -} - -export interface ISecurityReasonCategoryProps { - categoryName: string; - securityReasons: ISecurityReason[]; -} -/** - * Security Reason Selector for security application. - * - * @return {*} - */ -const SecurityReasonCategory = (props: ISecurityReasonCategoryProps) => { - const { categoryName, securityReasons } = props; - - const [open, setOpen] = useState(true); - - return ( - - - - {categoryName} - - - setOpen(!open)} color="primary" aria-label="dropdown arrow" component="label"> - - - - - - - ( - <> - {securityReasons - .sort((a, b) => a.name.localeCompare(b.name)) - .map((securityReason) => { - return ( - - { - if ( - !arrayHelpers.form.values.securityReasons.find( - (sr: ISecurityReason) => sr.name === securityReason.name - ) - ) { - arrayHelpers.push({ ...securityReason, category: categoryName }); - } - }} - isSelected={false} - /> - - ); - })} - - )} - /> - - - - ); -}; - -export const SecurityReason = (props: ISecurityReasonProps) => { - const { securityReason, onClick, isSelected } = props; - - return ( - - - - {securityReason.name} - {securityReason.description} - - {securityReason.category} - - - - onClick(securityReason)} color="primary" aria-label="add security"> - - - - - - ); -}; - -export default SecurityReasonCategory; diff --git a/app/src/features/datasets/components/security/SecurityReasonSelector.tsx b/app/src/features/datasets/components/security/SecurityReasonSelector.tsx deleted file mode 100644 index 00bcf2f31..000000000 --- a/app/src/features/datasets/components/security/SecurityReasonSelector.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { Button, Paper } from '@mui/material'; -import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; -import { ActionToolbar } from 'components/toolbar/ActionToolbars'; -import { FieldArray, useFormikContext } from 'formik'; -import { ISelectSecurityReasonForm } from './ApplySecurityDialog'; -import SecurityReasonCategory, { ISecurityReason, SecurityReason } from './SecurityReasonCategory'; - -/** - * Security Reason Selector for security application. - * - * @return {*} - */ -const SecurityReasonSelector = (props: ISelectSecurityReasonForm) => { - const { securityReasons } = props; - const { values, setFieldValue } = useFormikContext(); - - return ( - - - - - - { - return !values.securityReasons.some((reason) => reason.name === value.name); - })} - /> - - - - - - - - - - - {values.securityReasons.length > 1 && ( - - )} - - - - ( - <> - {values.securityReasons.map((securityReason, index) => { - return ( - - arrayHelpers.remove(index)} - isSelected={true} - /> - - ); - })} - - )} - /> - - - - ); -}; - -export default SecurityReasonSelector; diff --git a/app/src/features/datasets/components/security/SelectedDocumentsDataset.tsx b/app/src/features/datasets/components/security/SelectedDocumentsDataset.tsx deleted file mode 100644 index 9a4a811d3..000000000 --- a/app/src/features/datasets/components/security/SelectedDocumentsDataset.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Paper } from '@mui/material'; -import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; -import { DataGrid, GridColDef } from '@mui/x-data-grid'; -import { ActionToolbar } from 'components/toolbar/ActionToolbars'; -import { IArtifact } from 'interfaces/useDatasetApi.interface'; - -export interface IRelatedDatasetsProps { - selectedArtifacts: IArtifact[]; -} - -/** - * Selected Documents Dataset for security application. - * - * @return {*} - */ -const SelectedDocumentsDataset: React.FC = (props) => { - const { selectedArtifacts } = props; - - const columns: GridColDef[] = [ - { - field: 'file_type', - headerName: 'Type', - flex: 0.5 - }, - { - field: 'file_name', - headerName: 'Title', - flex: 2, - disableColumnMenu: true - } - ]; - - return ( - - - - - row.artifact_id} - autoHeight - rows={selectedArtifacts} - columns={columns} - disableVirtualization - disableRowSelectionOnClick - disableColumnSelector - disableColumnFilter - disableColumnMenu - disableDensitySelector - hideFooter - /> - - - ); -}; - -export default SelectedDocumentsDataset; diff --git a/app/src/features/home/HomePage.tsx b/app/src/features/home/HomePage.tsx deleted file mode 100644 index fd6773e81..000000000 --- a/app/src/features/home/HomePage.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Container, Paper, Typography } from '@mui/material'; -import Box from '@mui/material/Box'; -import SearchComponent from 'features/search/SearchComponent'; -import { Formik, FormikProps } from 'formik'; -import { IAdvancedSearch } from 'interfaces/useSearchApi.interface'; -import { useRef } from 'react'; -import { useHistory } from 'react-router'; - -const HomePage = () => { - const formikRef = useRef>(null); - const history = useHistory(); - - const handleSubmit = () => { - const query = formikRef.current?.values?.keywords || ''; - - if (!query) { - return; - } - - history.push(`/search?keywords=${query}`); - }; - - return ( - - - - Biodiversity Hub B.C. - - - Open access to British Columbia's terrestrial, aquatic species and habitat inventory data - - - - - - - - - ); -}; - -export default HomePage; diff --git a/app/src/features/home/HomeRouter.tsx b/app/src/features/home/HomeRouter.tsx deleted file mode 100644 index baaadc629..000000000 --- a/app/src/features/home/HomeRouter.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Switch } from 'react-router'; -import RouteWithTitle from 'utils/RouteWithTitle'; -import { getTitle } from 'utils/Utils'; -import HomePage from './HomePage'; - -/** - * Router for all `/*` pages. - * - * @return {*} - */ -const HomeRouter: React.FC = () => { - return ( - - - - - - ); -}; - -export default HomeRouter; diff --git a/app/src/features/map/MapPage.test.tsx b/app/src/features/map/MapPage.test.tsx deleted file mode 100644 index eb198a3fa..000000000 --- a/app/src/features/map/MapPage.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { FeatureCollection } from 'geojson'; -import { createMemoryHistory } from 'history'; -import { useApi } from 'hooks/useApi'; -import { Router } from 'react-router'; -import { cleanup, render, waitFor } from 'test-helpers/test-utils'; -import MapPage from './MapPage'; - -jest.mock('../../hooks/useApi'); - -const mockBiohubApi = useApi as jest.Mock; - -const mockUseApi = { - search: { - getSpatialData: jest.fn() - } -}; - -const history = createMemoryHistory(); - -const renderContainer = () => { - return render( - - - - ); -}; - -jest.mock('./SideSearchBar', () => () =>
); - -describe('MapPage', () => { - beforeEach(() => { - mockBiohubApi.mockImplementation(() => mockUseApi); - }); - - afterEach(() => { - cleanup(); - }); - - it('shows `Map` and map container when there are no occurrences', async () => { - mockUseApi.search.getSpatialData.mockReturnValue([]); - - const { getByTestId } = renderContainer(); - - await waitFor(() => { - expect(getByTestId('MapContainer')).toBeVisible(); - }); - }); - - it('shows `Map` and map container with data points when there is occurrences', async () => { - const vaildFeatureCollection: FeatureCollection = { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [-129.083298513, 55.40257311] - }, - properties: { - type: 'Occurrence', - eventDate: 'string' - } - } - ] - }; - - mockUseApi.search.getSpatialData.mockReturnValue(vaildFeatureCollection); - - const { getByTestId } = renderContainer(); - - await waitFor(() => { - expect(getByTestId('MapContainer')).toBeVisible(); - }); - }); -}); diff --git a/app/src/features/map/MapPage.tsx b/app/src/features/map/MapPage.tsx deleted file mode 100644 index 1651fdb8c..000000000 --- a/app/src/features/map/MapPage.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import Box from '@mui/material/Box'; -import Paper from '@mui/material/Paper'; -import intersect from '@turf/intersect'; -import { IMarkerLayer } from 'components/map/components/MarkerClusterControls'; -import { IStaticLayer, IStaticLayerFeature } from 'components/map/components/StaticLayersControls'; -import MapContainer from 'components/map/MapContainer'; -import { AreaToolTip, IFormikAreaUpload } from 'components/upload/UploadArea'; -import { ALL_OF_BC_BOUNDARY, MAP_DEFAULT_ZOOM, SPATIAL_COMPONENT_TYPE } from 'constants/spatial'; -import { IDatasetVisibility, ISearchResult } from 'features/datasets/components/SearchResultList'; -import { Feature, Polygon } from 'geojson'; -import { useApi } from 'hooks/useApi'; -import useDataLoader from 'hooks/useDataLoader'; -import useDataLoaderError from 'hooks/useDataLoaderError'; -import useURL from 'hooks/useURL'; -import { LatLngBounds, LatLngBoundsExpression, LatLngTuple } from 'leaflet'; -import React, { useEffect, useRef, useState } from 'react'; -import { calculateUpdatedMapBounds } from 'utils/mapUtils'; -import { parseBoundaryCentroidResults, parseOccurrenceResults, parseSpatialDataByType } from 'utils/spatial-utils'; -import SideSearchBar from './SideSearchBar'; - -const MapPage: React.FC = () => { - const api = useApi(); - - const url = useURL<{ - mapViewBoundary: Feature | undefined; - drawnBoundary: Feature | undefined; - type: string[] | undefined; - zoom: number | undefined; - }>(); - - const mapDataLoader = useDataLoader( - (searchBoundary: Feature[], searchType: string[], species?: string[], searchZoom?: number, datasetID?: string) => - api.search.getSpatialData({ - boundary: searchBoundary, - type: searchType, - species: species, - zoom: searchZoom, - datasetID: datasetID - }) - ); - - const loadedFromUrl = useRef(false); - - useDataLoaderError(mapDataLoader, () => { - return { - dialogTitle: 'Error Loading Map Data', - dialogText: - 'An error has occurred while attempting to load map data, please try again. If the error persists, please contact your system administrator.' - }; - }); - - const [mapViewBoundary] = useState | undefined>(url.queryParams.mapViewBoundary); - const [drawnBoundary, setDrawnBoundary] = useState | undefined>(url.queryParams.drawnBoundary); - - const [type] = useState(url.queryParams.type || [SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID]); - const [zoom, setZoom] = useState(url.queryParams.zoom || MAP_DEFAULT_ZOOM); - - const [markerLayers, setMarkerLayers] = useState([]); - const [staticLayers, setStaticLayers] = useState([]); - const [shouldUpdateBounds, setShouldUpdateBounds] = useState(false); - const [updatedBounds, setUpdatedBounds] = useState(undefined); - const [areaStaticLayers, setAreaStaticLayers] = useState([]); - const [datasetVisibility, setDatasetVisibility] = useState({}); - - const [parsedSearchResults, setParsedSearchResults] = useState([]); - - useEffect(() => { - if (!mapDataLoader.data) { - return; - } - - const result = parseSpatialDataByType(mapDataLoader.data, datasetVisibility); - - setMarkerLayers(result.markerLayers); - setStaticLayers(result.staticLayers); - - setParsedSearchResults([ - ...parseOccurrenceResults(mapDataLoader.data, datasetVisibility), - ...parseBoundaryCentroidResults(mapDataLoader.data, datasetVisibility) - ]); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [mapDataLoader.data, datasetVisibility]); - - useEffect(() => { - if (!loadedFromUrl.current) { - loadedFromUrl.current = true; - if (drawnBoundary) { - const searchBoundary = getSearchBoundary(mapViewBoundary, drawnBoundary); - mapDataLoader.refresh([searchBoundary], type, [], zoom); - } - } - }); - - const getSearchBoundary = (boundary1?: Feature, boundary2?: Feature) => { - return (boundary2 && boundary1 && intersect(boundary2, boundary1)) || boundary1 || boundary2 || ALL_OF_BC_BOUNDARY; - }; - - const onDrawChange = (features: Feature[]) => { - // In this case, we have disabled all draw controls except Polygons, and limited it to only 1 at a time, so - // assuming the type should be safe. This will need to be updated if the draw control options are changed. - const bounds = features?.[0] as Feature | undefined; - - // Store user drawn boundary - setDrawnBoundary(bounds); - - // Calculate search boundary based on drawn and map view boundaries - const searchBoundary = getSearchBoundary(mapViewBoundary, bounds); - - // Store drawn bounds in URL - url.appendQueryParams({ drawnBoundary: bounds }); - - mapDataLoader.refresh([searchBoundary], type, [], zoom); - }; - - const onAreaUpdate = (areas: IFormikAreaUpload[]) => { - const staticLayers: IStaticLayer[] = []; - const featureArray: Feature[] = []; - - areas.forEach((area) => { - const layers: IStaticLayerFeature[] = []; - area.features.forEach((feature: Feature) => { - const staticLayerFeature: IStaticLayerFeature = { - geoJSON: feature, - tooltip: - }; - layers.push(staticLayerFeature); - featureArray.push(feature); - }); - const staticLayer: IStaticLayer = { layerName: area.name, features: layers, visible: true }; - staticLayers.push(staticLayer); - }); - - setAreaStaticLayers(staticLayers); - setBounds(featureArray); - }; - - const setBounds = (features: Feature[]) => { - const bounds = calculateUpdatedMapBounds(features); - - if (bounds) { - const newBounds = new LatLngBounds(bounds[0] as LatLngTuple, bounds[1] as LatLngTuple); - setShouldUpdateBounds(true); - setUpdatedBounds(newBounds); - } else { - const boundsBC = calculateUpdatedMapBounds([ALL_OF_BC_BOUNDARY]); - if (boundsBC) { - const newBounds = new LatLngBounds(boundsBC[0] as LatLngTuple, boundsBC[1] as LatLngTuple); - setShouldUpdateBounds(true); - setUpdatedBounds(newBounds); - } - } - }; - - const onToggleDataVisibility = (visibility: IDatasetVisibility) => { - setDatasetVisibility({ ...visibility }); - }; - - return ( - - - - - - , zoom: number) => { - setZoom(zoom); - setShouldUpdateBounds(false); - }} - drawControls={{ - initialFeatures: drawnBoundary && [drawnBoundary], - options: { - // Disable all controls except for Polygon (and Rectangle, which is just a type of Polygon) - draw: { circle: false, circlemarker: false, marker: false, polyline: false } - }, - // Limit drawing to 1 shape at a time - clearOnDraw: true - }} - onDrawChange={onDrawChange} - scrollWheelZoom={true} - zoom={zoom} - fullScreenControl={true} - markerLayers={markerLayers} - staticLayers={[...staticLayers, ...areaStaticLayers]} - bounds={(shouldUpdateBounds && updatedBounds) || undefined} - zoomControlEnabled={true} - doubleClickZoomEnabled={true} - draggingEnabled={true} - layerControlEnabled={true} - /> - - - ); -}; - -export default MapPage; diff --git a/app/src/features/map/MapRouter.tsx b/app/src/features/map/MapRouter.tsx deleted file mode 100644 index 7ba5dbd78..000000000 --- a/app/src/features/map/MapRouter.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Switch } from 'react-router'; -import RouteWithTitle from 'utils/RouteWithTitle'; -import { getTitle } from 'utils/Utils'; -import MapPage from './MapPage'; - -/** - * Router for all `/map` pages. - * - * @return {*} - */ -const MapRouter: React.FC = () => { - return ( - - - - - - ); -}; - -export default MapRouter; diff --git a/app/src/features/map/SideSearchBar.test.tsx b/app/src/features/map/SideSearchBar.test.tsx deleted file mode 100644 index 6eb064585..000000000 --- a/app/src/features/map/SideSearchBar.test.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Feature, GeoJsonProperties, Geometry } from 'geojson'; -import { useApi } from 'hooks/useApi'; -import useDataLoader from 'hooks/useDataLoader'; -import { ISpatialData } from 'interfaces/useSearchApi.interface'; -import { cleanup, render } from 'test-helpers/test-utils'; -import SideSearchBar from './SideSearchBar'; - -jest.mock('../../hooks/useApi'); - -const mockBiohubApi = useApi as jest.Mock; - -const mockUseApi = { - taxonomy: { - searchSpecies: jest.fn() - } -}; - -describe('SideSearchBar', () => { - beforeEach(() => { - mockBiohubApi.mockImplementation(() => mockUseApi); - }); - afterEach(() => { - cleanup(); - }); - - it('renders an empty component', () => { - const TestComponent = () => { - const mockCallBack = jest.fn().mockResolvedValue({}); - const mockMapDataLoader = useDataLoader< - [ - searchBoundary: Feature[], - searchType: string[], - species?: string[], - searchZoom?: number, - datasetID?: string - ], - ISpatialData[], - unknown - >(mockCallBack); - const mockAreaUpdate = jest.fn(); - const mockOnToggleDataVisibility = jest.fn(); - return ( - - ); - }; - - const { getByText } = render(); - - expect(getByText('What do you want to find?', { exact: false })).toBeVisible(); - expect(getByText('Refine Search Area', { exact: false })).toBeVisible(); - expect(getByText('Find Data', { exact: false })).toBeVisible(); - }); -}); diff --git a/app/src/features/map/SideSearchBar.tsx b/app/src/features/map/SideSearchBar.tsx deleted file mode 100644 index 7a8521d07..000000000 --- a/app/src/features/map/SideSearchBar.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { mdiArrowRight } from '@mdi/js'; -import Icon from '@mdi/react'; -import LoadingButton from '@mui/lab/LoadingButton'; -import { Box, Button } from '@mui/material'; -import Divider from '@mui/material/Divider'; -import Typography from '@mui/material/Typography'; -import simplify from '@turf/simplify'; -import { ErrorDialog } from 'components/dialog/ErrorDialog'; -import { IMultiAutocompleteFieldOption } from 'components/fields/MultiAutocompleteField'; -import { IFormikAreaUpload } from 'components/upload/UploadArea'; -import DatasetSearchForm, { - DatasetSearchFormInitialValues, - DatasetSearchFormYupSchema, - IDatasetSearchForm -} from 'features/datasets/components/DatasetSearchForm'; -import SearchResultList, { IDatasetVisibility, ISearchResult } from 'features/datasets/components/SearchResultList'; -import { Form, Formik, FormikProps } from 'formik'; -import { Feature, GeoJsonProperties, Geometry, Polygon } from 'geojson'; -import { DataLoader } from 'hooks/useDataLoader'; -import { ISpatialData } from 'interfaces/useSearchApi.interface'; -import { useEffect, useRef, useState } from 'react'; - -export interface IDatasetRequest { - criteria: { - boundary: Feature; - type: string[]; - species?: IMultiAutocompleteFieldOption[]; - zoom?: number; // TODO include in request params when backend is updated to receive it - datasetID?: string; - datasetName?: string; - }; -} - -export interface SideSearchBarProps { - mapDataLoader: DataLoader< - [ - searchBoundary: Feature[], - searchType: string[], - species?: string[], - searchZoom?: number, - datasetID?: string - ], - ISpatialData[], - unknown - >; - searchResults: ISearchResult[]; - onAreaUpdate: (area: IFormikAreaUpload[]) => void; - onToggleDataVisibility: (datasets: IDatasetVisibility) => void; -} - -const SideSearchBar: React.FC = (props) => { - const formikRef = useRef>(null); - const [showForm, setShowForm] = useState(true); - const [showNoData, setShowNoData] = useState(false); - const [showSpinner, setShowSpinner] = useState(false); - - const [formData, setFormData] = useState(null); - /** - * Handle dataset requests. - */ - const handleDatasetRequestCreation = async (values: IDatasetSearchForm) => { - const featureArray: Feature[] = []; - values.area.forEach((area: IFormikAreaUpload) => { - area.features.forEach((feature: Feature) => { - const newFeature: Feature = { - type: 'Feature', - geometry: simplify(feature.geometry, { tolerance: 0.01, highQuality: false }), - properties: feature.properties - }; - featureArray.push(newFeature); - }); - }); - - const species_array: string[] = []; - values.species_list.forEach((item) => { - species_array.push(item.value.toString()); - }); - - props.mapDataLoader.refresh(featureArray, [values.dataset], species_array); - setFormData(values); - }; - - const toggleShowForm = () => { - setShowForm(!showForm); - }; - - useEffect(() => { - setShowSpinner(true); - if (props.mapDataLoader.isReady) { - if (!props.mapDataLoader.data?.length) { - setShowNoData(true); - setShowForm(true); - } else { - setShowForm(false); - } - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.mapDataLoader.isLoading, props.mapDataLoader.isReady]); - - const hasResults = props.searchResults.length > 0; - - return ( - <> - setShowNoData(false)} - onOk={() => setShowNoData(false)} - /> - {showForm && ( - - - - - Map Search - - {hasResults && ( - - )} - - - - - - innerRef={formikRef} - enableReinitialize={true} - initialValues={formData || DatasetSearchFormInitialValues} - validationSchema={DatasetSearchFormYupSchema} - validateOnBlur={true} - validateOnChange={false} - onSubmit={handleDatasetRequestCreation}> - {(formikProps) => ( - -
- - - - {showSpinner && - (props.mapDataLoader.isLoading ? ( - - Submit - - ) : ( - - ))} - - -
- )} - -
-
- )} - - {!showForm && hasResults && ( - toggleShowForm()} - onToggleDataVisibility={props.onToggleDataVisibility} - /> - )} - - ); -}; - -export default SideSearchBar; diff --git a/app/src/features/search/SearchComponent.tsx b/app/src/features/search/SearchComponent.tsx index d9f8c1ab7..72d7f3830 100644 --- a/app/src/features/search/SearchComponent.tsx +++ b/app/src/features/search/SearchComponent.tsx @@ -1,13 +1,9 @@ import { mdiMagnify } from '@mdi/js'; import { Icon } from '@mdi/react'; import { Theme } from '@mui/material'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; import Input from '@mui/material/Input'; import InputAdornment from '@mui/material/InputAdornment'; import { makeStyles } from '@mui/styles'; -import { useFormikContext } from 'formik'; -import { IAdvancedSearch } from 'interfaces/useSearchApi.interface'; import { ChangeEvent } from 'react'; export const useSearchInputStyles = makeStyles((theme: Theme) => ({ @@ -86,35 +82,3 @@ export const SearchInput = (props: ISearchInputProps) => { /> ); }; - -const SearchComponent: React.FC = () => { - const classes = useSearchInputStyles(); - - const formikProps = useFormikContext(); - const { handleSubmit, handleChange, values } = formikProps; - - return ( -
- - - - -
- ); -}; - -export default SearchComponent; diff --git a/app/src/hooks/api/useDatasetApi.test.ts b/app/src/hooks/api/useDatasetApi.test.ts index d97b085f1..e7f01a331 100644 --- a/app/src/hooks/api/useDatasetApi.test.ts +++ b/app/src/hooks/api/useDatasetApi.test.ts @@ -13,16 +13,6 @@ describe('useDatasetApi', () => { mock.restore(); }); - it('getDatasetEML works as expected', async () => { - const response = 'response'; - - mock.onGet('api/dwc/submission/a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf/get').reply(200, response); - - const actualResult = await useDatasetApi(axios).getDatasetEML('a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf'); - - expect(actualResult).toEqual('response'); - }); - it('getDatasetArtifacts works as expected', async () => { const response = { artifacts: [{ artifact_id: 1 }, { artifact_id: 2 }] @@ -44,29 +34,4 @@ describe('useDatasetApi', () => { expect(actualResult).toEqual('http://example.com'); }); - - it('getRelatedDatasets works as expected', async () => { - mock.onGet(`api/dwc/submission/${'123-456-789'}/related`).reply(200, { - datasets: [{ datasetId: '123' }, { datasetId: '456' }] - }); - - const actualResult = await useDatasetApi(axios).getRelatedDatasets('123-456-789'); - - expect(actualResult).toEqual({ - datasets: [{ datasetId: '123' }, { datasetId: '456' }] - }); - }); - - it('getHandleBarsTemplateByDatasetId works as expected', async () => { - mock.onGet(`api/dwc/submission/uuid/handlebar`).reply(200, { - header: 'Header Template', - details: 'Details Template' - }); - - const results = await useDatasetApi(axios).getHandleBarsTemplateByDatasetId('uuid'); - expect(results).toEqual({ - header: 'Header Template', - details: 'Details Template' - }); - }); }); diff --git a/app/src/hooks/api/useDatasetApi.ts b/app/src/hooks/api/useDatasetApi.ts index cc764c050..b1b7905d2 100644 --- a/app/src/hooks/api/useDatasetApi.ts +++ b/app/src/hooks/api/useDatasetApi.ts @@ -1,5 +1,5 @@ import { AxiosInstance } from 'axios'; -import { IArtifact, IHandlebarsTemplates, IListRelatedDatasetsResponse } from 'interfaces/useDatasetApi.interface'; +import { IArtifact } from 'interfaces/useDatasetApi.interface'; /** * Returns a set of supported api methods for working with datasets. @@ -8,18 +8,6 @@ import { IArtifact, IHandlebarsTemplates, IListRelatedDatasetsResponse } from 'i * @return {*} object whose properties are supported api methods. */ const useDatasetApi = (axios: AxiosInstance) => { - /** - * Fetch dataset metadata by datasetId. - * - * @param {string} datasetId - * @return {*} {Promise} - */ - const getDatasetEML = async (datasetId: string): Promise => { - const { data } = await axios.get(`api/dwc/submission/${datasetId}/get`); - - return data; - }; - /** * Fetch dataset data by datasetUUID. * @@ -55,37 +43,10 @@ const useDatasetApi = (axios: AxiosInstance) => { return data; }; - /** - * Fetch the signed handlebar template for a given dataset ID. - * - * @param {string} datasetId - * @return {*} {Promise} - */ - const getHandleBarsTemplateByDatasetId = async (datasetId: string): Promise => { - const { data } = await axios.get(`api/dwc/submission/${datasetId}/handlebar`); - - return data; - }; - - /** - * Fetch a list of datasets related to the given dataset - * - * @param {string} datasetId - * @return {*} {Promise} - */ - const getRelatedDatasets = async (datasetId: string): Promise => { - const { data } = await axios.get(`api/dwc/submission/${datasetId}/related`); - - return data; - }; - return { - getDatasetEML, getDataset, getDatasetArtifacts, - getArtifactSignedUrl, - getHandleBarsTemplateByDatasetId, - getRelatedDatasets + getArtifactSignedUrl }; }; diff --git a/app/src/hooks/api/useSearchApi.test.ts b/app/src/hooks/api/useSearchApi.test.ts deleted file mode 100644 index 531306964..000000000 --- a/app/src/hooks/api/useSearchApi.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; -import { Feature, FeatureCollection } from 'geojson'; -import useSearchApi, { usePublicSearchApi } from './useSearchApi'; - -describe('useSearchApi', () => { - let mock: any; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('getSpatialData', () => { - it('with criteria `type` works as expected', async () => { - const res = [{ type: 'FeatureCollection' } as FeatureCollection]; - - mock.onGet('/api/dwc/spatial/search').reply(200, res); - - const result = await useSearchApi(axios).getSpatialData({ - boundary: [{ type: 'Feature' }] as Feature[], - type: ['type'] - }); - - expect(result[0]).toEqual({ type: 'FeatureCollection' }); - }); - - it('with criteria `species` works as expected', async () => { - const res = [{ species: 'Spotted Owl' }]; - - mock.onGet('/api/dwc/spatial/search').reply(200, res); - - const result = await useSearchApi(axios).getSpatialData({ - boundary: [{ type: 'Feature' }] as Feature[], - type: ['type'], - species: ['species'] - }); - - expect(result[0]).toEqual({ species: 'Spotted Owl' }); - }); - }); - - it('getSpatialFile works as expected', async () => { - const res = 'zipped file contents'; - - mock.onGet('/api/dwc/spatial/download').reply(200, res); - - const result = await useSearchApi(axios).getSpatialDataFile({ - boundary: [{ type: 'Feature' }] as Feature[], - type: ['type'], - datasetID: '123' - }); - - expect(typeof result).toBe('string'); - expect(result).toEqual('zipped file contents'); - }); -}); - -describe('usePublicSearchApi', () => { - let mock: any; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - it('getSearchResults works as expected', async () => { - const res = [ - { - id: '1', - name: 'name', - objectives: 'objectives', - geometry: [] - } - ]; - - mock.onGet('api/public/search').reply(200, res); - - const result = await usePublicSearchApi(axios).getSearchResults(); - - expect(result[0].id).toEqual('1'); - }); -}); diff --git a/app/src/hooks/api/useSearchApi.ts b/app/src/hooks/api/useSearchApi.ts deleted file mode 100644 index bb72ed935..000000000 --- a/app/src/hooks/api/useSearchApi.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { AxiosInstance } from 'axios'; -import { Feature, GeoJsonProperties } from 'geojson'; -import { IGetSearchResultsResponse, ISpatialData } from 'interfaces/useSearchApi.interface'; - -/** - * Returns a set of supported api methods for working with search functionality - * - * @param {AxiosInstance} axios - * @return {*} object whose properties are supported api methods. - */ -const useSearchApi = (axios: AxiosInstance) => { - const getSpatialData = async (criteria: { - boundary: Feature[]; - type: string[]; - species?: string[]; - zoom?: number; // TODO include in request params when backend is updated to receive it - datasetID?: string; - }): Promise => { - const { data } = await axios.get(`/api/dwc/spatial/search`, { - params: { - boundary: criteria.boundary, - species: criteria.species, - type: criteria.type, - datasetID: criteria.datasetID - } - }); - - return data; - }; - - const getSpatialMetadata = async (submissionSpatialComponentIds: number[]): Promise => { - const { data } = await axios.get(`/api/dwc/spatial/metadata`, { - params: { submissionSpatialComponentIds } - }); - - return data; - }; - - const getSpatialDataFile = async (criteria: { - boundary: Feature[]; - type: string[]; - zoom?: number; // TODO include in request params when backend is updated to receive it - datasetID?: string; - }): Promise => { - const { data } = await axios.get(`/api/dwc/spatial/download`, { - params: { boundary: criteria.boundary, type: criteria.type, datasetID: criteria.datasetID } - }); - - return data; - }; - - return { - getSpatialData, - getSpatialMetadata, - getSpatialDataFile - }; -}; - -export default useSearchApi; - -/** - * Returns a set of supported api methods for working with public search functionality. - * - * @param {AxiosInstance} axios - * @return {*} object whose properties are supported api methods. - */ -export const usePublicSearchApi = (axios: AxiosInstance) => { - /** - * Get public search results (spatial) - * - * @return {*} {Promise} - */ - const getSearchResults = async (): Promise => { - const { data } = await axios.get(`/api/public/search`); - - return data; - }; - - return { - getSearchResults - }; -}; diff --git a/app/src/hooks/api/useSubmissionsApi.test.ts b/app/src/hooks/api/useSubmissionsApi.test.ts index 81d4869b8..3928c102e 100644 --- a/app/src/hooks/api/useSubmissionsApi.test.ts +++ b/app/src/hooks/api/useSubmissionsApi.test.ts @@ -13,34 +13,6 @@ describe('useSubmissionApi', () => { mock.restore(); }); - it('listSubmissions works as expected', async () => { - const res = [ - { - create_date: '2022-05-24T18:41:42.056Z', - create_user: 15, - darwin_core_source: {}, - delete_timestamp: null, - eml_source: null, - event_timestamp: '2022-05-24T18:41:42.211Z', - input_file_name: 'moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - input_key: 'biohub/1/moose_aerial_stratifiedrandomblock_composition_recruitment_survey_2.5_withdata.zip', - revision_count: 1, - source: 'SIMS', - submission_id: 1, - submission_status: null, - update_date: '2022-05-24T18:41:42.056Z', - update_user: 15, - uuid: '2267501d-c6a9-43b5-b951-2324faff6397' - } - ]; - - mock.onGet('/api/dwc/submission/list').reply(200, res); - - const result = await useSubmissionsApi(axios).listSubmissions(); - - expect(result[0].submission_id).toEqual(1); - }); - it('getSignedUrl works as expected', async () => { const res = 'test-signed-url'; const testSubmissionId = 1; diff --git a/app/src/hooks/api/useSubmissionsApi.ts b/app/src/hooks/api/useSubmissionsApi.ts index 69211fb9b..a73446780 100644 --- a/app/src/hooks/api/useSubmissionsApi.ts +++ b/app/src/hooks/api/useSubmissionsApi.ts @@ -2,7 +2,6 @@ import { AxiosInstance } from 'axios'; import { IGetDownloadSubmissionResponse, IGetSubmissionGroupedFeatureResponse, - IListSubmissionsResponse, SubmissionFeatureSignedUrlPayload, SubmissionRecordPublishedForPublic, SubmissionRecordWithSecurity, @@ -16,17 +15,6 @@ import { * @return {*} object whose properties are supported api methods. */ const useSubmissionsApi = (axios: AxiosInstance) => { - /** - * Fetch all submissions. - * - * @return {*} {Promise} - */ - const listSubmissions = async (): Promise => { - const { data } = await axios.get('/api/dwc/submission/list'); - - return data; - }; - /** * Fetch the signed URL of a submission by submission ID. * @@ -169,7 +157,6 @@ const useSubmissionsApi = (axios: AxiosInstance) => { }; return { - listSubmissions, getSignedUrl, getSubmissionDownloadPackage, getSubmissionPublishedDownloadPackage, diff --git a/app/src/hooks/useApi.ts b/app/src/hooks/useApi.ts index 4af2637da..a2be22539 100644 --- a/app/src/hooks/useApi.ts +++ b/app/src/hooks/useApi.ts @@ -5,7 +5,6 @@ import useArtifactApi from './api/useArtifactApi'; import useAxios from './api/useAxios'; import useCodesApi from './api/useCodesApi'; import useDatasetApi from './api/useDatasetApi'; -import useSearchApi, { usePublicSearchApi } from './api/useSearchApi'; import useSecurityApi from './api/useSecurityApi'; import useSubmissionsApi from './api/useSubmissionsApi'; import useTaxonomyApi from './api/useTaxonomyApi'; @@ -28,12 +27,6 @@ export const useApi = () => { const taxonomy = useTaxonomyApi(apiAxios); - const publicApis = { - search: usePublicSearchApi(apiAxios) - }; - - const search = useSearchApi(apiAxios); - const dataset = useDatasetApi(apiAxios); const artifact = useArtifactApi(apiAxios); @@ -46,8 +39,6 @@ export const useApi = () => { user, admin, submissions, - public: publicApis, - search, dataset, taxonomy, security, diff --git a/app/src/interfaces/useDatasetApi.interface.ts b/app/src/interfaces/useDatasetApi.interface.ts index f09ecba4a..25afd6211 100644 --- a/app/src/interfaces/useDatasetApi.interface.ts +++ b/app/src/interfaces/useDatasetApi.interface.ts @@ -47,10 +47,6 @@ export interface IListRelatedDatasetsResponse { datasetsWithSupplementaryData: IRelatedDataset[]; } -export interface IHandlebarsTemplates { - header: string; - details: string; -} export interface IDatasetForReview { dataset_id: string; // UUID artifacts_to_review: number; diff --git a/app/src/interfaces/useSearchApi.interface.ts b/app/src/interfaces/useSearchApi.interface.ts deleted file mode 100644 index 8e56d706c..000000000 --- a/app/src/interfaces/useSearchApi.interface.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Feature, FeatureCollection } from 'geojson'; - -/** - * Get search results response object. - * - * @export - * @interface IGetSearchResultsResponse - */ -export interface IGetSearchResultsResponse { - id: string; - name?: string; - objectives?: string; - taxonID?: string; - lifestage?: string; - geometry: Feature[]; -} - -export type EmptyObject = Record; - -export interface ITaxaData { - taxon_id?: string; - vernacular_name?: string; - submission_spatial_component_id: number; -} -export interface ISpatialData { - taxa_data: ITaxaData[]; - spatial_data: FeatureCollection | EmptyObject; -} - -/** - * An interface for an instance of filter fields for search results - */ -export interface ISearchResultsAdvancedFilterRequest { - record_type: string; - geometry: Feature[]; -} - -/** - * Interface for Occurrence table return. - * Check api\src\repositories\occurrence-repository.ts for updates - * @export - * @interface IGetOccurrenceData - */ -export interface IGetOccurrenceData { - occurrenceId: number; - submissionId: number; - taxonId: string | null; - lifeStage: string | null; - sex: string | null; - eventDate: object | null; - vernacularName: string | null; - individualCount: number | null; - organismQuantity: number | null; - organismQuantityType: string | null; - geometry: Feature | null; -} - -export type IElasticSearchResponse = { - id: string; - fields: Fields; - source: Source; -}; - -export interface IKeywordSearchResponse, Source = Record> - extends IElasticSearchResponse { - observation_count: number; -} - -export interface IAdvancedSearch { - keywords: string; -} diff --git a/app/src/interfaces/useSubmissionsApi.interface.ts b/app/src/interfaces/useSubmissionsApi.interface.ts index 0912f52e5..7578c23ba 100644 --- a/app/src/interfaces/useSubmissionsApi.interface.ts +++ b/app/src/interfaces/useSubmissionsApi.interface.ts @@ -1,23 +1,5 @@ import { SECURITY_APPLIED_STATUS } from './useDatasetApi.interface'; -export type IListSubmissionsResponse = Array<{ - submission_id: number; - submission_status: string; - source_transform_id: string; - uuid: string; - event_timestamp: string; - delete_timestamp: string | null; - input_key: string | null; - input_file_name: string | null; - eml_source: string | null; - darwin_core_source: string | null; - create_date: string; - create_user: number; - update_date: string | null; - update_user: number | null; - revision_count: number; -}>; - /** NET-NEW INTERFACES FOR UPDATED SCHEMA **/ export type SubmissionRecord = { diff --git a/app/src/styles/handlebar.scss b/app/src/styles/handlebar.scss deleted file mode 100644 index d759e4097..000000000 --- a/app/src/styles/handlebar.scss +++ /dev/null @@ -1,62 +0,0 @@ -.hbr-header { - display: flex; - flex-direction: column; - - .hbr-header-title-primary { - font-size: 32px; - color: #313132; - font-weight: bold; - margin: 0; - } - - .hbr-header-badge { - background-color: #d9eaf7; - border-radius: 16px; - padding: 5px 10px; - width: fit-content; - font-size: 10px; - font-weight: bold; - color: #313132; - text-transform: uppercase; - margin-top: 6px; - } -} - -.hbr-container { - .hbr-project-coordinator { - list-style-type: none; - padding: 0; - margin: 0; - } - - .details-container { - border-radius: 6px; - background-color: #ffffff; - margin: 0 24px; - .details-container-title { - background-color: #ffffff; - background-size: cover; - font-size: 20px; - font-weight: bold; - padding: 18px 25px; - border-bottom: solid thin #dadada; - border-radius: 6px 6px 0 0; - } - - .details-container-metadata { - div { - display: flex; - padding: 10px 0px; - border-bottom: solid thin #dadada; - dt { - color: #787f81; - flex: 1; - max-width: 200px; - } - dd { - flex: 1; - } - } - } - } -} diff --git a/app/src/utils/handlebarsUtils.ts b/app/src/utils/handlebarsUtils.ts deleted file mode 100644 index b60d7030a..000000000 --- a/app/src/utils/handlebarsUtils.ts +++ /dev/null @@ -1,106 +0,0 @@ -import dayjs from 'dayjs'; -import Handlebars, { HelperOptions } from 'handlebars'; - -export const useHandlebars = () => { - /** - * This functions allows us to conditionally check values - * - */ - const applyConditionalChecks = () => { - Handlebars.registerHelper('ifCond', (v1, operator, v2, options: HelperOptions) => { - switch (operator) { - case '==': - return v1 === v2 ? options.fn(this) : options.inverse(this); - case '===': - return v1 === v2 ? options.fn(this) : options.inverse(this); - case '!=': - return v1 !== v2 ? options.fn(this) : options.inverse(this); - case '!==': - return v1 !== v2 ? options.fn(this) : options.inverse(this); - case '<': - return v1 < v2 ? options.fn(this) : options.inverse(this); - case '<=': - return v1 <= v2 ? options.fn(this) : options.inverse(this); - case '>': - return v1 > v2 ? options.fn(this) : options.inverse(this); - case '>=': - return v1 >= v2 ? options.fn(this) : options.inverse(this); - case '&&': - return v1 && v2 ? options.fn(this) : options.inverse(this); - case '||': - return v1 || v2 ? options.fn(this) : options.inverse(this); - default: - return options.inverse(this); - } - }); - }; - - /** - * This is a Handlesbars helper to format date strings from a given format to another using dayjs - * Example of use - * - * {{formatDate MyDateProperty 'YYYY-MM-DD' 'MMM YYYY'}} - * - */ - const formatDateHelper = () => { - Handlebars.registerHelper('formatDate', (dateString: string, ogFormat: string, newFormat: string) => { - return dayjs(dateString, ogFormat).format(newFormat).toString(); - }); - }; - - /** - * This is a Handlesbars helper to check if a passed item is an array or not - * Example of use in a template - * - * - * {{#if (isAnArray AnyObject)}} - * - * {{else}} - * - * {{/if}} - * - */ - const isAnArray = () => { - Handlebars.registerHelper('isAnArray', (item: any) => { - return Array.isArray(item); - }); - }; - - /** - * This function converts a rawTemplate to a template - * - * @param {string} template - * @return {*} {HandlebarsTemplateDelegate} - */ - const compileFromRawTemplate = (template: TemplateSpecification): HandlebarsTemplateDelegate => { - applyConditionalChecks(); - formatDateHelper(); - isAnArray(); - return Handlebars.compile(template); - }; - - /** - * This function converts a precompiled template - * see Readme/handlebars.md for more information - * - * @param {TemplateSpecification} preCompiledTemplate - * @return {*} {HandlebarsTemplateDelegate} - */ - const compileFromPrecompiledTemplate = (preCompiledTemplate: TemplateSpecification): HandlebarsTemplateDelegate => { - // This is a workaround to using Handlebars.template(preCompiledTemplate) - // in order to avoid an unknown object exception - - const encodedHandlebarsFunction = `(handlebars) => handlebars.template(${preCompiledTemplate})`; - // eslint-disable-next-line no-eval - const handlebarsFunction = eval(encodedHandlebarsFunction); - - return handlebarsFunction(Handlebars); - }; - - return { - compileFromRawTemplate, - compileFromPrecompiledTemplate - }; -}; - -export default useHandlebars; diff --git a/app/src/utils/spatial-utils.test.tsx b/app/src/utils/spatial-utils.test.tsx deleted file mode 100644 index 4507bc8ad..000000000 --- a/app/src/utils/spatial-utils.test.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { LAYER_NAME, SPATIAL_COMPONENT_TYPE } from 'constants/spatial'; -import { ISpatialData } from 'interfaces/useSearchApi.interface'; -import { parseSpatialDataByType } from './spatial-utils'; - -describe('parseSpatialDataByType', () => { - it('returns empty responses if featureCollections param is empty', () => { - const spatialData: ISpatialData[] = []; - - const result = parseSpatialDataByType(spatialData); - - expect(result.markerLayers).toEqual([{ layerName: LAYER_NAME.OCCURRENCES, markers: [], visible: true }]); - - expect(result.staticLayers).toEqual([{ layerName: LAYER_NAME.BOUNDARIES, features: [], visible: true }]); - }); - - it('returns empty responses if featureCollections param is has no features', () => { - const spatialData: ISpatialData[] = [ - { - taxa_data: [ - { - submission_spatial_component_id: 1 - } - ], - spatial_data: { - type: 'FeatureCollection', - features: [] - } - } - ]; - - const result = parseSpatialDataByType(spatialData); - - expect(result.markerLayers).toEqual([{ layerName: LAYER_NAME.OCCURRENCES, markers: [], visible: true }]); - - expect(result.staticLayers).toEqual([{ layerName: LAYER_NAME.BOUNDARIES, features: [], visible: true }]); - }); - - it('returns non-empty responses if featureCollections has features', () => { - const spatialData: ISpatialData[] = [ - { - taxa_data: [ - { - taxon_id: 'M-ALAM', - vernacular_name: 'Moose', - submission_spatial_component_id: 1 - } - ], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [123, 456] - }, - properties: { - type: SPATIAL_COMPONENT_TYPE.OCCURRENCE - } - } - ] - } - }, - { - taxa_data: [ - { - submission_spatial_component_id: 2 - } - ], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'GeometryCollection', - geometries: [] - }, - properties: {} - } - ] - } - }, - { - taxa_data: [ - { - submission_spatial_component_id: 3 - } - ], - spatial_data: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [321, 654] - }, - properties: { - type: SPATIAL_COMPONENT_TYPE.BOUNDARY - } - } - ] - } - } - ]; - - const result = parseSpatialDataByType(spatialData); - - expect(result.markerLayers.length).toEqual(1); - expect(result.markerLayers[0]).toMatchObject({ - layerName: LAYER_NAME.OCCURRENCES, - markers: [ - { - position: [123, 456], - key: '1', - popup: expect.any(Object) - } - ] - }); - - expect(result.staticLayers.length).toEqual(1); - expect(result.staticLayers[0]).toMatchObject({ - layerName: LAYER_NAME.BOUNDARIES, - features: [ - { - geoJSON: { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [321, 654] - }, - properties: { - type: SPATIAL_COMPONENT_TYPE.BOUNDARY - } - }, - key: undefined, - popup: expect.any(Object) - } - ] - }); - }); -}); diff --git a/app/src/utils/spatial-utils.tsx b/app/src/utils/spatial-utils.tsx deleted file mode 100644 index 9f06a3c2e..000000000 --- a/app/src/utils/spatial-utils.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import { IMarker, IMarkerLayer } from 'components/map/components/MarkerClusterControls'; -import { IStaticLayer } from 'components/map/components/StaticLayersControls'; -import DatasetPopup from 'components/map/DatasetPopup'; -import FeaturePopup, { BoundaryCentroidFeature, BoundaryFeature, OccurrenceFeature } from 'components/map/FeaturePopup'; -import { LAYER_NAME, SPATIAL_COMPONENT_TYPE } from 'constants/spatial'; -import { IDatasetVisibility, ISearchResult } from 'features/datasets/components/SearchResultList'; -import { Feature } from 'geojson'; -import { EmptyObject, ISpatialData, ITaxaData } from 'interfaces/useSearchApi.interface'; -import { LatLngTuple } from 'leaflet'; -import { isObject } from './Utils'; - -const UNKNOWN_TAXON = 'Unknown'; - -export interface ISpatialDataGroupedBySpecies { - [species: string]: ISpatialData[]; -} - -export const parseSpatialDataByType = ( - spatialDataRecords: ISpatialData[], - datasetVisibility: IDatasetVisibility = {} -) => { - const occurrencesMarkerLayer: IMarkerLayer = { layerName: LAYER_NAME.OCCURRENCES, markers: [], visible: true }; - const boundaryStaticLayer: IStaticLayer = { layerName: LAYER_NAME.BOUNDARIES, features: [], visible: true }; - - for (const spatialRecord of spatialDataRecords) { - if (isEmptyObject(spatialRecord.spatial_data)) { - continue; - } - - for (const feature of spatialRecord.spatial_data.features) { - let visible = true; - - if (feature.geometry.type === 'GeometryCollection') { - // Not expecting or supporting geometry collections - continue; - } - - if (isOccurrenceFeature(feature)) { - // check if species has been toggled on/ off - const marker = occurrenceMarkerSetup( - feature.geometry.coordinates as LatLngTuple, - spatialRecord.taxa_data, - datasetVisibility - ); - if (marker) { - occurrencesMarkerLayer.markers.push(marker); - } - } - - if (isBoundaryFeature(feature)) { - // check if dataset has been toggled - const ids = getSubmissionSpatialComponentIds(spatialRecord); - const key = makeKeyFromIds(ids); - if (ids.length > 0) { - visible = datasetVisibility[key] === undefined ? true : datasetVisibility[key]; - } - - if (visible) { - boundaryStaticLayer.features.push({ - geoJSON: feature, - key: feature.id, - popup: - }); - } - } - - if (isBoundaryCentroidFeature(feature)) { - // check if dataset has been toggled - const ids = getSubmissionSpatialComponentIds(spatialRecord); - const key = makeKeyFromIds(ids); - if (ids.length > 0) { - visible = datasetVisibility[key] === undefined ? true : datasetVisibility[key]; - } - - if (visible) { - boundaryStaticLayer.features.push({ - geoJSON: feature, - key: feature.id || feature.properties.datasetID, - popup: - }); - } - } - } - } - - return { markerLayers: [occurrencesMarkerLayer], staticLayers: [boundaryStaticLayer] }; -}; - -/** - * Helper function to *consistently* make React keys from an array of submission_spatial_component_ids. - * @param submissionSpatialComponentIds A list of IDs - * @returns A string joining all the id's by a seperator - */ -const makeKeyFromIds = (submissionSpatialComponentIds: number[]): string => { - return submissionSpatialComponentIds.join('-'); -}; - -/** - * Gleans submission spatial component IDs from a spatial component. - * @param spatialRecord A spatial component record - * @returns an array of submission_spatial_component_ids - */ -const getSubmissionSpatialComponentIds = (spatialRecord: ISpatialData): number[] => { - return spatialRecord.taxa_data.map((record) => record.submission_spatial_component_id); -}; - -/** - * Takes a geographic point, an array of taxonomy data, and a Record denoting dataset visibility, - * and produces an IMarker whose FeaturePopup contains submission spatial component IDs - * commensurate with the given dataset visibility. - * @param latLng The geograhic point of the marker - * @param taxaData The taxonomic data for the point (namely submission_spatial_component_ids) - * @param datasetVisibility The dataset visiblity record - * @returns An IMarker - */ -const occurrenceMarkerSetup = ( - latLng: LatLngTuple, - taxaData: ITaxaData[], - datasetVisibility: IDatasetVisibility -): IMarker | null => { - const submission_ids: number[] = taxaData - .filter((item: ITaxaData) => { - const taxonId = item.taxon_id || UNKNOWN_TAXON; - - return datasetVisibility[taxonId] === undefined ? true : datasetVisibility[taxonId]; - }) - .map((item: ITaxaData) => item.submission_spatial_component_id); - - if (submission_ids.length > 0) { - return { - position: latLng, - key: makeKeyFromIds(submission_ids), - popup: , - count: submission_ids.length - }; - } - - return null; -}; - -/** - * Takes an array of ISpatialData and maps it to an ISearchResult array denoting visibility based - * on the given datasetVisibility Record. - * @param data The array of spatial data - * @param datasetVisibility a Record denoting dataset visiblity - * @returns an array of type ISearchResult - */ -export const parseBoundaryCentroidResults = ( - data: ISpatialData[], - datasetVisibility: IDatasetVisibility -): ISearchResult[] => { - const results: ISearchResult[] = []; - data.forEach((spatialData: ISpatialData) => { - if (isBoundaryCentroidFeature(spatialData.spatial_data.features[0])) { - const key = makeKeyFromIds(getSubmissionSpatialComponentIds(spatialData)); - results.push({ - key: key, - name: `${spatialData.spatial_data?.features[0]?.properties?.datasetTitle}`, - count: 0, - visible: datasetVisibility[key] !== undefined ? datasetVisibility[key] : true - }); - } - }); - - return results; -}; - -/** - * Takes an array of ISpatialData and maps it to an ISearchResult array denoting visibility based - * on the given datasetVisibility Record, while numerating the count of each species. - * @param data The array of spatial data - * @param datasetVisibility a Record denoting dataset visiblity - * @returns an array of type ISearchResult - */ -export const parseOccurrenceResults = ( - data: ISpatialData[], - datasetVisibility: IDatasetVisibility -): ISearchResult[] => { - const taxaMap: Record = {}; - data.forEach((spatialData) => { - spatialData.taxa_data.forEach((item) => { - // need to check if it is an occurrence or not - if (isOccurrenceFeature(spatialData.spatial_data.features[0])) { - const taxonId = item.taxon_id || UNKNOWN_TAXON; - const taxonName = item.vernacular_name || UNKNOWN_TAXON; - - if (taxaMap[taxonId] === undefined) { - taxaMap[taxonId] = { - key: taxonId, - name: `${taxonName} (${taxonId})`, - count: 0, - visible: datasetVisibility[taxonId] !== undefined ? datasetVisibility[taxonId] : true - }; - } - - taxaMap[taxonId].count++; - } - }); - }); - - return Object.values(taxaMap); -}; - -/** - * Checks if `obj` is an object with no keys (aka: an empty object) - */ -export const isEmptyObject = (obj: any): obj is EmptyObject => { - return !!(isObject(obj) && !Object.keys(obj).length); -}; - -/** - * Asserts if a feature is an OccurrenceFeature. - */ -export const isOccurrenceFeature = (feature: Feature): feature is OccurrenceFeature => { - return feature.geometry.type === 'Point' && feature.properties?.['type'] === SPATIAL_COMPONENT_TYPE.OCCURRENCE; -}; - -/** - * Asserts if a feature is an BoundaryFeature. - */ -export const isBoundaryFeature = (feature: Feature): feature is BoundaryFeature => { - return feature?.properties?.['type'] === SPATIAL_COMPONENT_TYPE.BOUNDARY; -}; - -/** - * Asserts if a feature is an BoundaryCentroidFeature. - */ -export const isBoundaryCentroidFeature = (feature: Feature): feature is BoundaryCentroidFeature => { - return feature?.properties?.['type'] === SPATIAL_COMPONENT_TYPE.BOUNDARY_CENTROID; -}; diff --git a/database/liquibase/changelog.xml b/database/liquibase/changelog.xml deleted file mode 100644 index e69de29bb..000000000 diff --git a/database/liquibase/docker-compose.yml b/database/liquibase/docker-compose.yml deleted file mode 100644 index fd756bfb8..000000000 --- a/database/liquibase/docker-compose.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: "3.5" - -services: - ## Build liquibase docker image - lquibase: - image: liquibase/liquibase:latest - container_name: liquibase-container - environment: - - LIQUIBASE_COMMAND_USERNAME=${LIQUIBASE_COMMAND_USERNAME} - - LIQUIBASE_COMMAND_PASSWORD=${LIQUIBASE_COMMAND_PASSWORD} - - LIQUIBASE_COMMAND_URL=${LIQUIBASE_COMMAND_URL} - - LIQUIBASE_PRO_LICENSE_KEY=${LIQUIBASE_PRO_LICENSE_KEY} - - LIQUIBASE_COMMAND_CHANGELOG_FILE=${LIQUIBASE_COMMAND_CHANGELOG_FILE} - networks: - - local-network - volumes: - - liquibase:/liquibase/changelog - command: ["update-sql"] -networks: - local-network: - driver: bridge - -volumes: - liquibase: - name: liquibase-vol diff --git a/database/liquibase/liquibase.docker.properties b/database/liquibase/liquibase.docker.properties deleted file mode 100644 index 4de906d4f..000000000 --- a/database/liquibase/liquibase.docker.properties +++ /dev/null @@ -1,6 +0,0 @@ -classpath: /liquibase/changelog -url: jdbc:postgresql://localhost:5432/restoration-tracker?currentSchema=restoration -changeLogFile: changelog.xml -username: postgres -password: postgres -liquibaseProLicenseKey= diff --git a/database/package-lock.json b/database/package-lock.json index 2148757cf..958af2e67 100644 --- a/database/package-lock.json +++ b/database/package-lock.json @@ -14,20 +14,21 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "dev": true }, "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "dependencies": { "chalk": { @@ -90,9 +91,9 @@ } }, "@faker-js/faker": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.2.0.tgz", - "integrity": "sha512-VacmzZqVxdWdf9y64lDOMZNDMM/FQdtM9IsaOPKOm2suYwEatb8VkdHqOzXcDnZbk7YDE2BmsJmy/2Hmkn563g==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", "dev": true }, "@humanwhocodes/config-array": { @@ -139,9 +140,9 @@ } }, "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "@tsconfig/node12": { @@ -163,9 +164,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/node": { @@ -202,9 +203,9 @@ }, "dependencies": { "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -270,9 +271,9 @@ }, "dependencies": { "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -313,9 +314,9 @@ "dev": true }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true }, "ajv": { @@ -367,13 +368,13 @@ } }, "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" } }, "array-union": { @@ -382,6 +383,22 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + } + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -389,10 +406,13 @@ "dev": true }, "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "requires": { + "possible-typed-array-names": "^1.0.0" + } }, "balanced-match": { "version": "1.0.2", @@ -425,13 +445,16 @@ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" } }, "callsites": { @@ -503,7 +526,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "colorette": { @@ -519,7 +542,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "core-util-is": { @@ -545,6 +568,39 @@ "which": "^2.0.1" } }, + "data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -559,12 +615,24 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } @@ -600,12 +668,13 @@ "dev": true }, "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" } }, "error-ex": { @@ -618,56 +687,92 @@ } }, "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.15" + } + }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" } }, "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" } }, "es-to-primitive": { @@ -682,9 +787,9 @@ } }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" }, "escape-string-regexp": { "version": "4.0.0", @@ -764,9 +869,9 @@ "dev": true }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -906,9 +1011,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -927,13 +1032,13 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -958,19 +1063,20 @@ } }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "for-each": { @@ -995,30 +1101,30 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" } }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, "functions-have-names": { @@ -1048,15 +1154,16 @@ } }, "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" } }, "get-package-type": { @@ -1071,13 +1178,14 @@ "dev": true }, "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" } }, "getopts": { @@ -1109,21 +1217,22 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "requires": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" } }, "globby": { @@ -1155,14 +1264,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -1172,22 +1273,22 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "requires": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" } }, "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true }, "has-symbols": { @@ -1197,12 +1298,20 @@ "dev": true }, "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "requires": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" } }, "hosted-git-info": { @@ -1212,9 +1321,9 @@ "dev": true }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "import-fresh": { @@ -1238,13 +1347,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -1258,13 +1367,13 @@ "dev": true }, "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" } }, @@ -1274,20 +1383,19 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" }, "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" } }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-bigint": { @@ -1316,11 +1424,20 @@ "dev": true }, "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, "requires": { - "has": "^1.0.3" + "is-typed-array": "^1.1.13" } }, "is-date-object": { @@ -1335,7 +1452,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -1354,9 +1471,9 @@ } }, "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true }, "is-number": { @@ -1385,12 +1502,12 @@ } }, "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "requires": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" } }, "is-string": { @@ -1412,16 +1529,12 @@ } }, "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.14" } }, "is-weakref": { @@ -1442,7 +1555,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "js-tokens": { @@ -1461,6 +1574,12 @@ "esprima": "^4.0.0" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -1476,7 +1595,7 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "jsonparse": { @@ -1485,6 +1604,15 @@ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "knex": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz", @@ -1519,7 +1647,7 @@ "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -1542,7 +1670,7 @@ "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lru-cache": { @@ -1563,7 +1691,7 @@ "memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true }, "merge2": { @@ -1599,7 +1727,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "nice-try": { @@ -1670,19 +1798,19 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -1691,7 +1819,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "which": { @@ -1706,9 +1834,9 @@ } }, "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true }, "object-keys": { @@ -1718,13 +1846,13 @@ "dev": true }, "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } @@ -1732,16 +1860,16 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -1749,7 +1877,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" } }, "packet-reader": { @@ -1769,7 +1897,7 @@ "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "requires": { "error-ex": "^1.3.1", @@ -1779,7 +1907,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -1825,14 +1953,14 @@ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, "pg-pool": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.0.tgz", - "integrity": "sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==" }, "pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "pg-types": { "version": "2.2.0", @@ -1854,6 +1982,12 @@ "split2": "^4.1.0" } }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1869,7 +2003,13 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true }, "postgres-array": { @@ -1880,7 +2020,7 @@ "postgres-bytea": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==" }, "postgres-date": { "version": "1.0.7", @@ -1935,9 +2075,9 @@ "dev": true }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "queue-microtask": { @@ -1949,7 +2089,7 @@ "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -1992,14 +2132,15 @@ } }, "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" } }, "regexpp": { @@ -2015,11 +2156,11 @@ "dev": true }, "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -2053,6 +2194,26 @@ "queue-microtask": "^1.2.2" } }, + "safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2060,20 +2221,46 @@ "dev": true }, "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" } }, "semver": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", - "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + "integrity": "sha512-VyFUffiBx8hABJ9HYSTXLRwyZtdDHMzMtFmID1aiNAD2BZppBmJm0Hqw3p2jkgxP9BNt1pQ9RnC49P0EcXf6cA==" + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } }, "shebang-command": { "version": "2.0.0", @@ -2097,14 +2284,15 @@ "dev": true }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" } }, "slash": { @@ -2161,9 +2349,9 @@ } }, "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "spdx-expression-parse": { @@ -2177,9 +2365,9 @@ } }, "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "split2": { @@ -2190,7 +2378,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "string-width": { @@ -2205,47 +2393,49 @@ } }, "string.prototype.padend": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", - "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" } }, "string_decoder": { @@ -2269,7 +2459,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-json-comments": { @@ -2293,9 +2483,9 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -2306,15 +2496,15 @@ }, "dependencies": { "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js": "^4.4.1" } }, "json-schema-traverse": { @@ -2333,7 +2523,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { @@ -2377,9 +2567,9 @@ }, "dependencies": { "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true } } @@ -2414,15 +2604,56 @@ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true }, + "typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "requires": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" } }, "typescript": { @@ -2458,9 +2689,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, "validate-npm-package-license": { @@ -2496,29 +2727,28 @@ } }, "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.2" } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "xtend": { diff --git a/docker-compose.yml b/docker-compose.yml index e52180c32..08a2e4dc3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,9 +42,6 @@ services: environment: - NODE_ENV=${NODE_ENV} - NODE_OPTIONS=${API_NODE_OPTIONS} - - ELASTICSEARCH_URL=${ELASTICSEARCH_URL} - - ELASTICSEARCH_EML_INDEX=${ELASTICSEARCH_EML_INDEX} - - ELASTICSEARCH_TAXONOMY_INDEX=${ELASTICSEARCH_TAXONOMY_INDEX} # ITIS API - ITIS_SOLR_URL=${ITIS_SOLR_URL} - S3_KEY_PREFIX=${S3_KEY_PREFIX} @@ -111,9 +108,6 @@ services: dockerfile: ./.docker/queue/Dockerfile environment: - NODE_ENV=${NODE_ENV} - - ELASTICSEARCH_URL=${ELASTICSEARCH_URL} - - ELASTICSEARCH_EML_INDEX=${ELASTICSEARCH_EML_INDEX} - - ELASTICSEARCH_TAXONOMY_INDEX=${ELASTICSEARCH_TAXONOMY_INDEX} - S3_KEY_PREFIX=${S3_KEY_PREFIX} - API_HOST=${API_HOST} - API_PORT=${API_PORT} diff --git a/env_config/env.docker b/env_config/env.docker index d50d74799..13e28f305 100644 --- a/env_config/env.docker +++ b/env_config/env.docker @@ -143,13 +143,6 @@ GCNOTIFY_ONBOARDING_REQUEST_SMS_TEMPLATE=af2f1e40-bd72-4612-9c5a-567ee5b26ca5 GCNOTIFY_EMAIL_URL=https://api.notification.canada.ca/v2/notifications/email GCNOTIFY_SMS_URL=https://api.notification.canada.ca/v2/notifications/sms -# ------------------------------------------------------------------------------ -# Elasticsearch Platform API -# ------------------------------------------------------------------------------ -ELASTICSEARCH_URL=https://elasticsearch-a0ec71-dev.apps.silver.devops.gov.bc.ca -ELASTICSEARCH_EML_INDEX=eml -ELASTICSEARCH_TAXONOMY_INDEX=taxonomy_3.0.0 - # ------------------------------------------------------------------------------ # ITIS Platform API # ------------------------------------------------------------------------------ From 80c57ce233b9526e8e866af601eed12eec4cbf04 Mon Sep 17 00:00:00 2001 From: Nick Phura Date: Wed, 1 May 2024 16:01:05 -0700 Subject: [PATCH 6/7] Remove more unused code --- .../{submissionId}/download/index.ts | 2 +- .../{submissionId}/features/index.ts | 2 +- .../{submissionFeatureId}/signed-url.ts | 2 +- .../published/download/index.ts | 2 +- api/src/queue/queue-registry.test.ts | 9 +-- api/src/queue/queue-registry.ts | 2 - api/src/queue/queue.ts | 4 +- .../submission-job-queue-repository.test.ts | 31 ---------- .../submission-repository.test.ts | 61 +------------------ api/src/repositories/submission-repository.ts | 55 ----------------- api/src/services/submission-service.test.ts | 21 ------- api/src/services/submission-service.ts | 14 ----- app/src/hooks/api/useDatasetApi.test.ts | 37 ----------- app/src/hooks/api/useDatasetApi.ts | 53 ---------------- app/src/hooks/api/useSubmissionsApi.test.ts | 11 ---- app/src/hooks/api/useSubmissionsApi.ts | 14 ----- app/src/hooks/useApi.ts | 4 -- app/src/layouts/BaseLayout.test.tsx | 2 - 18 files changed, 10 insertions(+), 316 deletions(-) delete mode 100644 app/src/hooks/api/useDatasetApi.test.ts delete mode 100644 app/src/hooks/api/useDatasetApi.ts diff --git a/api/src/paths/submission/{submissionId}/download/index.ts b/api/src/paths/submission/{submissionId}/download/index.ts index 1140eddc2..a4fbc2732 100644 --- a/api/src/paths/submission/{submissionId}/download/index.ts +++ b/api/src/paths/submission/{submissionId}/download/index.ts @@ -11,7 +11,7 @@ export const GET: Operation = [downloadSubmission()]; GET.apiDoc = { description: 'Downloads a submission record and all associated features from the submission table', - tags: ['eml'], + tags: ['submission'], security: [ { OptionalBearer: [] diff --git a/api/src/paths/submission/{submissionId}/features/index.ts b/api/src/paths/submission/{submissionId}/features/index.ts index 6a7de1b49..62c87f98a 100644 --- a/api/src/paths/submission/{submissionId}/features/index.ts +++ b/api/src/paths/submission/{submissionId}/features/index.ts @@ -11,7 +11,7 @@ export const GET: Operation = [getSubmissionFeatures()]; GET.apiDoc = { description: 'Retrieves a submission record from the submission table', - tags: ['eml'], + tags: ['submission'], security: [ { OptionalBearer: [] diff --git a/api/src/paths/submission/{submissionId}/features/{submissionFeatureId}/signed-url.ts b/api/src/paths/submission/{submissionId}/features/{submissionFeatureId}/signed-url.ts index eaec2742d..d782a6e9c 100644 --- a/api/src/paths/submission/{submissionId}/features/{submissionFeatureId}/signed-url.ts +++ b/api/src/paths/submission/{submissionId}/features/{submissionFeatureId}/signed-url.ts @@ -12,7 +12,7 @@ export const GET: Operation = [getSubmissionFeatureSignedUrl()]; GET.apiDoc = { description: 'Retrieves a signed url of a submission feature', - tags: ['eml'], + tags: ['submission'], security: [ { OptionalBearer: [] diff --git a/api/src/paths/submission/{submissionId}/published/download/index.ts b/api/src/paths/submission/{submissionId}/published/download/index.ts index 92e75f569..5e7afb692 100644 --- a/api/src/paths/submission/{submissionId}/published/download/index.ts +++ b/api/src/paths/submission/{submissionId}/published/download/index.ts @@ -11,7 +11,7 @@ export const GET: Operation = [downloadPublishedSubmission()]; GET.apiDoc = { description: 'Downloads a submission record and all associated features from the submission table', - tags: ['eml'], + tags: ['submission'], security: [ { OptionalBearer: [] diff --git a/api/src/queue/queue-registry.test.ts b/api/src/queue/queue-registry.test.ts index 7e6d9aaa7..8e994605b 100644 --- a/api/src/queue/queue-registry.test.ts +++ b/api/src/queue/queue-registry.test.ts @@ -5,16 +5,9 @@ import * as db from '../database/db'; import { ISubmissionJobQueueRecord } from '../repositories/submission-job-queue-repository'; import { SubmissionJobQueueService } from '../services/submission-job-queue-service'; import { getMockDBConnection } from '../__mocks__/db'; -import { DWC_DATASET_SUBMISSION_JOB, jobQueueAttemptsWrapper, QueueJobRegistry } from './queue-registry'; +import { jobQueueAttemptsWrapper, QueueJobRegistry } from './queue-registry'; describe('QueueJobRegistry', () => { - it.skip('returns a known job function', () => { - const job = QueueJobRegistry.findMatchingJob(DWC_DATASET_SUBMISSION_JOB); - - expect(job).not.to.be.undefined; - expect(typeof job).to.equal('function'); - }); - it('returns null if no matching job found', () => { const job = QueueJobRegistry.findMatchingJob('not_a_real_job'); diff --git a/api/src/queue/queue-registry.ts b/api/src/queue/queue-registry.ts index cb9620cf0..b11549ade 100644 --- a/api/src/queue/queue-registry.ts +++ b/api/src/queue/queue-registry.ts @@ -7,8 +7,6 @@ const defaultLog = getLogger('queue/queue-registry'); type QueueJob = (jobQueueRecord: ISubmissionJobQueueRecord) => Promise; -export const DWC_DATASET_SUBMISSION_JOB = 'dwc_dataset_submission_job'; - /** * Translates a string to a queue-job compatible function. */ diff --git a/api/src/queue/queue.ts b/api/src/queue/queue.ts index 860182b57..6b852e141 100644 --- a/api/src/queue/queue.ts +++ b/api/src/queue/queue.ts @@ -1,6 +1,6 @@ import fastq from 'fastq'; import { ISubmissionJobQueueRecord } from '../repositories/submission-job-queue-repository'; -import { DWC_DATASET_SUBMISSION_JOB, QueueJobRegistry } from './queue-registry'; +import { QueueJobRegistry } from './queue-registry'; import { QUEUE_DEFAULT_CONCURRENCY, QUEUE_DEFAULT_TIMEOUT } from './queue-scheduler'; export class Queue { @@ -39,7 +39,7 @@ export class Queue { * @memberof Queue */ async _queueWorker(jobQueueRecord: ISubmissionJobQueueRecord) { - const job = QueueJobRegistry.findMatchingJob(DWC_DATASET_SUBMISSION_JOB); + const job = QueueJobRegistry.findMatchingJob('placeholder-not_a_real_job'); if (!job) { throw new Error('Failed to find matching queue job handler'); diff --git a/api/src/repositories/submission-job-queue-repository.test.ts b/api/src/repositories/submission-job-queue-repository.test.ts index bdb08e82c..380859663 100644 --- a/api/src/repositories/submission-job-queue-repository.test.ts +++ b/api/src/repositories/submission-job-queue-repository.test.ts @@ -73,37 +73,6 @@ describe('SubmissionJobQueueRepository', () => { }); }); - describe('getSourceTransformIdForUserId', () => { - it('should return with transform id', async () => { - const mockQueryResponse = { rowCount: 1, rows: [{ source_transform_id: 3 }] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - const repo = new SubmissionJobQueueRepository(mockDBConnection); - - const transformId = await repo.getSourceTransformIdForUserId(1); - expect(transformId).to.be.eql(3); - }); - - it('should throw an error when no transforms are found', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ - sql: () => mockQueryResponse - }); - - const repo = new SubmissionJobQueueRepository(mockDBConnection); - - try { - await repo.getSourceTransformIdForUserId(1); - expect.fail(); - } catch (error) { - expect((error as ApiGeneralError).message).to.equal('Failed to get source transform Id'); - } - }); - }); - describe('getNextUnprocessedJobQueueRecords', () => { it('should return all matching rows with no optional parameters', async () => { const mockQueryResponse = { diff --git a/api/src/repositories/submission-repository.test.ts b/api/src/repositories/submission-repository.test.ts index 79e98b991..edf5dffec 100644 --- a/api/src/repositories/submission-repository.test.ts +++ b/api/src/repositories/submission-repository.test.ts @@ -10,6 +10,7 @@ import { SECURITY_APPLIED_STATUS } from './security-repository'; import { ISourceTransformModel, ISpatialComponentCount, + ISubmissionModel, PatchSubmissionRecord, SubmissionFeatureRecord, SubmissionRecord, @@ -86,14 +87,8 @@ describe('SubmissionRepository', () => { }); it('should succeed with valid data', async () => { - const mockResponse = { - source_transform_id: 'test', - input_file_name: 'test', - input_key: 'test', - record_effective_date: 'test', - eml_source: 'test', - darwin_core_source: 'test', - uuid: 'test' + const mockResponse: ISubmissionModel = { + uuid: '123-456-789' }; const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; @@ -423,56 +418,6 @@ describe('SubmissionRepository', () => { }); }); - describe('insertSubmissionObservationRecord', () => { - afterEach(() => { - sinon.restore(); - }); - - it('should throw an error when insert sql fails', async () => { - const mockQueryResponse = { rowCount: 0 } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const submissionData = { - submission_id: 1, - darwin_core_source: '', - submission_security_request: '', - foi_reason: '' - }; - try { - await submissionRepository.insertSubmissionObservationRecord(submissionData); - expect.fail(); - } catch (actualError) { - expect((actualError as ApiGeneralError).message).to.equal('Failed to insert submission observation record'); - } - }); - - it('should succeed with valid data', async () => { - const mockResponse = { - id: 1 - }; - - const mockQueryResponse = { rowCount: 1, rows: [mockResponse] } as any as Promise>; - - const mockDBConnection = getMockDBConnection({ sql: () => mockQueryResponse }); - - const submissionRepository = new SubmissionRepository(mockDBConnection); - - const submissionData = { - submission_id: 1, - darwin_core_source: '', - submission_security_request: '', - foi_reason: '' - }; - - const response = await submissionRepository.insertSubmissionObservationRecord(submissionData); - - expect(response).to.eql(mockResponse); - }); - }); - describe('getUnreviewedSubmissionsForAdmins', () => { beforeEach(() => { sinon.restore(); diff --git a/api/src/repositories/submission-repository.ts b/api/src/repositories/submission-repository.ts index 6d45bcc78..fa4c32e3b 100644 --- a/api/src/repositories/submission-repository.ts +++ b/api/src/repositories/submission-repository.ts @@ -186,20 +186,6 @@ export interface ISubmissionJobQueueRecord { revision_count: number; } -export interface ISubmissionMetadataRecord { - submission_metadata_id?: number; - submission_id: number; - eml_source: string; - eml_json_source?: any; - record_effective_timestamp?: string | null; - record_end_timestamp?: string | null; - create_date?: string; - create_user?: string; - update_date?: string; - update_user?: string; - revision_count?: string; -} - export interface ISubmissionObservationRecord { submission_observation_id?: number; submission_id: number; @@ -736,47 +722,6 @@ export class SubmissionRepository extends BaseRepository { return response.rows[0]; } - /** - * Insert a new Observation Record - * - * @param {ISubmissionObservationRecord} submissionObservation - * @return {*} {Promise<{ submission_observation_id: number }>} - * @memberof SubmissionRepository - */ - async insertSubmissionObservationRecord( - submissionObservation: ISubmissionObservationRecord - ): Promise<{ submission_observation_id: number }> { - const sqlStatement = SQL` - INSERT INTO submission_observation ( - submission_id, - darwin_core_source, - submission_security_request, - foi_reason, - record_effective_timestamp - ) VALUES ( - ${submissionObservation.submission_id}, - ${submissionObservation.darwin_core_source}, - ${submissionObservation.submission_security_request}, - ${submissionObservation.foi_reason}, - now() - ) - RETURNING - submission_observation_id - ; - `; - - const response = await this.connection.sql<{ submission_observation_id: number }>(sqlStatement); - - if (!response.rowCount) { - throw new ApiExecuteSQLError('Failed to insert submission observation record', [ - 'SubmissionRepository->insertSubmissionObservationRecord', - 'rowCount was null or undefined, expected rowCount >= 0' - ]); - } - - return response.rows[0]; - } - /** * Get all submissions that have not completed security review. * diff --git a/api/src/services/submission-service.test.ts b/api/src/services/submission-service.test.ts index f38e504f4..dfd868c1f 100644 --- a/api/src/services/submission-service.test.ts +++ b/api/src/services/submission-service.test.ts @@ -9,7 +9,6 @@ import { ISubmissionFeature, ISubmissionJobQueueRecord, ISubmissionModel, - ISubmissionObservationRecord, PatchSubmissionRecord, SubmissionFeatureDownloadRecord, SubmissionFeatureRecord, @@ -401,26 +400,6 @@ describe('SubmissionService', () => { }); }); - describe('insertSubmissionObservationRecord', () => { - it('should return a submission observation record', async () => { - const mockDBConnection = getMockDBConnection(); - const submissionService = new SubmissionService(mockDBConnection); - - const repo = sinon.stub(SubmissionRepository.prototype, 'insertSubmissionObservationRecord').resolves({ - submission_observation_id: 1 - }); - - const response = await submissionService.insertSubmissionObservationRecord({ - test: 'test' - } as unknown as ISubmissionObservationRecord); - - expect(repo).to.be.calledOnce; - expect(response).to.be.eql({ - submission_observation_id: 1 - }); - }); - }); - describe('getUnreviewedSubmissionsForAdmins', () => { it('should return an array of submission records', async () => { const mockSubmissionRecords: SubmissionRecordWithSecurityAndRootFeatureType[] = [ diff --git a/api/src/services/submission-service.ts b/api/src/services/submission-service.ts index f56254366..29f1c0df6 100644 --- a/api/src/services/submission-service.ts +++ b/api/src/services/submission-service.ts @@ -6,7 +6,6 @@ import { ISubmissionFeature, ISubmissionJobQueueRecord, ISubmissionModel, - ISubmissionObservationRecord, ISubmissionRecord, PatchSubmissionRecord, SubmissionFeatureDownloadRecord, @@ -262,19 +261,6 @@ export class SubmissionService extends DBService { return this.submissionRepository.getSubmissionJobQueue(submissionId); } - /** - * Insert a new Observation Record - * - * @param {ISubmissionObservationRecord} submissionObservation - * @return {*} {Promise<{ submission_observation_id: number }>} - * @memberof SubmissionService - */ - async insertSubmissionObservationRecord( - submissionObservation: ISubmissionObservationRecord - ): Promise<{ submission_observation_id: number }> { - return this.submissionRepository.insertSubmissionObservationRecord(submissionObservation); - } - /** * Get all submissions that are pending security review (are unreviewed). * diff --git a/app/src/hooks/api/useDatasetApi.test.ts b/app/src/hooks/api/useDatasetApi.test.ts deleted file mode 100644 index e7f01a331..000000000 --- a/app/src/hooks/api/useDatasetApi.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; -import useDatasetApi from './useDatasetApi'; - -describe('useDatasetApi', () => { - let mock: any; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - it('getDatasetArtifacts works as expected', async () => { - const response = { - artifacts: [{ artifact_id: 1 }, { artifact_id: 2 }] - }; - - mock.onGet('api/dwc/submission/a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf/artifacts').reply(200, response); - - const actualResult = await useDatasetApi(axios).getDatasetArtifacts('a6f90fb7-2f20-4d6e-b1cd-75f3336c2dcf'); - - expect(actualResult).toEqual({ - artifacts: [{ artifact_id: 1 }, { artifact_id: 2 }] - }); - }); - - it('getArtifactSignedUrl works as expected', async () => { - mock.onGet(`api/artifact/${1}/getSignedUrl`).reply(200, 'http://example.com'); - - const actualResult = await useDatasetApi(axios).getArtifactSignedUrl(1); - - expect(actualResult).toEqual('http://example.com'); - }); -}); diff --git a/app/src/hooks/api/useDatasetApi.ts b/app/src/hooks/api/useDatasetApi.ts deleted file mode 100644 index b1b7905d2..000000000 --- a/app/src/hooks/api/useDatasetApi.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { AxiosInstance } from 'axios'; -import { IArtifact } from 'interfaces/useDatasetApi.interface'; - -/** - * Returns a set of supported api methods for working with datasets. - * - * @param {AxiosInstance} axios - * @return {*} object whose properties are supported api methods. - */ -const useDatasetApi = (axios: AxiosInstance) => { - /** - * Fetch dataset data by datasetUUID. - * - * @param {string} datasetUUID - * @return {*} {Promise} - */ - const getDataset = async (datasetUUID: string): Promise => { - const { data } = await axios.get(`api/dataset/${datasetUUID}`); - - return data; - }; - - /** - * Fetch dataset artifacts by datasetId. - * - * @param {string} datasetId - * @return {*} {Promise} - */ - const getDatasetArtifacts = async (datasetId: string): Promise => { - const { data } = await axios.get(`api/dwc/submission/${datasetId}/artifacts`); - - return data; - }; - - /** - * Fetch the signed URL of an artifact by its artifact ID. - * - * @return {*} {Promise} - */ - const getArtifactSignedUrl = async (artifactId: number): Promise => { - const { data } = await axios.get(`api/artifact/${artifactId}/getSignedUrl`); - - return data; - }; - - return { - getDataset, - getDatasetArtifacts, - getArtifactSignedUrl - }; -}; - -export default useDatasetApi; diff --git a/app/src/hooks/api/useSubmissionsApi.test.ts b/app/src/hooks/api/useSubmissionsApi.test.ts index 3928c102e..41ae2c0e1 100644 --- a/app/src/hooks/api/useSubmissionsApi.test.ts +++ b/app/src/hooks/api/useSubmissionsApi.test.ts @@ -13,17 +13,6 @@ describe('useSubmissionApi', () => { mock.restore(); }); - it('getSignedUrl works as expected', async () => { - const res = 'test-signed-url'; - const testSubmissionId = 1; - - mock.onGet(`/api/dwc/submission/${testSubmissionId}/getSignedUrl`).reply(200, res); - - const result = await useSubmissionsApi(axios).getSignedUrl(testSubmissionId); - - expect(result).toEqual('test-signed-url'); - }); - describe('getSubmissionFeatureSignedUrl', () => { it('should return signed URL', async () => { const payload = { diff --git a/app/src/hooks/api/useSubmissionsApi.ts b/app/src/hooks/api/useSubmissionsApi.ts index a73446780..00ba6beac 100644 --- a/app/src/hooks/api/useSubmissionsApi.ts +++ b/app/src/hooks/api/useSubmissionsApi.ts @@ -15,19 +15,6 @@ import { * @return {*} object whose properties are supported api methods. */ const useSubmissionsApi = (axios: AxiosInstance) => { - /** - * Fetch the signed URL of a submission by submission ID. - * - * @return {*} {Promise} - */ - const getSignedUrl = async (submissionId: number): Promise => { - const { data } = await axios.get(`/api/dwc/submission/${submissionId}/getSignedUrl`); - - return data; - }; - - /** NET-NEW FRONTEND REQUESTS FOR UPDATED SCHEMA **/ - /** * repackages and retrieves json data from self and each child under submission * @@ -157,7 +144,6 @@ const useSubmissionsApi = (axios: AxiosInstance) => { }; return { - getSignedUrl, getSubmissionDownloadPackage, getSubmissionPublishedDownloadPackage, getSubmissionFeatureGroups, diff --git a/app/src/hooks/useApi.ts b/app/src/hooks/useApi.ts index a2be22539..074a50f9f 100644 --- a/app/src/hooks/useApi.ts +++ b/app/src/hooks/useApi.ts @@ -4,7 +4,6 @@ import useAdminApi from './api/useAdminApi'; import useArtifactApi from './api/useArtifactApi'; import useAxios from './api/useAxios'; import useCodesApi from './api/useCodesApi'; -import useDatasetApi from './api/useDatasetApi'; import useSecurityApi from './api/useSecurityApi'; import useSubmissionsApi from './api/useSubmissionsApi'; import useTaxonomyApi from './api/useTaxonomyApi'; @@ -27,8 +26,6 @@ export const useApi = () => { const taxonomy = useTaxonomyApi(apiAxios); - const dataset = useDatasetApi(apiAxios); - const artifact = useArtifactApi(apiAxios); const security = useSecurityApi(apiAxios); @@ -39,7 +36,6 @@ export const useApi = () => { user, admin, submissions, - dataset, taxonomy, security, artifact, diff --git a/app/src/layouts/BaseLayout.test.tsx b/app/src/layouts/BaseLayout.test.tsx index a45ee1257..498a6a369 100644 --- a/app/src/layouts/BaseLayout.test.tsx +++ b/app/src/layouts/BaseLayout.test.tsx @@ -9,8 +9,6 @@ const history = createMemoryHistory(); describe.skip('BaseLayout', () => { it('renders correctly', () => { - process.env.REACT_APP_NODE_ENV = 'local'; - const authState = getMockAuthState({ base: SystemAdminAuthState }); const { getByText } = render( From 4180e4f4ca9dd99f61b0af0b8e9b9db93f96b4fe Mon Sep 17 00:00:00 2001 From: Nick Phura Date: Wed, 1 May 2024 16:03:18 -0700 Subject: [PATCH 7/7] Update pipeline settings --- api/.pipeline/config.js | 8 ++++---- app/.pipeline/config.js | 4 ++-- database/.pipeline/config.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/.pipeline/config.js b/api/.pipeline/config.js index 1c09c7c12..3ab7df0f9 100644 --- a/api/.pipeline/config.js +++ b/api/.pipeline/config.js @@ -116,7 +116,7 @@ const phases = { cpuLimit: '1250m', memoryRequest: '100Mi', memoryLimit: '4Gi', - replicas: '2', + replicas: '1', replicasMax: '2' }, prod: { @@ -139,10 +139,10 @@ const phases = { logLevel: 'warn', nodeOptions: '--max_old_space_size=6000', // 75% of memoryLimit (bytes) cpuRequest: '50m', - cpuLimit: '1750m', + cpuLimit: '1500m', memoryRequest: '100Mi', - memoryLimit: '8Gi', - replicas: '2', + memoryLimit: '6Gi', + replicas: '1', replicasMax: '2' } }; diff --git a/app/.pipeline/config.js b/app/.pipeline/config.js index f6dd04eb6..54e8fb829 100644 --- a/app/.pipeline/config.js +++ b/app/.pipeline/config.js @@ -111,7 +111,7 @@ const phases = { cpuLimit: '400m', memoryRequest: '100Mi', memoryLimit: '750Mi', - replicas: '2', + replicas: '1', replicasMax: '2' }, prod: { @@ -134,7 +134,7 @@ const phases = { cpuLimit: '500m', memoryRequest: '100Mi', memoryLimit: '1Gi', - replicas: '2', + replicas: '1', replicasMax: '2' } }; diff --git a/database/.pipeline/config.js b/database/.pipeline/config.js index f83e03428..0ee612f73 100644 --- a/database/.pipeline/config.js +++ b/database/.pipeline/config.js @@ -117,9 +117,9 @@ const phases = { dbSetupDockerfilePath: dbSetupDockerfilePath, volumeCapacity: '5Gi', cpuRequest: '50m', - cpuLimit: '2000m', + cpuLimit: '1500m', memoryRequest: '100Mi', - memoryLimit: '8Gi', + memoryLimit: '6Gi', replicas: '1' } };