From a26801e034d05727b524253144c6b717cecaa047 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 24 Feb 2026 13:14:50 +0100 Subject: [PATCH 01/46] init --- CI/E2E/.env.elastic-search | 6 -- CI/E2E/.env.open-search | 3 + CI/E2E/backend.env | 2 +- CI/E2E/docker-compose-local.yaml | 71 ++++++++++++------- package-lock.json | 52 ++++++++++---- package.json | 4 +- src/datasets/datasets.service.ts | 3 +- .../configuration/indexSetting.ts | 15 ++-- src/elastic-search/elastic-search.service.ts | 47 ++++++------ src/elastic-search/helpers/utils.ts | 20 +++--- .../interfaces/es-common.type.ts | 11 --- .../interfaces/mappingInterface.type.ts | 13 +--- .../providers/query-builder.service.ts | 9 ++- 13 files changed, 135 insertions(+), 121 deletions(-) delete mode 100644 CI/E2E/.env.elastic-search create mode 100644 CI/E2E/.env.open-search diff --git a/CI/E2E/.env.elastic-search b/CI/E2E/.env.elastic-search deleted file mode 100644 index d51156aa8..000000000 --- a/CI/E2E/.env.elastic-search +++ /dev/null @@ -1,6 +0,0 @@ -STACK_VERSION=8.8.2 -ES_PORT=9200 -CLUSTER_NAME=es-cluster -MEM_LIMIT=4G -ES_USERNAME=elastic -ES_PASSWORD=duo-password \ No newline at end of file diff --git a/CI/E2E/.env.open-search b/CI/E2E/.env.open-search new file mode 100644 index 000000000..0973fa08f --- /dev/null +++ b/CI/E2E/.env.open-search @@ -0,0 +1,3 @@ +MEM_LIMIT=4G +OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g +OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 \ No newline at end of file diff --git a/CI/E2E/backend.env b/CI/E2E/backend.env index c6166a164..9a294163b 100644 --- a/CI/E2E/backend.env +++ b/CI/E2E/backend.env @@ -39,7 +39,7 @@ SAMPLE_GROUPS="" ELASTICSEARCH_ENABLED='yes' ES_HOST=http://es01:9200 ES_USERNAME=elastic -ES_PASSWORD=duo-password +ES_PASSWORD=Scicat-password2026 MONGODB_COLLECTION=Dataset ES_MAX_RESULT=210000 ES_FIELDS_LIMIT=400000 diff --git a/CI/E2E/docker-compose-local.yaml b/CI/E2E/docker-compose-local.yaml index f4f7c8e82..b5a7a6b83 100644 --- a/CI/E2E/docker-compose-local.yaml +++ b/CI/E2E/docker-compose-local.yaml @@ -1,50 +1,67 @@ version: "3.2" services: mongodb: - image: bitnami/mongodb:latest + image: mongo:latest volumes: - - "mongodb_data:/bitnami" + - "mongodb_data://data/db" ports: - "27017:27017" - scichat-loopback: - image: dacat/scichat-loopback:e2e - command: - [ - "./wait-for-it.sh", - "mongodb:27017", - "--", - "node", - "-r", - "dotenv/config", - "." - ] - volumes: - - ".env.scichat-loopback:/home/node/app/.env" - depends_on: - - mongodb + # scichat-loopback: + # image: dacat/scichat-loopback:e2e + # command: + # [ + # "./wait-for-it.sh", + # "mongodb:27017", + # "--", + # "node", + # "-r", + # "dotenv/config", + # "." + # ] + # volumes: + # - ".env.scichat-loopback:/home/node/app/.env" + # depends_on: + # - mongodb - es01: + opensearch: depends_on: - mongodb - image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} + image: opensearchproject/opensearch:3.5.0 ports: - - ${ES_PORT}:9200 + - 9200:9200 environment: - - node.name=es01 - - ES_JAVA_OPTS=-Xms2g -Xmx2g - - cluster.name=${CLUSTER_NAME} - - cluster.initial_master_nodes=es01 - - ELASTIC_PASSWORD=${ES_PASSWORD} + - node.name=opensearch + - discovery.type=single-node + - cluster.name=os-cluster - bootstrap.memory_lock=true + - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - DISABLE_INSTALL_DEMO_CONFIG=false + volumes: + - opensearch_data:/usr/share/opensearch/data mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 + + # Web UI for OpenSearch + # Enable this service if you want to use OpenSearch Dashboards to visualize and debug your OpenSearch data. + # Access it at http://localhost:5601 + opensearch-dashboards: + image: opensearchproject/opensearch-dashboards:3.5.0 + ports: + - "5601:5601" + environment: + - OPENSEARCH_HOSTS=https://opensearch:9200 + - OPENSEARCH_USERNAME=admin + - OPENSEARCH_PASSWORD=admin + - OPENSEARCH_SSL_VERIFICATIONMODE=none + volumes: mongodb_data: driver: local - es01: + opensearch_data: driver: local diff --git a/package-lock.json b/package-lock.json index 137f27e98..4e144c869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "@nestjs/common": "^11", "@nestjs/config": "^4", "@nestjs/core": "^11", - "@nestjs/elasticsearch": "^11", "@nestjs/event-emitter": "^3", "@nestjs/jwt": "^11", "@nestjs/mongoose": "^11.0.4", @@ -26,6 +25,7 @@ "@nestjs/swagger": "^11.1.1", "@nestjs/terminus": "^11", "@nestjs/throttler": "^6.5.0", + "@opensearch-project/opensearch": "^3.5.1", "@types/json-merge-patch": "^1.0.0", "@types/jsonschema": "^1.1.1", "@user-office-software/duo-logger": "^2.1.1", @@ -2771,17 +2771,6 @@ } } }, - "node_modules/@nestjs/elasticsearch": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/elasticsearch/-/elasticsearch-11.1.0.tgz", - "integrity": "sha512-NwMakVs8LeXUksaSNp0ejhv223yVCK4w9iqMBrsonKj2gl4sBIBrAgJq/aXhD9bJCNLYb+waoRAsxuuPxYcjXw==", - "license": "MIT", - "peerDependencies": { - "@elastic/elasticsearch": "^7.4.0 || ^8.0.0 || ^9.0.0", - "@nestjs/common": "^10.0.0 || ^11.0.0", - "rxjs": "^7.2.0" - } - }, "node_modules/@nestjs/event-emitter": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", @@ -3155,6 +3144,30 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "optional": true }, + "node_modules/@opensearch-project/opensearch": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-3.5.1.tgz", + "integrity": "sha512-6bf+HcuERzAtHZxrm6phjref54ABse39BpkDie/YO3AUFMCBrb3SK5okKSdT5n3+nDRuEEQLhQCl0RQV3s1qpA==", + "license": "Apache-2.0", + "dependencies": { + "aws4": "^1.11.0", + "debug": "^4.3.1", + "hpagent": "^1.2.0", + "json11": "^2.0.0", + "ms": "^2.1.3", + "secure-json-parse": "^2.4.0" + }, + "engines": { + "node": ">=14", + "yarn": "^1.22.10" + } + }, + "node_modules/@opensearch-project/opensearch/node_modules/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==", + "license": "BSD-3-Clause" + }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -5032,6 +5045,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, "node_modules/axios": { "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", @@ -9911,6 +9930,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json11": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/json11/-/json11-2.0.2.tgz", + "integrity": "sha512-HIrd50UPYmP6sqLuLbFVm75g16o0oZrVfxrsY0EEys22klz8mRoWlX9KAEDOSOR9Q34rcxsyC8oDveGrCz5uLQ==", + "license": "MIT", + "bin": { + "json11": "dist/cli.mjs" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", diff --git a/package.json b/package.json index 2152819ec..2aa8ced15 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "test:api": "npm run test:api:jest --maxWorkers=50% && concurrently -k -s first \"wait-on http://localhost:3000/explorer/ && npm run test:api:mocha\" \"npm run start:test\"", "test:api:jest": "dotenv -o -e test/config/.env -e test/config/.env.override -- jest --config ./test/config/jest-e2e.json --maxWorkers=50%", "test:api:mocha": "dotenv -o -e test/config/.env -e test/config/.env.override -- mocha --config ./test/config/.mocharc.js -r chai/register-should.js", - "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml --env-file CI/E2E/.env.elastic-search up -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" + "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml --env-file CI/E2E/.env.open-search up -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" }, "dependencies": { "@casl/ability": "^6.3.2", @@ -41,7 +41,6 @@ "@nestjs/common": "^11", "@nestjs/config": "^4", "@nestjs/core": "^11", - "@nestjs/elasticsearch": "^11", "@nestjs/event-emitter": "^3", "@nestjs/jwt": "^11", "@nestjs/mongoose": "^11.0.4", @@ -50,6 +49,7 @@ "@nestjs/swagger": "^11.1.1", "@nestjs/terminus": "^11", "@nestjs/throttler": "^6.5.0", + "@opensearch-project/opensearch": "^3.5.1", "@types/json-merge-patch": "^1.0.0", "@types/jsonschema": "^1.1.1", "@user-office-software/duo-logger": "^2.1.1", diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 18d5b4196..c59e2ba0d 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -308,7 +308,6 @@ export class DatasetsService { modifiers.skip, modifiers.sort, ); - datasets = await this.datasetModel .find({ pid: { $in: esResult.data } }) .sort(modifiers.sort) @@ -602,6 +601,6 @@ export class DatasetsService { async isElasticSearchDBEmpty() { if (!this.ESClient) return; const count = await this.ESClient.getCount(); - return count.count > 0; + return count.body.count > 0; } } diff --git a/src/elastic-search/configuration/indexSetting.ts b/src/elastic-search/configuration/indexSetting.ts index 07ce39ac6..36250e30c 100644 --- a/src/elastic-search/configuration/indexSetting.ts +++ b/src/elastic-search/configuration/indexSetting.ts @@ -1,12 +1,5 @@ -import { - AnalysisEdgeNGramTokenizer, - AnalysisPatternReplaceCharFilter, - IndicesIndexSettingsAnalysis, - MappingDynamicTemplate, -} from "@elastic/elasticsearch/lib/api/types"; - //Tokenizers -export const autocomplete_tokenizer: AnalysisEdgeNGramTokenizer = { +export const autocomplete_tokenizer: any = { type: "edge_ngram", min_gram: 2, max_gram: 40, @@ -14,14 +7,14 @@ export const autocomplete_tokenizer: AnalysisEdgeNGramTokenizer = { }; //Filters -export const special_character_filter: AnalysisPatternReplaceCharFilter = { +export const special_character_filter: any = { pattern: "[^A-Za-z0-9]", type: "pattern_replace", replacement: "", }; //Dynamic templates -export const dynamic_template: Record[] = [ +export const dynamic_template: Record[] = [ // NOTE: date as keyword is temporary solution for date format inconsistency issue in the scientificMetadata field { date_as_keyword: { @@ -123,4 +116,4 @@ export const defaultElasticSettings = { autocomplete: autocomplete_tokenizer, }, }, -} as IndicesIndexSettingsAnalysis; +} as any; diff --git a/src/elastic-search/elastic-search.service.ts b/src/elastic-search/elastic-search.service.ts index f6743ec38..731bd6d11 100644 --- a/src/elastic-search/elastic-search.service.ts +++ b/src/elastic-search/elastic-search.service.ts @@ -5,14 +5,10 @@ import { Logger, OnModuleInit, } from "@nestjs/common"; +import { Client } from "@opensearch-project/opensearch"; -import { Client } from "@elastic/elasticsearch"; import { SearchQueryService } from "./providers/query-builder.service"; -import { - SearchRequest, - AggregationsAggregate, - SortOrder, -} from "@elastic/elasticsearch/lib/api/types"; + import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; import { defaultElasticSettings, @@ -32,6 +28,7 @@ import { } from "./helpers/utils"; import { SortFields } from "./providers/fields.enum"; +import { SortOrder } from "@opensearch-project/opensearch/api/_types/ml._common"; @Injectable() export class ElasticSearchService implements OnModuleInit { @@ -105,7 +102,7 @@ export class ElasticSearchService implements OnModuleInit { username: this.username, password: this.password, }, - tls: { + ssl: { rejectUnauthorized: false, }, }); @@ -146,10 +143,10 @@ export class ElasticSearchService implements OnModuleInit { body: { settings: defaultElasticSettings, mappings: { - dynamic: true, + dynamic: "true", dynamic_templates: dynamic_template, numeric_detection: false, - date_detection: true, + date_detection: false, dynamic_date_formats: [ "yyyy-MM-dd'T'HH:mm:ss|| yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd'T'HH:mm:ss.SSS'Z'||yyyy-MM-dd'T'HH:mm:ss.SSS", ], @@ -208,7 +205,6 @@ export class ElasticSearchService implements OnModuleInit { await this.esService.indices.putMapping({ index, - dynamic: true, body: { properties: datasetMappings, dynamic_templates: dynamic_template, @@ -270,14 +266,17 @@ export class ElasticSearchService implements OnModuleInit { const searchQuery = this.searchService.buildSearchQuery(searchParam); const searchOptions = { track_scores: true, - sort: [{ _score: { order: "desc" } }], + sort: [{ _score: { order: "desc" } }] as unknown as Record< + string, + unknown + >[], query: searchQuery.query, from: skip, size: limit, min_score: defaultMinScore, track_total_hits: true, _source: [""], - } as SearchRequest; + }; if (!isSortEmpty) { const sortField = Object.keys(sort)[0]; @@ -312,11 +311,16 @@ export class ElasticSearchService implements OnModuleInit { } } - const body = await this.esService.search(searchOptions); + const body = (await this.esService.search({ + index: this.defaultIndex, + body: searchOptions, + } as any)) as any; + + console.log("---body---", body.body.hits); - const totalCount = body.hits.hits.length || 0; + const totalCount = body.body.hits.hits.length || 0; - const data = body.hits.hits.map((item) => item._id || ""); + const data = body.body.hits.hits.map((item: any) => item._id || ""); return { totalCount, data, @@ -339,13 +343,14 @@ export class ElasticSearchService implements OnModuleInit { size: 0, aggs: facetPipeline, _source: [""], - } as SearchRequest; + }; - const body = await this.esService.search(searchOptions); + const body = (await this.esService.search({ + index: this.defaultIndex, + body: searchOptions, + } as any)) as any; - const transformedFacets = transformFacets( - body.aggregations as AggregationsAggregate, - ); + const transformedFacets = transformFacets(body.aggregations || {}); return transformedFacets; } catch (error) { @@ -370,7 +375,7 @@ export class ElasticSearchService implements OnModuleInit { await this.esService.index({ index: this.defaultIndex, id: data.pid, - document: transformedData, + body: transformedData, refresh: this.refresh, }); diff --git a/src/elastic-search/helpers/utils.ts b/src/elastic-search/helpers/utils.ts index 48817e625..5d6e9448a 100644 --- a/src/elastic-search/helpers/utils.ts +++ b/src/elastic-search/helpers/utils.ts @@ -1,7 +1,3 @@ -import { - AggregationsAggregate, - AggregationsFrequentItemSetsBucketKeys, -} from "@elastic/elasticsearch/lib/api/types"; import { DatasetClass } from "src/datasets/schemas/dataset.schema"; import { IFilter, @@ -10,6 +6,10 @@ import { ScientificQuery, } from "../interfaces/es-common.type"; import { isObject } from "lodash"; +import { + AggregateBase, + SingleBucketAggregateBase, +} from "@opensearch-project/opensearch/api/_types/_common.aggregations"; export const transformKey = (key: string): string => { return key.trim().replace(/[.]/g, "\\.").replace(/ /g, "_").toLowerCase(); @@ -231,19 +231,17 @@ export const convertToElasticSearchQuery = ( }; export const transformFacets = ( - aggregation: AggregationsAggregate, + aggregation: AggregateBase, ): Record[] => { const transformed = Object.entries(aggregation).reduce( (acc, [key, value]) => { const isBucketArray = Array.isArray(value.buckets); acc[key] = isBucketArray - ? value.buckets.map( - (bucket: AggregationsFrequentItemSetsBucketKeys) => ({ - _id: bucket.key, - count: bucket.doc_count, - }), - ) + ? value.buckets.map((bucket: SingleBucketAggregateBase) => ({ + _id: bucket.key, + count: bucket.doc_count, + })) : [{ totalSets: value.value }]; return acc; diff --git a/src/elastic-search/interfaces/es-common.type.ts b/src/elastic-search/interfaces/es-common.type.ts index e751ea0ca..7addfa65e 100644 --- a/src/elastic-search/interfaces/es-common.type.ts +++ b/src/elastic-search/interfaces/es-common.type.ts @@ -1,14 +1,3 @@ -export type searchType = - | "text" - | "keyword" - | "long" - | "integer" - | "date" - | "boolean" - | "object" - | "flattened" - | "nested"; - export type ObjectType = { begin: string; end: string; diff --git a/src/elastic-search/interfaces/mappingInterface.type.ts b/src/elastic-search/interfaces/mappingInterface.type.ts index b4a62b0f3..b05a89e7e 100644 --- a/src/elastic-search/interfaces/mappingInterface.type.ts +++ b/src/elastic-search/interfaces/mappingInterface.type.ts @@ -1,15 +1,4 @@ -import { searchType } from "./es-common.type"; - -export interface MappingProperty { - type: searchType; - fields?: { - keyword: { - type: searchType; - ignore_above: number; - }; - }; - [key: string]: unknown; -} +export type MappingProperty = Record; export interface MappingObject { [key: string]: MappingProperty; diff --git a/src/elastic-search/providers/query-builder.service.ts b/src/elastic-search/providers/query-builder.service.ts index 47b83fc86..1a0521973 100644 --- a/src/elastic-search/providers/query-builder.service.ts +++ b/src/elastic-search/providers/query-builder.service.ts @@ -1,5 +1,4 @@ import { Injectable, Logger } from "@nestjs/common"; -import { QueryDslQueryContainer } from "@elastic/elasticsearch/lib/api/types"; import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; import { IBoolShould, @@ -81,8 +80,8 @@ export class SearchQueryService { private buildTextQuery( fields: Partial, - ): QueryDslQueryContainer[] { - let wildcardQueries: QueryDslQueryContainer[] = []; + ): Record[] { + let wildcardQueries: Record[] = []; const { text } = fields; //NOTE: if text field is present, we query both datasetName and description fields @@ -103,7 +102,7 @@ export class SearchQueryService { .filter(Boolean); } - private buildWildcardQueries(text: string): QueryDslQueryContainer[] { + private buildWildcardQueries(text: string): Record[] { const terms = this.splitSearchText(text); return terms.flatMap((term) => this.mustFields.map((fieldName) => ({ @@ -211,7 +210,7 @@ export class SearchQueryService { private constructFinalQuery( filter: IFilter[], should: IBoolShould, - query: QueryDslQueryContainer[], + query: Record[], ) { const finalQuery = { query: { From 1467a31bec0ffc7b406591640a31530d63ece107 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Mon, 2 Mar 2026 11:53:50 +0100 Subject: [PATCH 02/46] modified api code --- CI/E2E/docker-compose-local.yaml | 3 +- src/elastic-search/elastic-search.service.ts | 37 +++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/CI/E2E/docker-compose-local.yaml b/CI/E2E/docker-compose-local.yaml index b5a7a6b83..8b7e531bd 100644 --- a/CI/E2E/docker-compose-local.yaml +++ b/CI/E2E/docker-compose-local.yaml @@ -3,7 +3,7 @@ services: mongodb: image: mongo:latest volumes: - - "mongodb_data://data/db" + - "mongodb_data:/data/db" ports: - "27017:27017" # scichat-loopback: @@ -36,7 +36,6 @@ services: - bootstrap.memory_lock=true - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} - - DISABLE_INSTALL_DEMO_CONFIG=false volumes: - opensearch_data:/usr/share/opensearch/data mem_limit: ${MEM_LIMIT} diff --git a/src/elastic-search/elastic-search.service.ts b/src/elastic-search/elastic-search.service.ts index 731bd6d11..2d7f66355 100644 --- a/src/elastic-search/elastic-search.service.ts +++ b/src/elastic-search/elastic-search.service.ts @@ -32,7 +32,7 @@ import { SortOrder } from "@opensearch-project/opensearch/api/_types/ml._common" @Injectable() export class ElasticSearchService implements OnModuleInit { - private esService: Client; + private osService: Client; private host: string; private username: string; private password: string; @@ -81,6 +81,7 @@ export class ElasticSearchService implements OnModuleInit { try { await this.retryConnection(3, 3000); const isIndexExists = await this.isIndexExists(this.defaultIndex); + if (!isIndexExists) { await this.createIndex(this.defaultIndex); Logger.log( @@ -106,8 +107,10 @@ export class ElasticSearchService implements OnModuleInit { rejectUnauthorized: false, }, }); + await connection.ping(); - this.esService = connection; + + this.osService = connection; } private async retryConnection(maxRetries: number, interval: number) { let retryCount = 0; @@ -131,14 +134,14 @@ export class ElasticSearchService implements OnModuleInit { } async isIndexExists(index = this.defaultIndex) { - return await this.esService.indices.exists({ + return await this.osService.indices.exists({ index, }); } async createIndex(index = this.defaultIndex) { try { - await this.esService.indices.create({ + await this.osService.indices.create({ index, body: { settings: defaultElasticSettings, @@ -167,7 +170,7 @@ export class ElasticSearchService implements OnModuleInit { } } async syncDatabase(collection: DatasetClass[], index = this.defaultIndex) { - const indexExists = await this.esService.indices.exists({ index }); + const indexExists = await this.osService.indices.exists({ index }); if (!indexExists) { throw new Error("Index not found"); } @@ -184,7 +187,7 @@ export class ElasticSearchService implements OnModuleInit { async getCount(index = this.defaultIndex) { try { - return await this.esService.count({ index }); + return await this.osService.count({ index }); } catch (error) { throw new HttpException( `getCount failed-> ElasticSearchService ${error}`, @@ -195,15 +198,15 @@ export class ElasticSearchService implements OnModuleInit { async updateIndex(index = this.defaultIndex) { try { - await this.esService.indices.close({ + await this.osService.indices.close({ index, }); - await this.esService.indices.putSettings({ + await this.osService.indices.putSettings({ index, body: { settings: defaultElasticSettings }, }); - await this.esService.indices.putMapping({ + await this.osService.indices.putMapping({ index, body: { properties: datasetMappings, @@ -211,7 +214,7 @@ export class ElasticSearchService implements OnModuleInit { }, }); - await this.esService.indices.open({ + await this.osService.indices.open({ index, }); Logger.log( @@ -228,7 +231,7 @@ export class ElasticSearchService implements OnModuleInit { async getIndexSettings(index = this.defaultIndex) { try { - return await this.esService.indices.getSettings({ index }); + return await this.osService.indices.getSettings({ index }); } catch (error) { throw new HttpException( `getIndexSettings failed-> ElasticSearchService ${error}`, @@ -239,7 +242,7 @@ export class ElasticSearchService implements OnModuleInit { async deleteIndex(index = this.defaultIndex) { try { - await this.esService.indices.delete({ index }); + await this.osService.indices.delete({ index }); Logger.log( `Elasticsearch Index Deleted-> Index: ${index} `, "Elasticsearch", @@ -311,7 +314,7 @@ export class ElasticSearchService implements OnModuleInit { } } - const body = (await this.esService.search({ + const body = (await this.osService.search({ index: this.defaultIndex, body: searchOptions, } as any)) as any; @@ -345,7 +348,7 @@ export class ElasticSearchService implements OnModuleInit { _source: [""], }; - const body = (await this.esService.search({ + const body = (await this.osService.search({ index: this.defaultIndex, body: searchOptions, } as any)) as any; @@ -372,7 +375,7 @@ export class ElasticSearchService implements OnModuleInit { scientificMetadata: transformedScientificMetadata, }; try { - await this.esService.index({ + await this.osService.index({ index: this.defaultIndex, id: data.pid, body: transformedData, @@ -393,7 +396,7 @@ export class ElasticSearchService implements OnModuleInit { async deleteDocument(id: string) { try { - await this.esService.delete({ + await this.osService.delete({ index: this.defaultIndex, id, refresh: this.refresh, @@ -413,7 +416,7 @@ export class ElasticSearchService implements OnModuleInit { // *** NOTE: below are helper methods *** async performBulkOperation(collection: DatasetClass[], index: string) { - const result = await this.esService.helpers.bulk({ + const result = await this.osService.helpers.bulk({ retries: 5, wait: 10000, datasource: collection, From 5dc47b270892d8cebd39c72e61f330aa53a71290 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 4 Mar 2026 11:54:47 +0100 Subject: [PATCH 03/46] renaming elasticSearch to Opensearch --- CI/E2E/docker-compose-local.yaml | 35 +-- package-lock.json | 239 ----------------- package.json | 1 - src/casl/casl-ability.factory.ts | 10 +- src/common/types.ts | 3 +- src/config/configuration.ts | 24 +- src/datasets/datasets.module.ts | 4 +- src/datasets/datasets.service.ts | 50 ++-- .../configuration/indexSetting.ts | 119 -------- src/elastic-search/dto/index.ts | 13 - src/elastic-search/helpers/utils.ts | 253 ------------------ .../interfaces/es-common.type.ts | 96 ------- .../configuration/datasetFieldMapping.ts | 26 +- src/opensearch/configuration/indexSetting.ts | 51 ++++ .../dto/create-index.dto.ts | 0 .../dto/delete-index.dto.ts | 0 .../dto/get-index.dto.ts | 0 src/opensearch/dto/index.ts | 13 + .../dto/search.dto.ts | 0 .../dto/sync-data.dto.ts | 0 .../dto/update-index.dto.ts | 0 src/opensearch/helpers/utils.ts | 30 +++ .../interfaces/mappingInterface.type.ts | 0 src/opensearch/interfaces/os-common.type.ts | 9 + .../opensearch.controller.ts} | 48 ++-- .../opensearch.module.ts} | 13 +- .../opensearch.service.spec.ts} | 12 +- .../opensearch.service.ts} | 192 +++++-------- .../opensearch_guide.md} | 0 .../providers/fields.enum.ts | 0 .../providers/query-builder.service.spec.ts | 0 .../providers/query-builder.service.ts | 77 ++---- 32 files changed, 308 insertions(+), 1010 deletions(-) delete mode 100644 src/elastic-search/configuration/indexSetting.ts delete mode 100644 src/elastic-search/dto/index.ts delete mode 100644 src/elastic-search/helpers/utils.ts delete mode 100644 src/elastic-search/interfaces/es-common.type.ts rename src/{elastic-search => opensearch}/configuration/datasetFieldMapping.ts (82%) create mode 100644 src/opensearch/configuration/indexSetting.ts rename src/{elastic-search => opensearch}/dto/create-index.dto.ts (100%) rename src/{elastic-search => opensearch}/dto/delete-index.dto.ts (100%) rename src/{elastic-search => opensearch}/dto/get-index.dto.ts (100%) create mode 100644 src/opensearch/dto/index.ts rename src/{elastic-search => opensearch}/dto/search.dto.ts (100%) rename src/{elastic-search => opensearch}/dto/sync-data.dto.ts (100%) rename src/{elastic-search => opensearch}/dto/update-index.dto.ts (100%) create mode 100644 src/opensearch/helpers/utils.ts rename src/{elastic-search => opensearch}/interfaces/mappingInterface.type.ts (100%) create mode 100644 src/opensearch/interfaces/os-common.type.ts rename src/{elastic-search/elastic-search.controller.ts => opensearch/opensearch.controller.ts} (68%) rename src/{elastic-search/elastic-search.module.ts => opensearch/opensearch.module.ts} (52%) rename src/{elastic-search/elastic-search.service.spec.ts => opensearch/opensearch.service.spec.ts} (77%) rename src/{elastic-search/elastic-search.service.ts => opensearch/opensearch.service.ts} (59%) rename src/{elastic-search/Elasticsearch_guide.md => opensearch/opensearch_guide.md} (100%) rename src/{elastic-search => opensearch}/providers/fields.enum.ts (100%) rename src/{elastic-search => opensearch}/providers/query-builder.service.spec.ts (100%) rename src/{elastic-search => opensearch}/providers/query-builder.service.ts (74%) diff --git a/CI/E2E/docker-compose-local.yaml b/CI/E2E/docker-compose-local.yaml index 8b7e531bd..e38f9369f 100644 --- a/CI/E2E/docker-compose-local.yaml +++ b/CI/E2E/docker-compose-local.yaml @@ -6,22 +6,23 @@ services: - "mongodb_data:/data/db" ports: - "27017:27017" - # scichat-loopback: - # image: dacat/scichat-loopback:e2e - # command: - # [ - # "./wait-for-it.sh", - # "mongodb:27017", - # "--", - # "node", - # "-r", - # "dotenv/config", - # "." - # ] - # volumes: - # - ".env.scichat-loopback:/home/node/app/.env" - # depends_on: - # - mongodb + + scichat-loopback: + image: dacat/scichat-loopback:e2e + command: + [ + "./wait-for-it.sh", + "mongodb:27017", + "--", + "node", + "-r", + "dotenv/config", + "." + ] + volumes: + - ".env.scichat-loopback:/home/node/app/.env" + depends_on: + - mongodb opensearch: depends_on: @@ -55,7 +56,7 @@ services: environment: - OPENSEARCH_HOSTS=https://opensearch:9200 - OPENSEARCH_USERNAME=admin - - OPENSEARCH_PASSWORD=admin + - OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} - OPENSEARCH_SSL_VERIFICATIONMODE=none volumes: diff --git a/package-lock.json b/package-lock.json index 4e144c869..2bb983e73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@casl/ability": "^6.3.2", "@casl/mongoose": "^8.0.4", - "@elastic/elasticsearch": "^8.15.0", "@nestjs-modules/mailer": "^2", "@nestjs/axios": "^4", "@nestjs/common": "^11", @@ -1020,38 +1019,6 @@ "kuler": "^2.0.0" } }, - "node_modules/@elastic/elasticsearch": { - "version": "8.19.1", - "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.19.1.tgz", - "integrity": "sha512-+1j9NnQVOX+lbWB8LhCM7IkUmjU05Y4+BmSLfusq0msCsQb1Va+OUKFCoOXjCJqQrcgdRdQCjYYyolQ/npQALQ==", - "license": "Apache-2.0", - "dependencies": { - "@elastic/transport": "^8.9.6", - "apache-arrow": "18.x - 21.x", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@elastic/transport": { - "version": "8.9.6", - "resolved": "https://registry.npmjs.org/@elastic/transport/-/transport-8.9.6.tgz", - "integrity": "sha512-v71jgmZtgPg2ouXF5KTPxU1a6z7YYc8nazAS7jLySteC/vrShs1OJh6oEEeo5oDc19MYUofV/JV1h5vqJVBXOw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "1.x", - "debug": "^4.4.0", - "hpagent": "^1.2.0", - "ms": "^2.1.3", - "secure-json-parse": "^3.0.1", - "tslib": "^2.8.1", - "undici": "^6.21.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@emnapi/core": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", @@ -3168,15 +3135,6 @@ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", "license": "BSD-3-Clause" }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@paralleldrive/cuid2": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", @@ -3316,14 +3274,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -3443,16 +3393,6 @@ "assertion-error": "^2.0.1" } }, - "node_modules/@types/command-line-args": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", - "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==" - }, - "node_modules/@types/command-line-usage": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/command-line-usage/-/command-line-usage-5.0.4.tgz", - "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==" - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -4921,38 +4861,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/apache-arrow": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/apache-arrow/-/apache-arrow-18.1.0.tgz", - "integrity": "sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==", - "dependencies": { - "@swc/helpers": "^0.5.11", - "@types/command-line-args": "^5.2.3", - "@types/command-line-usage": "^5.0.4", - "@types/node": "^20.13.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^24.3.25", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.js" - } - }, - "node_modules/apache-arrow/node_modules/@types/node": { - "version": "20.17.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", - "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/apache-arrow/node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" - }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -4970,14 +4878,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "engines": { - "node": ">=6" - } - }, "node_modules/array-timsort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", @@ -5631,20 +5531,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk-template": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", - "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "dependencies": { - "chalk": "^4.1.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -6025,50 +5911,6 @@ "node": ">= 0.8" } }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", - "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", - "dependencies": { - "array-back": "^6.2.2", - "chalk-template": "^0.4.0", - "table-layout": "^4.1.0", - "typical": "^7.1.1" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "engines": { - "node": ">=12.17" - } - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -7631,17 +7473,6 @@ "node": ">= 0.8" } }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -7710,11 +7541,6 @@ "node": ">=16" } }, - "node_modules/flatbuffers": { - "version": "24.12.23", - "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-24.12.23.tgz", - "integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==" - }, "node_modules/flatted": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", @@ -9890,14 +9716,6 @@ "node": ">=6" } }, - "node_modules/json-bignum": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/json-bignum/-/json-bignum-0.0.3.tgz", - "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -10323,11 +10141,6 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT" }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -13198,22 +13011,6 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, - "node_modules/secure-json-parse": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-3.0.2.tgz", - "integrity": "sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", @@ -13880,26 +13677,6 @@ "url": "https://opencollective.com/synckit" } }, - "node_modules/table-layout": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", - "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", - "dependencies": { - "array-back": "^6.2.2", - "wordwrapjs": "^5.1.0" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "engines": { - "node": ">=12.17" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -14522,14 +14299,6 @@ "node": ">=14.17" } }, - "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "engines": { - "node": ">=8" - } - }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -15306,14 +15075,6 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, - "node_modules/wordwrapjs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", - "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", - "engines": { - "node": ">=12.17" - } - }, "node_modules/workerpool": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", diff --git a/package.json b/package.json index 2aa8ced15..a5905e895 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "dependencies": { "@casl/ability": "^6.3.2", "@casl/mongoose": "^8.0.4", - "@elastic/elasticsearch": "^8.15.0", "@nestjs-modules/mailer": "^2", "@nestjs/axios": "^4", "@nestjs/common": "^11", diff --git a/src/casl/casl-ability.factory.ts b/src/casl/casl-ability.factory.ts index 3da7cf5ca..6db62764a 100644 --- a/src/casl/casl-ability.factory.ts +++ b/src/casl/casl-ability.factory.ts @@ -15,7 +15,6 @@ import { JobConfig } from "src/config/job-config/jobconfig.interface"; import { JobConfigService } from "src/config/job-config/jobconfig.service"; import { Datablock } from "src/datablocks/schemas/datablock.schema"; import { DatasetClass } from "src/datasets/schemas/dataset.schema"; -import { ElasticSearchActions } from "src/elastic-search/dto"; import { Instrument } from "src/instruments/schemas/instrument.schema"; import { JobClass } from "src/jobs/schemas/job.schema"; import { CreateJobAuth, UpdateJobAuth } from "src/jobs/types/jobs-auth.enum"; @@ -32,6 +31,7 @@ import { Action } from "./action.enum"; import { RuntimeConfig } from "src/config/runtime-config/schemas/runtime-config.schema"; import { accessibleBy } from "@casl/mongoose"; import { MetadataKeyClass } from "src/metadata-keys/schemas/metadatakey.schema"; +import { OpensearchActions } from "src/opensearch/dto"; type Subjects = | string @@ -50,7 +50,7 @@ type Subjects = | typeof User | typeof UserIdentity | typeof UserSettings - | typeof ElasticSearchActions + | typeof OpensearchActions | typeof Datablock | typeof RuntimeConfig | typeof MetadataKeyClass @@ -76,7 +76,7 @@ export class CaslAbilityFactory { [endpoint: string]: (user: JWTUser) => AppAbility; } = { datasets: this.datasetEndpointAccess, - "elastic-search": this.elasticSearchEndpointAccess, + opensearch: this.opensearchEndpointAccess, jobs: this.jobsEndpointAccess, instruments: this.instrumentEndpointAccess, logbooks: this.logbookEndpointAccess, @@ -328,7 +328,7 @@ export class CaslAbilityFactory { }); } - elasticSearchEndpointAccess(user: JWTUser) { + opensearchEndpointAccess(user: JWTUser) { const { can, build } = new AbilityBuilder( createMongoAbility, ); @@ -340,7 +340,7 @@ export class CaslAbilityFactory { /* / user that belongs to any of the group listed in ADMIN_GROUPS */ - can(Action.Manage, ElasticSearchActions); + can(Action.Manage, OpensearchActions); } return build({ detectSubjectType: (item) => diff --git a/src/common/types.ts b/src/common/types.ts index f2c4ea9d0..a716b66d6 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,6 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; import { ValidationError } from "class-validator"; -import { IFullFacets } from "src/elastic-search/interfaces/es-common.type"; export class FullFacetFilters { @ApiPropertyOptional() @@ -23,7 +22,7 @@ class TotalSets { totalSets: number; } -export class FullFacetResponse implements IFullFacets { +export class FullFacetResponse { @ApiProperty({ type: TotalSets, isArray: true }) all: [TotalSets]; diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 1115bfbc1..18ffc5aa5 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -372,16 +372,22 @@ const configuration = () => { username: process.env.RABBITMQ_USERNAME, password: process.env.RABBITMQ_PASSWORD, }, - elasticSearch: { - enabled: process.env.ELASTICSEARCH_ENABLED ?? "no", - username: process.env.ES_USERNAME, - password: process.env.ES_PASSWORD, - host: process.env.ES_HOST, - refresh: process.env.ES_REFRESH, - maxResultWindow: parseInt(process.env.ES_MAX_RESULT || "100000", 10), - fieldsLimit: parseInt(process.env.ES_FIELDS_LIMIT || "100000", 10), + opensearch: { + enabled: process.env.OPENSEARCH_ENABLED ?? "no", + username: process.env.OPENSEARCH_USERNAME ?? "admin", + password: process.env.OPENSEARCH_PASSWORD, + host: process.env.OPENSEARCH_HOST, + refresh: process.env.OPENSEARCH_REFRESH, + maxResultWindow: parseInt( + process.env.OPENSEARCH_MAX_RESULT || "100000", + 10, + ), + fieldsLimit: parseInt( + process.env.OPENSEARCH_FIELDS_LIMIT || "100000", + 10, + ), mongoDBCollection: process.env.MONGODB_COLLECTION, - defaultIndex: process.env.ES_INDEX ?? "dataset", + defaultIndex: process.env.OPENSEARCH_INDEX ?? "dataset", }, metrics: { // Note: `process.env.METRICS_ENABLED` is directly used for conditional module loading in diff --git a/src/datasets/datasets.module.ts b/src/datasets/datasets.module.ts index fe7d33286..d29b78bbd 100644 --- a/src/datasets/datasets.module.ts +++ b/src/datasets/datasets.module.ts @@ -11,7 +11,6 @@ import { InitialDatasetsModule } from "src/initial-datasets/initial-datasets.mod import { LogbooksModule } from "src/logbooks/logbooks.module"; import { PoliciesService } from "src/policies/policies.service"; import { PoliciesModule } from "src/policies/policies.module"; -import { ElasticSearchModule } from "src/elastic-search/elastic-search.module"; import { DatasetsV4Controller } from "./datasets.v4.controller"; import { DatasetsPublicV4Controller } from "./datasets-public.v4.controller"; import { DatasetsAccessService } from "./datasets-access.service"; @@ -25,6 +24,7 @@ import { applyHistoryPluginOnce } from "src/common/mongoose/plugins/history.plug import { ProposalsModule } from "src/proposals/proposals.module"; import { HistoryModule } from "src/history/history.module"; import { MetadataKeysModule } from "src/metadata-keys/metadatakeys.module"; +import { OpensearchModule } from "src/opensearch/opensearch.module"; @Module({ imports: [ @@ -35,7 +35,7 @@ import { MetadataKeysModule } from "src/metadata-keys/metadatakeys.module"; InitialDatasetsModule, HistoryModule, MetadataKeysModule, - ElasticSearchModule, + OpensearchModule, ProposalsModule, forwardRef(() => LogbooksModule), MongooseModule.forFeatureAsync([ diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index c59e2ba0d..31acb698f 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -34,7 +34,6 @@ import { parsePipelineSort, decodeMetadataKeyStrings, } from "src/common/utils"; -import { ElasticSearchService } from "src/elastic-search/elastic-search.service"; import { DatasetsAccessService } from "./datasets-access.service"; import { CreateDatasetDto } from "./dto/create-dataset.dto"; import { @@ -63,10 +62,11 @@ import { MetadataKeysService, MetadataSourceDoc, } from "src/metadata-keys/metadatakeys.service"; +import { OpensearchService } from "src/opensearch/opensearch.service"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { - private ESClient: ElasticSearchService | null; + private searchService: OpensearchService | null; constructor( private configService: ConfigService, @InjectModel(DatasetClass.name) @@ -74,12 +74,12 @@ export class DatasetsService { @Inject(REQUEST) private request: Request, private datasetsAccessService: DatasetsAccessService, - private elasticSearchService: ElasticSearchService, + private opensearchService: OpensearchService, private metadataKeysService: MetadataKeysService, private proposalService: ProposalsService, ) { - if (this.elasticSearchService.connected) { - this.ESClient = this.elasticSearchService; + if (this.opensearchService.connected) { + this.searchService = this.opensearchService; } } @@ -186,8 +186,8 @@ export class DatasetsService { const savedDataset = await createdDataset.save(); - if (this.ESClient && createdDataset) { - await this.ESClient.updateInsertDocument(savedDataset.toObject()); + if (this.searchService && createdDataset) { + await this.searchService.updateInsertDocument(savedDataset.toObject()); } if (savedDataset.proposalIds && savedDataset.proposalIds.length > 0) { @@ -297,12 +297,16 @@ export class DatasetsService { // NOTE: if Elastic search DB is empty we should use default mongo query const canPerformElasticSearchQueries = await this.isElasticSearchDBEmpty(); - if (!this.ESClient || isFieldsEmpty || !canPerformElasticSearchQueries) { + if ( + !this.searchService || + isFieldsEmpty || + !canPerformElasticSearchQueries + ) { datasets = await this.datasetModel .find(whereClause, null, modifiers) .exec(); } else { - const esResult = await this.ESClient.search( + const esResult = await this.searchService.search( filter.fields as IDatasetFields, modifiers.limit, modifiers.skip, @@ -332,7 +336,11 @@ export class DatasetsService { // NOTE: if Elastic search DB is empty we should use default mongo query const canPerformElasticSearchQueries = await this.isElasticSearchDBEmpty(); - if (!this.ESClient || isFieldsEmpty || !canPerformElasticSearchQueries) { + if ( + !this.searchService || + isFieldsEmpty || + !canPerformElasticSearchQueries + ) { const pipeline = createFullfacetPipeline( this.datasetModel, "pid", @@ -343,7 +351,7 @@ export class DatasetsService { data = await this.datasetModel.aggregate(pipeline).exec(); } else { - const facetResult = await this.ESClient.aggregate(fields); + const facetResult = await this.searchService.aggregate(fields); data = facetResult; } @@ -387,10 +395,10 @@ export class DatasetsService { ): Promise<{ count: number }> { const whereFilter: RootFilterQuery = filter.where ?? {}; let count = 0; - if (this.ESClient && !filter.where) { + if (this.searchService && !filter.where) { const totalDocCount = await this.datasetModel.countDocuments(); - const { totalCount } = await this.ESClient.search( + const { totalCount } = await this.searchService.search( whereFilter as IDatasetFields, totalDocCount, ); @@ -437,8 +445,8 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.ESClient) { - await this.ESClient.updateInsertDocument(updatedDataset.toObject()); + if (this.searchService) { + await this.searchService.updateInsertDocument(updatedDataset.toObject()); } await this.metadataKeysService.replaceManyFromSource( @@ -483,8 +491,8 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.ESClient) { - await this.ESClient.updateInsertDocument(patchedDataset.toObject()); + if (this.searchService) { + await this.searchService.updateInsertDocument(patchedDataset.toObject()); } await this.metadataKeysService.replaceManyFromSource( @@ -506,8 +514,8 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.ESClient) { - await this.ESClient.deleteDocument(id); + if (this.searchService) { + await this.searchService.deleteDocument(id); } if (deletedDataset?.proposalIds && deletedDataset.proposalIds.length > 0) { @@ -599,8 +607,8 @@ export class DatasetsService { } async isElasticSearchDBEmpty() { - if (!this.ESClient) return; - const count = await this.ESClient.getCount(); + if (!this.searchService) return; + const count = await this.searchService.getCount(); return count.body.count > 0; } } diff --git a/src/elastic-search/configuration/indexSetting.ts b/src/elastic-search/configuration/indexSetting.ts deleted file mode 100644 index 36250e30c..000000000 --- a/src/elastic-search/configuration/indexSetting.ts +++ /dev/null @@ -1,119 +0,0 @@ -//Tokenizers -export const autocomplete_tokenizer: any = { - type: "edge_ngram", - min_gram: 2, - max_gram: 40, - token_chars: ["letter", "digit", "symbol", "punctuation"], -}; - -//Filters -export const special_character_filter: any = { - pattern: "[^A-Za-z0-9]", - type: "pattern_replace", - replacement: "", -}; - -//Dynamic templates -export const dynamic_template: Record[] = [ - // NOTE: date as keyword is temporary solution for date format inconsistency issue in the scientificMetadata field - { - date_as_keyword: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "date", - mapping: { - type: "keyword", - }, - }, - }, - { - long_as_double: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "long", - mapping: { - type: "double", - coerce: true, - ignore_malformed: true, - }, - }, - }, - { - double_as_double: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "double", - mapping: { - type: "double", - coerce: true, - ignore_malformed: true, - }, - }, - }, - { - string_as_keyword_level2: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "string", - mapping: { - type: "text", - analyzer: "case_sensitive", - search_analyzer: "case_sensitive", - fields: { - keyword: { - type: "keyword", - ignore_above: 256, - }, - }, - }, - }, - }, - { - string_as_keyword_level1: { - path_match: "scientificMetadata.*", - match_mapping_type: "string", - mapping: { - type: "text", - analyzer: "case_sensitive", - search_analyzer: "case_sensitive", - fields: { - keyword: { - type: "keyword", - ignore_above: 256, - }, - }, - }, - }, - }, -]; - -//Index Settings -export const defaultElasticSettings = { - index: { - max_result_window: process.env.ES_MAX_RESULT || 2000000, - number_of_replicas: 0, - mapping: { - total_fields: { - limit: process.env.ES_FIELDS_LIMIT || 2000000, - }, - nested_fields: { - limit: 1000, - }, - }, - }, - analysis: { - analyzer: { - autocomplete: { - tokenizer: "autocomplete", - filter: ["lowercase"], - }, - autocomplete_search: { - tokenizer: "lowercase", - }, - case_sensitive: { - type: "custom", - tokenizer: "standard", - filter: [], - }, - }, - tokenizer: { - autocomplete: autocomplete_tokenizer, - }, - }, -} as any; diff --git a/src/elastic-search/dto/index.ts b/src/elastic-search/dto/index.ts deleted file mode 100644 index ea3e4ea1c..000000000 --- a/src/elastic-search/dto/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { CreateIndexDto } from "../dto/create-index.dto"; -import { DeleteIndexDto } from "../dto/delete-index.dto"; -import { GetIndexDto } from "../dto/get-index.dto"; -import { SyncDatabaseDto } from "../dto/sync-data.dto"; -import { UpdateIndexDto } from "../dto/update-index.dto"; - -export class ElasticSearchActions { - createIndex: CreateIndexDto; - deleteIndex: DeleteIndexDto; - updateIndex: UpdateIndexDto; - syncDatabase: SyncDatabaseDto; - getIndexSettings: GetIndexDto; -} diff --git a/src/elastic-search/helpers/utils.ts b/src/elastic-search/helpers/utils.ts deleted file mode 100644 index 5d6e9448a..000000000 --- a/src/elastic-search/helpers/utils.ts +++ /dev/null @@ -1,253 +0,0 @@ -import { DatasetClass } from "src/datasets/schemas/dataset.schema"; -import { - IFilter, - ITransformedFullFacets, - nestedQueryObject, - ScientificQuery, -} from "../interfaces/es-common.type"; -import { isObject } from "lodash"; -import { - AggregateBase, - SingleBucketAggregateBase, -} from "@opensearch-project/opensearch/api/_types/_common.aggregations"; - -export const transformKey = (key: string): string => { - return key.trim().replace(/[.]/g, "\\.").replace(/ /g, "_").toLowerCase(); -}; -export const addValueType = (obj: Record) => { - const newObj: Record = {}; - - for (const [key, value] of Object.entries(obj)) { - const newKey = transformKey(key); - - const isNumberValueType = - typeof (value as Record)?.value === "number"; - const isStringValueType = - typeof (value as Record)?.value === "string"; - - if (isObject(value)) { - if (isNumberValueType) { - (value as Record)["value_type"] = "number"; - } else if (isStringValueType) { - (value as Record)["value_type"] = "string"; - } else { - (value as Record)["value_type"] = "unknown"; - } - } - - newObj[newKey] = value; - } - - return newObj; -}; - -export const transformMiddleKey = (key: string) => { - const parts = key.trim().split("."); - const firstPart = parts[0]; - const lastPart = parts[parts.length - 1]; - - const middlePartRaw = parts.slice(1, parts.length - 1).join("."); - const middlePart = transformKey(middlePartRaw); - - let transformedKey = firstPart; - if (middlePart) { - transformedKey += `.${middlePart}`; - } - transformedKey += `.${lastPart}`; - - return { transformedKey, firstPart, middlePart, lastPart }; -}; - -export const initialSyncTransform = (obj: DatasetClass) => { - const modifedDocInArray = !!obj.scientificMetadata - ? Object.entries(obj.scientificMetadata as Record).map( - ([key, value]) => { - const transformedKey = transformKey(key); - - if (!isObject(value)) { - return [transformedKey, { value: value, unit: "" }]; - } - - if ("value" in value) { - if (typeof value.value === "number") { - return [transformedKey, { ...value, value_type: "number" }]; - } - if (typeof value.value === "string") { - return [transformedKey, { ...value, value_type: "string" }]; - } - } - - return [transformedKey, { ...value, value_type: "unknown" }]; - }, - ) - : []; - - const modifiedDocInObject = { - ...obj, - scientificMetadata: - modifedDocInArray.length > 0 ? Object.fromEntries(modifedDocInArray) : {}, - }; - - return modifiedDocInObject; -}; - -const extractNestedQueryOperationValue = (query: nestedQueryObject) => { - const field = Object.keys(query)[0]; - const operationWithPrefix = Object.keys(query[field])[0]; - - const value = - typeof query[field][operationWithPrefix] === "string" - ? (query[field][operationWithPrefix] as string).trim() - : query[field][operationWithPrefix]; - - const operation = operationWithPrefix.replace("$", ""); - - return { operation, value, field }; -}; - -export const convertToElasticSearchQuery = ( - scientificQuery: ScientificQuery, -): IFilter[] => { - const filters: IFilter[] = []; - - for (const field in scientificQuery) { - const query = scientificQuery[field]; - - if (field === "$and" && Array.isArray(query)) { - query.forEach((query: { $or: nestedQueryObject[] }) => { - const shouldQueries = query.$or.map((orQuery: nestedQueryObject) => { - const { operation, value, field } = - extractNestedQueryOperationValue(orQuery); - const filterType = operation === "eq" ? "term" : "range"; - - // NOTE: if value is not a number, we use keyword field for exact match - const targetField = - typeof value !== "number" ? `${field}.keyword` : field; - return { - [filterType]: { - [targetField]: - operation === "eq" ? value : { [operation]: value }, - }, - }; - }); - filters.push({ - bool: { - should: shouldQueries, - minimum_should_match: 1, - }, - }); - }); - } else if (field === "$or" && Array.isArray(query)) { - const shouldQueries = query.map((query: nestedQueryObject) => { - const { operation, value, field } = - extractNestedQueryOperationValue(query); - const filterType = operation === "eq" ? "term" : "range"; - - // NOTE: if value is not a number, we use keyword field for exact match - const targetField = - typeof value !== "number" ? `${field}.keyword` : field; - return { - [filterType]: { - [targetField]: operation === "eq" ? value : { [operation]: value }, - }, - }; - }); - filters.push({ - bool: { - should: shouldQueries, - minimum_should_match: 1, - }, - }); - } else if ( - Object.keys(query).length > 1 && - !Array.isArray(query) && - isObject(query) - ) { - const { transformedKey, firstPart, middlePart, lastPart } = - transformMiddleKey(field); - - const rangeOps: Record = {}; - - Object.keys(query).forEach((op) => { - const val = query[op]; - rangeOps[op.replace("$", "")] = - typeof val === "number" ? val : Number(val); - }); - - if (lastPart === "valueSI" || lastPart === "value") { - filters.push({ - term: { - [`${firstPart}.${middlePart}.value_type`]: "number", - }, - }); - } - filters.push({ - range: { [transformedKey]: rangeOps }, - }); - } else { - const operation = Object.keys(query)[0]; - const value = - typeof (query as Record)[operation] === "string" - ? (query as Record)[operation].trim() - : (query as Record)[operation]; - const esOperation = operation.replace("$", ""); - - // NOTE: - // trasnformedKey = "scientificMetadata.someKey.value" - // firstPart = "scientificMetadata", - // middlePart = "someKey" - // lastPart = "value" - const { transformedKey, firstPart, middlePart, lastPart } = - transformMiddleKey(field); - - if (lastPart === "valueSI" || lastPart === "value") { - const numberFilter = { - term: { - [`${firstPart}.${middlePart}.value_type`]: - typeof value === "number" ? "number" : "string", - }, - }; - filters.push(numberFilter); - } - - // NOTE: if value is not a number, we use keyword field for exact match for term queries - // for range queries we can skip this as string values will not be accepted - const targetField = - typeof value !== "number" ? `${field}.keyword` : field; - - const filter = - esOperation === "eq" - ? { - term: { [`${targetField}`]: value }, - } - : { - range: { [`${transformedKey}`]: { [esOperation]: value } }, - }; - - filters.push(filter); - } - } - return filters; -}; - -export const transformFacets = ( - aggregation: AggregateBase, -): Record[] => { - const transformed = Object.entries(aggregation).reduce( - (acc, [key, value]) => { - const isBucketArray = Array.isArray(value.buckets); - - acc[key] = isBucketArray - ? value.buckets.map((bucket: SingleBucketAggregateBase) => ({ - _id: bucket.key, - count: bucket.doc_count, - })) - : [{ totalSets: value.value }]; - - return acc; - }, - {} as ITransformedFullFacets, - ); - - return [transformed]; -}; diff --git a/src/elastic-search/interfaces/es-common.type.ts b/src/elastic-search/interfaces/es-common.type.ts deleted file mode 100644 index 7addfa65e..000000000 --- a/src/elastic-search/interfaces/es-common.type.ts +++ /dev/null @@ -1,96 +0,0 @@ -export type ObjectType = { - begin: string; - end: string; -}; - -export type NumberRangeType = { - min: string; - max: string; -}; - -export interface IShould { - terms?: { - [key: string]: string[] | undefined; - }; - term?: { - [key: string]: string | undefined; - }; - range?: { - [key: string]: { - gte?: string | number; - lte?: string | number; - }; - }; -} - -export interface IBoolShould { - bool: { - should: IShould[]; - minimum_should_match?: number; - }; -} - -export interface IFilter { - terms?: { - [key: string]: string[]; - }; - term?: { - [key: string]: boolean | string; - }; - range?: { - [key: string]: { - gte?: string | number; - lte?: string | number; - }; - }; - match?: { - [key: string]: string | number; - }; - nested?: { - path: string; - query: { - bool: { - must: ( - | { term?: { [key: string]: string } } - | { range?: { [key: string]: { [key: string]: string | number } } } - )[]; - }; - }; - }; - bool?: { - should: IShould[]; - minimum_should_match?: number; - }; -} - -export interface IFullFacets { - [key: string]: { - terms?: { - field: string; - order: { - _key?: string; - _count?: string; - }; - }; - value_count?: { - field: "pid"; - }; - }; -} - -export interface ITransformedFullFacets { - [key: string]: - | { - _id: string; - count: number; - } - | { totalSets: number }; -} - -export interface ScientificQuery { - [key: string]: Record | "$and" | "$or"; -} - -export interface nestedQueryObject { - [key: string]: Record; -} diff --git a/src/elastic-search/configuration/datasetFieldMapping.ts b/src/opensearch/configuration/datasetFieldMapping.ts similarity index 82% rename from src/elastic-search/configuration/datasetFieldMapping.ts rename to src/opensearch/configuration/datasetFieldMapping.ts index bed10c389..d9ed15b07 100644 --- a/src/elastic-search/configuration/datasetFieldMapping.ts +++ b/src/opensearch/configuration/datasetFieldMapping.ts @@ -5,23 +5,11 @@ export const datasetMappings: MappingObject = { type: "text", analyzer: "autocomplete", search_analyzer: "autocomplete_search", - fields: { - keyword: { - type: "keyword", - ignore_above: 256, - }, - }, }, datasetName: { type: "text", analyzer: "autocomplete", search_analyzer: "autocomplete_search", - fields: { - keyword: { - type: "keyword", - ignore_above: 256, - }, - }, }, pid: { type: "keyword", @@ -33,15 +21,11 @@ export const datasetMappings: MappingObject = { endTime: { type: "date", }, - scientificMetadata: { - type: "nested", - dynamic: true, - properties: {}, - }, - history: { - type: "nested", - dynamic: false, - }, + // scientificMetadata: { + // type: "nested", + // dynamic: true, + // properties: {}, + // }, proposalIds: { type: "keyword", ignore_above: 256, diff --git a/src/opensearch/configuration/indexSetting.ts b/src/opensearch/configuration/indexSetting.ts new file mode 100644 index 000000000..6bf247e5b --- /dev/null +++ b/src/opensearch/configuration/indexSetting.ts @@ -0,0 +1,51 @@ +//Tokenizers +export const autocomplete_tokenizer = { + type: "edge_ngram", + min_gram: 2, + max_gram: 40, + token_chars: ["letter", "digit", "symbol", "punctuation"], +}; + +//Filters +export const special_character_filter = { + pattern: "[^A-Za-z0-9]", + type: "pattern_replace", + replacement: "", +}; + +//Index Settings +export const defaultOpensearchSettings = { + index: { + max_result_window: process.env.ES_MAX_RESULT || 2000000, + number_of_replicas: 0, + mapping: { + total_fields: { + limit: process.env.ES_FIELDS_LIMIT || 2000000, + }, + nested_fields: { + limit: 1000, + }, + }, + }, + analysis: { + analyzer: { + autocomplete: { + type: "custom", + tokenizer: "autocomplete", + filter: ["lowercase"], + }, + autocomplete_search: { + type: "custom", + tokenizer: "lowercase", + }, + case_sensitive: { + type: "custom", + tokenizer: "standard", + filter: [], + }, + }, + tokenizer: { + autocomplete: autocomplete_tokenizer, + }, + }, +}; diff --git a/src/elastic-search/dto/create-index.dto.ts b/src/opensearch/dto/create-index.dto.ts similarity index 100% rename from src/elastic-search/dto/create-index.dto.ts rename to src/opensearch/dto/create-index.dto.ts diff --git a/src/elastic-search/dto/delete-index.dto.ts b/src/opensearch/dto/delete-index.dto.ts similarity index 100% rename from src/elastic-search/dto/delete-index.dto.ts rename to src/opensearch/dto/delete-index.dto.ts diff --git a/src/elastic-search/dto/get-index.dto.ts b/src/opensearch/dto/get-index.dto.ts similarity index 100% rename from src/elastic-search/dto/get-index.dto.ts rename to src/opensearch/dto/get-index.dto.ts diff --git a/src/opensearch/dto/index.ts b/src/opensearch/dto/index.ts new file mode 100644 index 000000000..844e68d25 --- /dev/null +++ b/src/opensearch/dto/index.ts @@ -0,0 +1,13 @@ +import { CreateIndexDto } from "./create-index.dto"; +import { DeleteIndexDto } from "./delete-index.dto"; +import { GetIndexDto } from "./get-index.dto"; +import { SyncDatabaseDto } from "./sync-data.dto"; +import { UpdateIndexDto } from "./update-index.dto"; + +export class OpensearchActions { + createIndex: CreateIndexDto; + deleteIndex: DeleteIndexDto; + updateIndex: UpdateIndexDto; + syncDatabase: SyncDatabaseDto; + getIndexSettings: GetIndexDto; +} diff --git a/src/elastic-search/dto/search.dto.ts b/src/opensearch/dto/search.dto.ts similarity index 100% rename from src/elastic-search/dto/search.dto.ts rename to src/opensearch/dto/search.dto.ts diff --git a/src/elastic-search/dto/sync-data.dto.ts b/src/opensearch/dto/sync-data.dto.ts similarity index 100% rename from src/elastic-search/dto/sync-data.dto.ts rename to src/opensearch/dto/sync-data.dto.ts diff --git a/src/elastic-search/dto/update-index.dto.ts b/src/opensearch/dto/update-index.dto.ts similarity index 100% rename from src/elastic-search/dto/update-index.dto.ts rename to src/opensearch/dto/update-index.dto.ts diff --git a/src/opensearch/helpers/utils.ts b/src/opensearch/helpers/utils.ts new file mode 100644 index 000000000..d3905c5b3 --- /dev/null +++ b/src/opensearch/helpers/utils.ts @@ -0,0 +1,30 @@ +import { + AggregateBase, + SingleBucketAggregateBase, +} from "@opensearch-project/opensearch/api/_types/_common.aggregations"; + +export const transformKey = (key: string): string => { + return key.trim().replace(/[.]/g, "\\.").replace(/ /g, "_").toLowerCase(); +}; + +export const transformFacets = ( + aggregation: AggregateBase, +): Record[] => { + const transformed = Object.entries(aggregation).reduce( + (acc, [key, value]) => { + const isBucketArray = Array.isArray(value.buckets); + + acc[key] = isBucketArray + ? value.buckets.map((bucket: SingleBucketAggregateBase) => ({ + _id: bucket.key, + count: bucket.doc_count, + })) + : [{ totalSets: value.value }]; + + return acc; + }, + {} as any, + ); + + return [transformed]; +}; diff --git a/src/elastic-search/interfaces/mappingInterface.type.ts b/src/opensearch/interfaces/mappingInterface.type.ts similarity index 100% rename from src/elastic-search/interfaces/mappingInterface.type.ts rename to src/opensearch/interfaces/mappingInterface.type.ts diff --git a/src/opensearch/interfaces/os-common.type.ts b/src/opensearch/interfaces/os-common.type.ts new file mode 100644 index 000000000..5344ef251 --- /dev/null +++ b/src/opensearch/interfaces/os-common.type.ts @@ -0,0 +1,9 @@ +export type ObjectType = { + begin: string; + end: string; +}; + +export type NumberRangeType = { + min: string; + max: string; +}; diff --git a/src/elastic-search/elastic-search.controller.ts b/src/opensearch/opensearch.controller.ts similarity index 68% rename from src/elastic-search/elastic-search.controller.ts rename to src/opensearch/opensearch.controller.ts index de2498da6..fb9ed78e7 100644 --- a/src/elastic-search/elastic-search.controller.ts +++ b/src/opensearch/opensearch.controller.ts @@ -17,7 +17,6 @@ import { PoliciesGuard } from "src/casl/guards/policies.guard"; import { DatasetsService } from "src/datasets/datasets.service"; import { SubDatasetsPublicInterceptor } from "src/datasets/interceptors/datasets-public.interceptor"; import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; -import { ElasticSearchActions } from "./dto"; import { CreateIndexDto } from "./dto/create-index.dto"; import { DeleteIndexDto } from "./dto/delete-index.dto"; import { GetIndexDto } from "./dto/get-index.dto"; @@ -25,20 +24,21 @@ import { GetIndexDto } from "./dto/get-index.dto"; import { SearchDto } from "./dto/search.dto"; import { SyncDatabaseDto } from "./dto/sync-data.dto"; import { UpdateIndexDto } from "./dto/update-index.dto"; -import { ElasticSearchService } from "./elastic-search.service"; +import { OpensearchActions } from "./dto"; +import { OpensearchService } from "./opensearch.service"; @ApiBearerAuth() -@ApiTags("elastic-search") -@Controller("elastic-search") -export class ElasticSearchServiceController { +@ApiTags("opensearch") +@Controller("opensearch") +export class OpensearchController { constructor( - private readonly elasticSearchService: ElasticSearchService, + private readonly opensearchService: OpensearchService, private readonly datasetService: DatasetsService, ) {} @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.CREATED) @ApiResponse({ @@ -49,12 +49,12 @@ export class ElasticSearchServiceController { async createIndex(@Query() { index }: CreateIndexDto) { const esIndex = index.trim(); - return this.elasticSearchService.createIndex(esIndex); + return this.opensearchService.createIndex(esIndex); } @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.OK) @ApiResponse({ @@ -66,12 +66,12 @@ export class ElasticSearchServiceController { const esIndex = index.trim(); const collectionData = await this.datasetService.getDatasetsWithoutId(); - return this.elasticSearchService.syncDatabase(collectionData, esIndex); + return this.opensearchService.syncDatabase(collectionData, esIndex); } @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.OK) @UseInterceptors(SubDatasetsPublicInterceptor) @@ -84,12 +84,12 @@ export class ElasticSearchServiceController { }) @Post("/search") async fetchESResults(@Body() searchDto: IDatasetFields) { - return this.elasticSearchService.search(searchDto); + return this.opensearchService.search(searchDto); } @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.OK) @ApiResponse({ @@ -100,12 +100,12 @@ export class ElasticSearchServiceController { async deleteIndex(@Query() { index }: DeleteIndexDto) { const esIndex = index.trim(); - return this.elasticSearchService.deleteIndex(esIndex); + return this.opensearchService.deleteIndex(esIndex); } @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.OK) @ApiResponse({ @@ -116,12 +116,12 @@ export class ElasticSearchServiceController { async getIndex(@Query() { index }: GetIndexDto) { const esIndex = index.trim(); - return this.elasticSearchService.getIndexSettings(esIndex); + return this.opensearchService.getIndexSettings(esIndex); } @UseGuards(PoliciesGuard) - @CheckPolicies("elastic-search", (ability: AppAbility) => - ability.can(Action.Manage, ElasticSearchActions), + @CheckPolicies("opensearch", (ability: AppAbility) => + ability.can(Action.Manage, OpensearchActions), ) @HttpCode(HttpStatus.OK) @ApiResponse({ @@ -132,6 +132,6 @@ export class ElasticSearchServiceController { async updateIndex(@Query() { index }: UpdateIndexDto) { const esIndex = index.trim(); - return this.elasticSearchService.updateIndex(esIndex); + return this.opensearchService.updateIndex(esIndex); } } diff --git a/src/elastic-search/elastic-search.module.ts b/src/opensearch/opensearch.module.ts similarity index 52% rename from src/elastic-search/elastic-search.module.ts rename to src/opensearch/opensearch.module.ts index b3e774ada..21497f941 100644 --- a/src/elastic-search/elastic-search.module.ts +++ b/src/opensearch/opensearch.module.ts @@ -2,14 +2,15 @@ import { Module, forwardRef } from "@nestjs/common"; import { CaslModule } from "src/casl/casl.module"; import { ConfigModule } from "@nestjs/config"; import { DatasetsModule } from "src/datasets/datasets.module"; -import { ElasticSearchServiceController } from "./elastic-search.controller"; -import { ElasticSearchService } from "./elastic-search.service"; + import { SearchQueryService } from "./providers/query-builder.service"; +import { OpensearchService } from "./opensearch.service"; +import { OpensearchController } from "./opensearch.controller"; @Module({ imports: [forwardRef(() => DatasetsModule), ConfigModule, CaslModule], - controllers: [ElasticSearchServiceController], - providers: [ElasticSearchService, SearchQueryService], - exports: [ElasticSearchService, SearchQueryService], + controllers: [OpensearchController], + providers: [OpensearchService, SearchQueryService], + exports: [OpensearchService, SearchQueryService], }) -export class ElasticSearchModule {} +export class OpensearchModule {} diff --git a/src/elastic-search/elastic-search.service.spec.ts b/src/opensearch/opensearch.service.spec.ts similarity index 77% rename from src/elastic-search/elastic-search.service.spec.ts rename to src/opensearch/opensearch.service.spec.ts index b86762852..7b04c1c6f 100644 --- a/src/elastic-search/elastic-search.service.spec.ts +++ b/src/opensearch/opensearch.service.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from "@nestjs/testing"; import { ConfigService } from "@nestjs/config"; -import { ElasticSearchService } from "./elastic-search.service"; +import { OpensearchService } from "./opensearch.service"; import { SearchQueryService } from "./providers/query-builder.service"; import { DatasetsService } from "src/datasets/datasets.service"; @@ -8,8 +8,8 @@ import { DatasetsService } from "src/datasets/datasets.service"; class SearchQueryServiceMock {} class DatasetsServiceMock {} -describe("ElasticSearchService", () => { - let service: ElasticSearchService; +describe("OpensearchService", () => { + let service: OpensearchService; const mockConfigService = { get: () => ({ @@ -24,7 +24,7 @@ describe("ElasticSearchService", () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - ElasticSearchService, + OpensearchService, { provide: ConfigService, useValue: mockConfigService, @@ -37,10 +37,10 @@ describe("ElasticSearchService", () => { ], }).compile(); - service = module.get(ElasticSearchService); + service = module.get(OpensearchService); }); - it("should properly load ElasticSearchService", () => { + it("should properly load OpensearchService", () => { expect(service).toBeDefined(); }); }); diff --git a/src/elastic-search/elastic-search.service.ts b/src/opensearch/opensearch.service.ts similarity index 59% rename from src/elastic-search/elastic-search.service.ts rename to src/opensearch/opensearch.service.ts index 2d7f66355..671a29147 100644 --- a/src/elastic-search/elastic-search.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -10,10 +10,7 @@ import { Client } from "@opensearch-project/opensearch"; import { SearchQueryService } from "./providers/query-builder.service"; import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; -import { - defaultElasticSettings, - dynamic_template, -} from "./configuration/indexSetting"; +import { defaultOpensearchSettings } from "./configuration/indexSetting"; import { datasetMappings } from "./configuration/datasetFieldMapping"; import { DatasetClass, @@ -21,18 +18,15 @@ import { } from "src/datasets/schemas/dataset.schema"; import { ConfigService } from "@nestjs/config"; import { sleep } from "src/common/utils"; -import { - initialSyncTransform, - transformFacets, - addValueType, -} from "./helpers/utils"; +import { transformFacets } from "./helpers/utils"; import { SortFields } from "./providers/fields.enum"; import { SortOrder } from "@opensearch-project/opensearch/api/_types/ml._common"; +import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; @Injectable() -export class ElasticSearchService implements OnModuleInit { - private osService: Client; +export class OpensearchService implements OnModuleInit { + private osClient: Client; private host: string; private username: string; private password: string; @@ -45,29 +39,27 @@ export class ElasticSearchService implements OnModuleInit { private readonly searchService: SearchQueryService, private readonly configService: ConfigService, ) { - this.host = this.configService.get("elasticSearch.host") || ""; - this.username = - this.configService.get("elasticSearch.username") || ""; - this.password = - this.configService.get("elasticSearch.password") || ""; + this.host = this.configService.get("opensearch.host") || ""; + this.username = this.configService.get("opensearch.username") || ""; + this.password = this.configService.get("opensearch.password") || ""; this.esEnabled = - this.configService.get("elasticSearch.enabled") === "yes" + this.configService.get("opensearch.enabled") === "yes" ? true : false; this.refresh = - this.configService.get<"false" | "wait_for">("elasticSearch.refresh") || + this.configService.get<"false" | "wait_for">("opensearch.refresh") || "false"; this.defaultIndex = - this.configService.get("elasticSearch.defaultIndex") || ""; + this.configService.get("opensearch.defaultIndex") || ""; if ( this.esEnabled && (!this.host || !this.username || !this.password || !this.defaultIndex) ) { Logger.error( - "Missing ENVIRONMENT variables for elastic search connection", - "ElasticSearch", + "Missing ENVIRONMENT variables for opensearch connection", + "Opensearch", ); } } @@ -84,15 +76,12 @@ export class ElasticSearchService implements OnModuleInit { if (!isIndexExists) { await this.createIndex(this.defaultIndex); - Logger.log( - `New index ${this.defaultIndex}is created `, - "ElasticSearch", - ); + Logger.log(`New index ${this.defaultIndex}is created `, "Opensearch"); } this.connected = true; - Logger.log("Elasticsearch Connected", "ElasticSearch"); + Logger.log("Opensearch Connected", "Opensearch"); } catch (error) { - Logger.error(error, "onModuleInit failed-> ElasticSearchService"); + Logger.error(error, "onModuleInit failed-> OpensearchService"); } } @@ -110,7 +99,7 @@ export class ElasticSearchService implements OnModuleInit { await connection.ping(); - this.osService = connection; + this.osClient = connection; } private async retryConnection(maxRetries: number, interval: number) { let retryCount = 0; @@ -127,50 +116,41 @@ export class ElasticSearchService implements OnModuleInit { if (retryCount === maxRetries) { throw new HttpException( - "Max retries reached; check Elasticsearch config.", + "Max retries reached; check Opensearch config.", HttpStatus.BAD_REQUEST, ); } } async isIndexExists(index = this.defaultIndex) { - return await this.osService.indices.exists({ + return await this.osClient.indices.exists({ index, }); } async createIndex(index = this.defaultIndex) { try { - await this.osService.indices.create({ + await this.osClient.indices.create({ index, body: { - settings: defaultElasticSettings, + settings: defaultOpensearchSettings as IndexSettings, mappings: { - dynamic: "true", - dynamic_templates: dynamic_template, - numeric_detection: false, - date_detection: false, - dynamic_date_formats: [ - "yyyy-MM-dd'T'HH:mm:ss|| yyyy-MM-dd HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSSZ||yyyy-MM-dd'T'HH:mm:ss.SSS'Z'||yyyy-MM-dd'T'HH:mm:ss.SSS", - ], + dynamic: "false", properties: datasetMappings, }, }, }); - Logger.log( - `Elasticsearch Index Created-> Index: ${index}`, - "Elasticsearch", - ); + Logger.log(`Opensearch Index Created-> Index: ${index}`, "Opensearch"); return HttpStatus.CREATED; } catch (error) { throw new HttpException( - `createIndex failed-> ElasticSearchService ${error}`, + `createIndex failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } } async syncDatabase(collection: DatasetClass[], index = this.defaultIndex) { - const indexExists = await this.osService.indices.exists({ index }); + const indexExists = await this.osClient.indices.exists({ index }); if (!indexExists) { throw new Error("Index not found"); } @@ -179,7 +159,7 @@ export class ElasticSearchService implements OnModuleInit { Logger.log( JSON.stringify(bulkResponse, null, 0), - "Elasticsearch Data Synchronization Response", + "Opensearch Data Synchronization Response", ); return bulkResponse; @@ -187,10 +167,10 @@ export class ElasticSearchService implements OnModuleInit { async getCount(index = this.defaultIndex) { try { - return await this.osService.count({ index }); + return await this.osClient.count({ index }); } catch (error) { throw new HttpException( - `getCount failed-> ElasticSearchService ${error}`, + `getCount failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -198,32 +178,28 @@ export class ElasticSearchService implements OnModuleInit { async updateIndex(index = this.defaultIndex) { try { - await this.osService.indices.close({ + await this.osClient.indices.close({ index, }); - await this.osService.indices.putSettings({ + await this.osClient.indices.putSettings({ index, - body: { settings: defaultElasticSettings }, + body: { settings: defaultOpensearchSettings as IndexSettings }, }); - await this.osService.indices.putMapping({ + await this.osClient.indices.putMapping({ index, body: { properties: datasetMappings, - dynamic_templates: dynamic_template, }, }); - await this.osService.indices.open({ + await this.osClient.indices.open({ index, }); - Logger.log( - `Elasticsearch Index Updated-> Index: ${index}`, - "Elasticsearch", - ); + Logger.log(`Opensearch Index Updated-> Index: ${index}`, "Opensearch"); } catch (error) { throw new HttpException( - `updateIndex failed-> ElasticSearchService ${error}`, + `updateIndex failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -231,10 +207,10 @@ export class ElasticSearchService implements OnModuleInit { async getIndexSettings(index = this.defaultIndex) { try { - return await this.osService.indices.getSettings({ index }); + return await this.osClient.indices.getSettings({ index }); } catch (error) { throw new HttpException( - `getIndexSettings failed-> ElasticSearchService ${error}`, + `getIndexSettings failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -242,15 +218,12 @@ export class ElasticSearchService implements OnModuleInit { async deleteIndex(index = this.defaultIndex) { try { - await this.osService.indices.delete({ index }); - Logger.log( - `Elasticsearch Index Deleted-> Index: ${index} `, - "Elasticsearch", - ); + await this.osClient.indices.delete({ index }); + Logger.log(`Opensearch Index Deleted-> Index: ${index} `, "Opensearch"); return { success: true, message: `Index ${index} deleted` }; } catch (error) { throw new HttpException( - `deleteIndex failed-> ElasticSearchService ${error}`, + `deleteIndex failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -265,7 +238,7 @@ export class ElasticSearchService implements OnModuleInit { const defaultMinScore = searchParam.text ? 1 : 0; try { - const isSortEmpty = !sort || JSON.stringify(sort) === "{}"; + // const isSortEmpty = !sort || JSON.stringify(sort) === "{}"; const searchQuery = this.searchService.buildSearchQuery(searchParam); const searchOptions = { track_scores: true, @@ -281,49 +254,29 @@ export class ElasticSearchService implements OnModuleInit { _source: [""], }; - if (!isSortEmpty) { - const sortField = Object.keys(sort)[0]; - const sortDirection = Object.values(sort)[0]; - - // NOTE: To sort datasetName field we need to use datasetName.keyword field, - // as elasticsearch does not have good support for text type field sorting - const isDatasetName = sortField === SortFields.DatasetName; - const fieldForSorting = isDatasetName - ? SortFields.DatasetNameKeyword - : sortField; - - const isNestedField = fieldForSorting.includes( - SortFields.ScientificMetadata, - ); - - if (isNestedField) { - searchOptions.sort = [ - { - [`${SortFields.ScientificMetadataRunNumberValue}`]: { - order: sortDirection, - nested: { - path: SortFields.ScientificMetadata, - }, - }, - }, - ]; - } else { - searchOptions.sort = [ - { [fieldForSorting]: { order: sortDirection } }, - ]; - } - } + // if (!isSortEmpty) { + // const sortField = Object.keys(sort)[0]; + // const sortDirection = Object.values(sort)[0]; + + // // NOTE: To sort datasetName field we need to use datasetName.keyword field, + // // as Opensearch does not have good support for text type field sorting + // const isDatasetName = sortField === SortFields.DatasetName; + // const fieldForSorting = isDatasetName + // ? SortFields.DatasetNameKeyword + // : sortField; + + // searchOptions.sort = [{ [fieldForSorting]: { order: sortDirection } }]; + // } - const body = (await this.osService.search({ + const { body } = await this.osClient.search({ index: this.defaultIndex, body: searchOptions, - } as any)) as any; + }); - console.log("---body---", body.body.hits); + const totalCount = body.hits.hits.length || 0; - const totalCount = body.body.hits.hits.length || 0; + const data = body.hits.hits.map((item) => item._id || ""); - const data = body.body.hits.hits.map((item: any) => item._id || ""); return { totalCount, data, @@ -348,10 +301,10 @@ export class ElasticSearchService implements OnModuleInit { _source: [""], }; - const body = (await this.osService.search({ + const { body } = await this.osClient.search({ index: this.defaultIndex, body: searchOptions, - } as any)) as any; + }); const transformedFacets = transformFacets(body.aggregations || {}); @@ -366,29 +319,22 @@ export class ElasticSearchService implements OnModuleInit { async updateInsertDocument(data: Partial) { //NOTE: Replace all keys with lower case, also replace spaces and dot with underscore delete data._id; - const transformedScientificMetadata = addValueType( - data.scientificMetadata as Record, - ); - const transformedData = { - ...data, - scientificMetadata: transformedScientificMetadata, - }; try { - await this.osService.index({ + await this.osClient.index({ index: this.defaultIndex, id: data.pid, - body: transformedData, + body: data, refresh: this.refresh, }); Logger.log( `Document Update/inserted-> Document_id: ${data.pid} update/inserted on index: ${this.defaultIndex}`, - "Elasticsearch", + "Opensearch", ); } catch (error) { throw new HttpException( - `updateDocument failed-> ElasticSearchService ${error}`, + `updateDocument failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -396,18 +342,18 @@ export class ElasticSearchService implements OnModuleInit { async deleteDocument(id: string) { try { - await this.osService.delete({ + await this.osClient.delete({ index: this.defaultIndex, id, refresh: this.refresh, }); Logger.log( `Document Deleted-> Document_id: ${id} deleted on index: ${this.defaultIndex}`, - "Elasticsearch", + "Opensearch", ); } catch (error) { throw new HttpException( - `deleteDocument failed-> ElasticSearchService ${error}`, + `deleteDocument failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -416,7 +362,7 @@ export class ElasticSearchService implements OnModuleInit { // *** NOTE: below are helper methods *** async performBulkOperation(collection: DatasetClass[], index: string) { - const result = await this.osService.helpers.bulk({ + const result = await this.osClient.helpers.bulk({ retries: 5, wait: 10000, datasource: collection, @@ -428,7 +374,7 @@ export class ElasticSearchService implements OnModuleInit { _id: doc.pid, }, }, - initialSyncTransform(doc), + doc, ]; }, onDrop(doc) { diff --git a/src/elastic-search/Elasticsearch_guide.md b/src/opensearch/opensearch_guide.md similarity index 100% rename from src/elastic-search/Elasticsearch_guide.md rename to src/opensearch/opensearch_guide.md diff --git a/src/elastic-search/providers/fields.enum.ts b/src/opensearch/providers/fields.enum.ts similarity index 100% rename from src/elastic-search/providers/fields.enum.ts rename to src/opensearch/providers/fields.enum.ts diff --git a/src/elastic-search/providers/query-builder.service.spec.ts b/src/opensearch/providers/query-builder.service.spec.ts similarity index 100% rename from src/elastic-search/providers/query-builder.service.spec.ts rename to src/opensearch/providers/query-builder.service.spec.ts diff --git a/src/elastic-search/providers/query-builder.service.ts b/src/opensearch/providers/query-builder.service.ts similarity index 74% rename from src/elastic-search/providers/query-builder.service.ts rename to src/opensearch/providers/query-builder.service.ts index 1a0521973..ade6ddc83 100644 --- a/src/elastic-search/providers/query-builder.service.ts +++ b/src/opensearch/providers/query-builder.service.ts @@ -1,14 +1,6 @@ import { Injectable, Logger } from "@nestjs/common"; import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; -import { - IBoolShould, - IFilter, - IFullFacets, - IShould, - NumberRangeType, - ObjectType, - ScientificQuery, -} from "../interfaces/es-common.type"; +import { NumberRangeType, ObjectType } from "../interfaces/os-common.type"; import { FilterFields, MustFields, @@ -16,9 +8,8 @@ import { ShouldFields, } from "./fields.enum"; -import { mapScientificQuery } from "src/common/utils"; -import { IScientificFilter } from "src/common/interfaces/common.interface"; -import { convertToElasticSearchQuery } from "../helpers/utils"; +import { AggregationContainer } from "@opensearch-project/opensearch/api/_types/_common.aggregations"; +import { QueryContainer } from "@opensearch-project/opensearch/api/_types/_common.query_dsl"; @Injectable() export class SearchQueryService { @@ -46,8 +37,8 @@ export class SearchQueryService { throw err; } } - private buildFilterFields(fields: Partial): IFilter[] { - const filter: IFilter[] = []; + private buildFilterFields(fields: Partial): QueryContainer[] { + const filter: QueryContainer[] = []; Object.entries(fields).forEach(([key, value]) => { if (this.shouldFields.includes(key as ShouldFields) || key === "text") { @@ -61,8 +52,8 @@ export class SearchQueryService { return filter; } - private buildShouldFields(fields: Partial) { - const shouldFilter: IShould[] = []; + private buildShouldFields(fields: Partial): QueryContainer { + const shouldFilter = []; if (fields["sharedWith"]) { const termFilter = { terms: { sharedWith: fields["sharedWith"] } }; @@ -78,10 +69,8 @@ export class SearchQueryService { return { bool: { should: shouldFilter, minimum_should_match: 1 } }; } - private buildTextQuery( - fields: Partial, - ): Record[] { - let wildcardQueries: Record[] = []; + private buildTextQuery(fields: Partial): QueryContainer[] { + let wildcardQueries: QueryContainer[] = []; const { text } = fields; //NOTE: if text field is present, we query both datasetName and description fields @@ -102,7 +91,7 @@ export class SearchQueryService { .filter(Boolean); } - private buildWildcardQueries(text: string): Record[] { + private buildWildcardQueries(text: string): QueryContainer[] { const terms = this.splitSearchText(text); return terms.flatMap((term) => this.mustFields.map((fieldName) => ({ @@ -111,35 +100,17 @@ export class SearchQueryService { ); } - private buildTermsFilter(fieldName: string, values: unknown) { - const filterArray: IFilter[] = []; + private buildTermsFilter( + fieldName: string, + values: unknown, + ): QueryContainer[] { + const filterArray: QueryContainer[] = []; if (Array.isArray(values) && values.length === 0) { return filterArray; } switch (fieldName) { - case FilterFields.ScientificMetadata: - const scientificFilterQuery = mapScientificQuery( - fieldName, - values as IScientificFilter[], - ); - - const esScientificFilterQuery = convertToElasticSearchQuery( - scientificFilterQuery as ScientificQuery, - ); - filterArray.push({ - nested: { - path: "scientificMetadata", - query: { - bool: { - must: esScientificFilterQuery, - }, - }, - }, - }); - break; - case FilterFields.CreationTime: filterArray.push({ range: { @@ -208,9 +179,9 @@ export class SearchQueryService { } private constructFinalQuery( - filter: IFilter[], - should: IBoolShould, - query: Record[], + filter: QueryContainer[], + should: QueryContainer, + query: QueryContainer[], ) { const finalQuery = { query: { @@ -223,19 +194,19 @@ export class SearchQueryService { return finalQuery; } - public buildFullFacetPipeline(facetFields = this.facetFields) { - const pipeline: IFullFacets = { + public buildFullFacetPipeline( + facetFields = this.facetFields, + ): Record { + const pipeline: Record = { all: { - value_count: { - field: "pid", - }, + value_count: { field: "pid" }, }, }; for (const field of facetFields) { pipeline[field] = { terms: { - field: field, + field, order: { _count: "desc", }, From 8f62b6d2519e73a918f769fd79e3ce4c59c1f885 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Mon, 9 Mar 2026 15:09:13 +0100 Subject: [PATCH 04/46] refactor opensearch query logic --- src/common/utils.ts | 14 + src/datasets/datasets.controller.ts | 36 ++- src/datasets/datasets.service.ts | 134 ++++----- .../configuration/datasetFieldMapping.ts | 69 +---- src/opensearch/helpers/utils.ts | 30 --- src/opensearch/interfaces/os-common.type.ts | 14 +- src/opensearch/opensearch.controller.ts | 17 +- src/opensearch/opensearch.service.ts | 56 +--- src/opensearch/providers/fields.enum.ts | 30 --- .../providers/query-builder.service.ts | 188 +++---------- test/ElasticSearch.js | 254 ------------------ test/OpenSearch.js | 61 +++++ test/TestData.js | 122 ++------- 13 files changed, 252 insertions(+), 773 deletions(-) delete mode 100644 src/opensearch/helpers/utils.ts delete mode 100644 test/ElasticSearch.js create mode 100644 test/OpenSearch.js diff --git a/src/common/utils.ts b/src/common/utils.ts index fc1e48234..5f2567af8 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -700,6 +700,17 @@ export const createFullqueryFilter = ( }; const pipelineHandler = { + handleOpensearchIdList: ( + pipeline: PipelineStage[], + fields: T, + key: string, + ) => { + const match = { + $match: { _id: { $in: fields[key as keyof T] as string[] } }, + }; + return pipeline.unshift(match); + }, + handleTextSearch: ( pipeline: PipelineStage[], model: Model, @@ -836,6 +847,9 @@ export const createFullfacetPipeline = ( } switch (key) { + case "openSearchIdList": + pipelineHandler.handleOpensearchIdList(pipeline, fields, key); + break; case "text": pipelineHandler.handleTextSearch(pipeline, model, fields, key); break; diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index 0d179fafc..024b70d9b 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -969,6 +969,11 @@ export class DatasetsController { const user: JWTUser = request.user as JWTUser; const fields: IDatasetFields = JSON.parse(filters.fields ?? "{}"); + const parsedFilters: IFilters = { + fields: fields, + limits: JSON.parse(filters.limits ?? "{}"), + }; + const ability = this.caslAbilityFactory.datasetInstanceAccess(user); const canViewAny = ability.can(Action.DatasetReadAny, DatasetClass); @@ -985,12 +990,22 @@ export class DatasetsController { } } - const parsedFilters: IFilters = { - fields: fields, - limits: JSON.parse(filters.limits ?? "{}"), - }; + let datasets: DatasetDocument[] | null; + + const osEnabled = this.configService.get("opensearch.enabled"); + const textSearch = parsedFilters.fields?.text; + const isOpenSearchPopulated = + await this.datasetsService.isOpenSearchPopulated(); - const datasets = await this.datasetsService.fullquery(parsedFilters); + if (osEnabled && textSearch && isOpenSearchPopulated) { + const isAdmin = canViewAny; + datasets = await this.datasetsService.opensearchQuery( + parsedFilters, + isAdmin, + ); + } else { + datasets = await this.datasetsService.fullquery(parsedFilters); + } let outputDatasets: OutputDatasetObsoleteDto[] = []; @@ -1065,6 +1080,17 @@ export class DatasetsController { fields: fields, facets: JSON.parse(filters.facets ?? "[]"), }; + + const osEnabled = this.configService.get("opensearch.enabled"); + const textSearch = parsedFilters.fields?.text; + const isOpenSearchPopulated = + await this.datasetsService.isOpenSearchPopulated(); + + if (osEnabled && textSearch && isOpenSearchPopulated) { + const isAdmin = canViewAny; + return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); + } + return this.datasetsService.fullFacet(parsedFilters); } diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 31acb698f..6acdeec5f 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -66,7 +66,7 @@ import { OpensearchService } from "src/opensearch/opensearch.service"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { - private searchService: OpensearchService | null; + private searchService: OpensearchService | null = null; constructor( private configService: ConfigService, @InjectModel(DatasetClass.name) @@ -78,6 +78,8 @@ export class DatasetsService { private metadataKeysService: MetadataKeysService, private proposalService: ProposalsService, ) { + // TODO: Verify if this connection check is needed. + // OpenSearch should fallback to MongoDB if unavailable. if (this.opensearchService.connected) { this.searchService = this.opensearchService; } @@ -277,8 +279,6 @@ export class DatasetsService { filter: IFilters, extraWhereClause: FilterQuery = {}, ): Promise { - let datasets; - const filterQuery: FilterQuery = createFullqueryFilter( this.datasetModel, @@ -292,31 +292,44 @@ export class DatasetsService { }; const modifiers: QueryOptions = parseLimitFilters(filter.limits); - const isFieldsEmpty = Object.keys(whereClause).length === 0; + const datasets = await this.datasetModel + .find(whereClause, null, modifiers) + .exec(); - // NOTE: if Elastic search DB is empty we should use default mongo query - const canPerformElasticSearchQueries = await this.isElasticSearchDBEmpty(); + return datasets; + } - if ( - !this.searchService || - isFieldsEmpty || - !canPerformElasticSearchQueries - ) { - datasets = await this.datasetModel - .find(whereClause, null, modifiers) - .exec(); - } else { - const esResult = await this.searchService.search( - filter.fields as IDatasetFields, - modifiers.limit, - modifiers.skip, - modifiers.sort, - ); - datasets = await this.datasetModel - .find({ pid: { $in: esResult.data } }) - .sort(modifiers.sort) - .exec(); + async opensearchQuery( + filter: IFilters, + isAdmin: boolean, + ): Promise { + // if no text field is provided, we fallback to full query + if (!this.searchService || !filter.fields) { + return this.fullquery(filter); } + const { text, userGroups } = filter.fields || {}; + + const mongoQuery: FilterQuery = + createFullqueryFilter( + this.datasetModel, + "pid", + filter.fields as FilterQuery, + ); + + const modifiers: QueryOptions = parseLimitFilters(filter.limits); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { $text, ...otherQueries } = mongoQuery; + + const osResult = await this.searchService.search( + { text, userGroups, isAdmin: isAdmin }, + modifiers.limit, + modifiers.skip, + ); + const datasets = await this.datasetModel + .find({ pid: { $in: osResult.data }, ...otherQueries }) + .sort(modifiers.sort) + .exec(); return datasets; } @@ -324,38 +337,45 @@ export class DatasetsService { async fullFacet( filters: IFacets, ): Promise[]> { - let data; - const fields = filters.fields ?? {}; const facets = filters.facets ?? []; - // NOTE: if fields contains no value, we should use mongo query to optimize performance. - // however, fields always contain "mode" key, so we need to check if there's more than one key - const isFieldsEmpty = Object.keys(fields).length === 1; + const pipeline = createFullfacetPipeline( + this.datasetModel, + "pid", + fields, + facets, + "", + ); - // NOTE: if Elastic search DB is empty we should use default mongo query - const canPerformElasticSearchQueries = await this.isElasticSearchDBEmpty(); + return await this.datasetModel.aggregate(pipeline).exec(); + } - if ( - !this.searchService || - isFieldsEmpty || - !canPerformElasticSearchQueries - ) { - const pipeline = createFullfacetPipeline( - this.datasetModel, - "pid", - fields, - facets, - "", - ); + async opensearchFacet( + filters: IFacets, + isAdmin: boolean, + ): Promise[]> { + if (!this.searchService || !filters.fields) { + return this.fullFacet(filters); + } + const { text, userGroups } = filters.fields || {}; + const osResult = await this.searchService.search({ + text, + userGroups, + isAdmin: isAdmin, + }); - data = await this.datasetModel.aggregate(pipeline).exec(); - } else { - const facetResult = await this.searchService.aggregate(fields); + filters.fields.openSearchIdList = osResult.data; + delete filters.fields.text; + const pipeline = createFullfacetPipeline( + this.datasetModel, + "pid", + filters.fields ?? {}, + filters.facets ?? [], + "", + ); - data = facetResult; - } - return data; + return await this.datasetModel.aggregate(pipeline).exec(); } async updateAll( @@ -395,17 +415,7 @@ export class DatasetsService { ): Promise<{ count: number }> { const whereFilter: RootFilterQuery = filter.where ?? {}; let count = 0; - if (this.searchService && !filter.where) { - const totalDocCount = await this.datasetModel.countDocuments(); - - const { totalCount } = await this.searchService.search( - whereFilter as IDatasetFields, - totalDocCount, - ); - count = totalCount; - } else { - count = await this.datasetModel.countDocuments(whereFilter).exec(); - } + count = await this.datasetModel.countDocuments(whereFilter).exec(); return { count }; } @@ -606,7 +616,7 @@ export class DatasetsService { } } - async isElasticSearchDBEmpty() { + async isOpenSearchPopulated() { if (!this.searchService) return; const count = await this.searchService.getCount(); return count.body.count > 0; diff --git a/src/opensearch/configuration/datasetFieldMapping.ts b/src/opensearch/configuration/datasetFieldMapping.ts index d9ed15b07..5f0d3ded9 100644 --- a/src/opensearch/configuration/datasetFieldMapping.ts +++ b/src/opensearch/configuration/datasetFieldMapping.ts @@ -15,48 +15,10 @@ export const datasetMappings: MappingObject = { type: "keyword", ignore_above: 256, }, - creationTime: { - type: "date", - }, - endTime: { - type: "date", - }, - // scientificMetadata: { - // type: "nested", - // dynamic: true, - // properties: {}, - // }, - proposalIds: { - type: "keyword", - ignore_above: 256, - }, - sampleIds: { - type: "keyword", - ignore_above: 256, - }, - instrumentIds: { - type: "keyword", - ignore_above: 256, - }, - sourceFolder: { - type: "keyword", - ignore_above: 256, - }, + isPublished: { type: "boolean", }, - type: { - type: "keyword", - ignore_above: 256, - }, - keywords: { - type: "keyword", - ignore_above: 256, - }, - creationLocation: { - type: "keyword", - ignore_above: 256, - }, ownerGroup: { type: "keyword", ignore_above: 256, @@ -65,33 +27,4 @@ export const datasetMappings: MappingObject = { type: "keyword", ignore_above: 256, }, - sharedWith: { - type: "keyword", - ignore_above: 256, - }, - ownerEmail: { - type: "keyword", - ignore_above: 256, - }, - size: { - type: "long", - }, - runNumber: { - type: "keyword", - ignore_above: 256, - }, - // NOTE: below fields are for backward compatibility - // and should be removed when obsolete dto is removed - proposalId: { - type: "keyword", - ignore_above: 256, - }, - sampleId: { - type: "keyword", - ignore_above: 256, - }, - instrumentId: { - type: "keyword", - ignore_above: 256, - }, }; diff --git a/src/opensearch/helpers/utils.ts b/src/opensearch/helpers/utils.ts deleted file mode 100644 index d3905c5b3..000000000 --- a/src/opensearch/helpers/utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - AggregateBase, - SingleBucketAggregateBase, -} from "@opensearch-project/opensearch/api/_types/_common.aggregations"; - -export const transformKey = (key: string): string => { - return key.trim().replace(/[.]/g, "\\.").replace(/ /g, "_").toLowerCase(); -}; - -export const transformFacets = ( - aggregation: AggregateBase, -): Record[] => { - const transformed = Object.entries(aggregation).reduce( - (acc, [key, value]) => { - const isBucketArray = Array.isArray(value.buckets); - - acc[key] = isBucketArray - ? value.buckets.map((bucket: SingleBucketAggregateBase) => ({ - _id: bucket.key, - count: bucket.doc_count, - })) - : [{ totalSets: value.value }]; - - return acc; - }, - {} as any, - ); - - return [transformed]; -}; diff --git a/src/opensearch/interfaces/os-common.type.ts b/src/opensearch/interfaces/os-common.type.ts index 5344ef251..dd118820b 100644 --- a/src/opensearch/interfaces/os-common.type.ts +++ b/src/opensearch/interfaces/os-common.type.ts @@ -1,9 +1,5 @@ -export type ObjectType = { - begin: string; - end: string; -}; - -export type NumberRangeType = { - min: string; - max: string; -}; +export interface ISearchFilter { + isAdmin?: boolean; + userGroups?: string[]; + text?: string; +} diff --git a/src/opensearch/opensearch.controller.ts b/src/opensearch/opensearch.controller.ts index fb9ed78e7..52365f3f3 100644 --- a/src/opensearch/opensearch.controller.ts +++ b/src/opensearch/opensearch.controller.ts @@ -3,25 +3,22 @@ import { HttpCode, HttpStatus, Post, - Body, Get, Query, UseInterceptors, UseGuards, } from "@nestjs/common"; -import { ApiTags, ApiBearerAuth, ApiBody, ApiResponse } from "@nestjs/swagger"; +import { ApiTags, ApiBearerAuth, ApiResponse, ApiQuery } from "@nestjs/swagger"; import { Action } from "src/casl/action.enum"; import { AppAbility } from "src/casl/casl-ability.factory"; import { CheckPolicies } from "src/casl/decorators/check-policies.decorator"; import { PoliciesGuard } from "src/casl/guards/policies.guard"; import { DatasetsService } from "src/datasets/datasets.service"; import { SubDatasetsPublicInterceptor } from "src/datasets/interceptors/datasets-public.interceptor"; -import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; import { CreateIndexDto } from "./dto/create-index.dto"; import { DeleteIndexDto } from "./dto/delete-index.dto"; import { GetIndexDto } from "./dto/get-index.dto"; -import { SearchDto } from "./dto/search.dto"; import { SyncDatabaseDto } from "./dto/sync-data.dto"; import { UpdateIndexDto } from "./dto/update-index.dto"; import { OpensearchActions } from "./dto"; @@ -75,16 +72,18 @@ export class OpensearchController { ) @HttpCode(HttpStatus.OK) @UseInterceptors(SubDatasetsPublicInterceptor) - @ApiBody({ - type: SearchDto, + @ApiQuery({ + name: "textQuery", + description: "Partial search text for datasetName and description fields", + type: String, }) @ApiResponse({ status: 200, - description: "Search with elasticsearch to get restuls in PIDs", + description: "Search with opensearch to get results in PIDs", }) @Post("/search") - async fetchESResults(@Body() searchDto: IDatasetFields) { - return this.opensearchService.search(searchDto); + async fetchOSResults(@Query("textQuery") textQuery: string) { + return this.opensearchService.search({ text: textQuery }); } @UseGuards(PoliciesGuard) diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 671a29147..fa3b8279e 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -9,7 +9,6 @@ import { Client } from "@opensearch-project/opensearch"; import { SearchQueryService } from "./providers/query-builder.service"; -import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; import { defaultOpensearchSettings } from "./configuration/indexSetting"; import { datasetMappings } from "./configuration/datasetFieldMapping"; import { @@ -18,11 +17,9 @@ import { } from "src/datasets/schemas/dataset.schema"; import { ConfigService } from "@nestjs/config"; import { sleep } from "src/common/utils"; -import { transformFacets } from "./helpers/utils"; -import { SortFields } from "./providers/fields.enum"; -import { SortOrder } from "@opensearch-project/opensearch/api/_types/ml._common"; import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import { ISearchFilter } from "./interfaces/os-common.type"; @Injectable() export class OpensearchService implements OnModuleInit { @@ -230,16 +227,12 @@ export class OpensearchService implements OnModuleInit { } async search( - searchParam: IDatasetFields, + filter: ISearchFilter, limit = 20, skip = 0, - sort?: Record, ): Promise<{ totalCount: number; data: (string | undefined)[] }> { - const defaultMinScore = searchParam.text ? 1 : 0; - try { - // const isSortEmpty = !sort || JSON.stringify(sort) === "{}"; - const searchQuery = this.searchService.buildSearchQuery(searchParam); + const searchQuery = this.searchService.buildSearchQuery(filter); const searchOptions = { track_scores: true, sort: [{ _score: { order: "desc" } }] as unknown as Record< @@ -249,25 +242,11 @@ export class OpensearchService implements OnModuleInit { query: searchQuery.query, from: skip, size: limit, - min_score: defaultMinScore, + min_score: 1, track_total_hits: true, _source: [""], }; - // if (!isSortEmpty) { - // const sortField = Object.keys(sort)[0]; - // const sortDirection = Object.values(sort)[0]; - - // // NOTE: To sort datasetName field we need to use datasetName.keyword field, - // // as Opensearch does not have good support for text type field sorting - // const isDatasetName = sortField === SortFields.DatasetName; - // const fieldForSorting = isDatasetName - // ? SortFields.DatasetNameKeyword - // : sortField; - - // searchOptions.sort = [{ [fieldForSorting]: { order: sortDirection } }]; - // } - const { body } = await this.osClient.search({ index: this.defaultIndex, body: searchOptions, @@ -289,33 +268,6 @@ export class OpensearchService implements OnModuleInit { } } - async aggregate(searchParam: IDatasetFields) { - try { - const searchQuery = this.searchService.buildSearchQuery(searchParam); - const facetPipeline = this.searchService.buildFullFacetPipeline(); - - const searchOptions = { - query: searchQuery.query, - size: 0, - aggs: facetPipeline, - _source: [""], - }; - - const { body } = await this.osClient.search({ - index: this.defaultIndex, - body: searchOptions, - }); - - const transformedFacets = transformFacets(body.aggregations || {}); - - return transformedFacets; - } catch (error) { - throw new HttpException( - `SearchService || aggregate query issue || -> aggregate ${error}`, - HttpStatus.BAD_REQUEST, - ); - } - } async updateInsertDocument(data: Partial) { //NOTE: Replace all keys with lower case, also replace spaces and dot with underscore delete data._id; diff --git a/src/opensearch/providers/fields.enum.ts b/src/opensearch/providers/fields.enum.ts index 8030407ab..637db68a6 100644 --- a/src/opensearch/providers/fields.enum.ts +++ b/src/opensearch/providers/fields.enum.ts @@ -1,38 +1,8 @@ -export enum FilterFields { - Keywords = "keywords", - Pid = "pid", - Type = "type", - CreationLocation = "creationLocation", - CreationTime = "creationTime", - OwnerGroup = "ownerGroup", - AccessGroups = "accessGroups", - ScientificMetadata = "scientific", - IsPublished = "isPublished", - DatasetName = "datasetName", - Mode = "mode", -} - export enum MustFields { DatasetName = "datasetName", Description = "description", } export enum ShouldFields { - SharedWith = "sharedWith", UserGroups = "userGroups", } - -export enum FacetFields { - Type = "type", - CreationLocation = "creationLocation", - OwnerGroup = "ownerGroup", - AccessGroups = "accessGroups", - Keywords = "keywords", -} - -export enum SortFields { - DatasetName = "datasetName", - DatasetNameKeyword = "datasetName.keyword", - ScientificMetadata = "scientificMetadata", - ScientificMetadataRunNumberValue = "scientificMetadata.runnumber.value", -} diff --git a/src/opensearch/providers/query-builder.service.ts b/src/opensearch/providers/query-builder.service.ts index ade6ddc83..9b3e52f00 100644 --- a/src/opensearch/providers/query-builder.service.ts +++ b/src/opensearch/providers/query-builder.service.ts @@ -1,81 +1,59 @@ import { Injectable, Logger } from "@nestjs/common"; -import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; -import { NumberRangeType, ObjectType } from "../interfaces/os-common.type"; -import { - FilterFields, - MustFields, - FacetFields, - ShouldFields, -} from "./fields.enum"; +import { MustFields } from "./fields.enum"; -import { AggregationContainer } from "@opensearch-project/opensearch/api/_types/_common.aggregations"; import { QueryContainer } from "@opensearch-project/opensearch/api/_types/_common.query_dsl"; +import { ISearchFilter } from "../interfaces/os-common.type"; @Injectable() export class SearchQueryService { - readonly filterFields = [...Object.values(FilterFields)]; readonly mustFields = [...Object.values(MustFields)]; - readonly shouldFields = [...Object.values(ShouldFields)]; - readonly facetFields = [...Object.values(FacetFields)]; readonly textQuerySplitMethod = /[ ,]+/; - public buildSearchQuery(searchParam: IDatasetFields) { + public buildSearchQuery(filter: ISearchFilter) { try { - const { ...fields } = searchParam; - - const filter = this.buildFilterFields(fields); - const should = this.buildShouldFields(fields); - const must = this.buildTextQuery(fields); + const accessFilter = this.buildFilterFields(filter); + const textQuery = this.buildTextQuery(filter); // NOTE: The final query flow is as follows: // step 1. Build filter fields conditions must match all filter fields // step 2. Build should fields conditions must match at least one should field // step 3. Build text query conditions must match all text query fields - return this.constructFinalQuery(filter, should, must); + return this.constructFinalQuery(accessFilter, textQuery); } catch (err) { - Logger.error("Elastic search build search query failed"); + Logger.error("Open search build search query failed", err); throw err; } } - private buildFilterFields(fields: Partial): QueryContainer[] { + private buildFilterFields(fields: ISearchFilter): QueryContainer[] { const filter: QueryContainer[] = []; - Object.entries(fields).forEach(([key, value]) => { - if (this.shouldFields.includes(key as ShouldFields) || key === "text") { - return; - } - - const filterQueries = this.buildTermsFilter(key, value); - filter.push(...filterQueries); - }); - - return filter; - } - - private buildShouldFields(fields: Partial): QueryContainer { - const shouldFilter = []; - if (fields["sharedWith"]) { - const termFilter = { terms: { sharedWith: fields["sharedWith"] } }; - - shouldFilter.push(termFilter); + if (fields.userGroups) { + filter.push({ + bool: { + should: [ + { terms: { ownerGroup: fields.userGroups } }, + { terms: { accessGroup: fields.userGroups } }, + ], + minimum_should_match: 1, + }, + }); + } else if (!fields.isAdmin) { + filter.push({ + term: { + isPublished: true, + }, + }); } - if (fields["userGroups"]) { - const ownerGroup = { terms: { ownerGroup: fields["userGroups"] } }; - const accessGroups = { terms: { accessGroups: fields["userGroups"] } }; - - shouldFilter.push(ownerGroup, accessGroups); - } - return { bool: { should: shouldFilter, minimum_should_match: 1 } }; + return filter; } - private buildTextQuery(fields: Partial): QueryContainer[] { + private buildTextQuery(filter: ISearchFilter): QueryContainer[] { let wildcardQueries: QueryContainer[] = []; - const { text } = fields; //NOTE: if text field is present, we query both datasetName and description fields - if (text) { - wildcardQueries = this.buildWildcardQueries(text); + if (filter.text) { + wildcardQueries = this.buildWildcardQueries(filter.text); } return wildcardQueries.length > 0 @@ -100,119 +78,19 @@ export class SearchQueryService { ); } - private buildTermsFilter( - fieldName: string, - values: unknown, - ): QueryContainer[] { - const filterArray: QueryContainer[] = []; - - if (Array.isArray(values) && values.length === 0) { - return filterArray; - } - - switch (fieldName) { - case FilterFields.CreationTime: - filterArray.push({ - range: { - [fieldName]: { - gte: (values as ObjectType).begin, - lte: (values as ObjectType).end, - }, - }, - }); - break; - - case FilterFields.Pid: - filterArray.push({ - term: { - [fieldName]: values as string, - }, - }); - break; - - case FilterFields.IsPublished: - filterArray.push({ - term: { - [fieldName]: values as boolean, - }, - }); - break; - default: - if (Array.isArray(values)) { - filterArray.push({ - terms: { - [fieldName]: values, - }, - }); - } - if (typeof values === "string" || typeof values === "number") { - filterArray.push({ - match: { - [fieldName]: values as string | number, - }, - }); - } - - if ( - values && - typeof values === "object" && - ("min" in values || - "max" in values || - "begin" in values || - "end" in values) - ) { - filterArray.push({ - range: { - [fieldName]: { - gte: - (values as NumberRangeType).min ?? - (values as ObjectType).begin, - lte: - (values as NumberRangeType).max ?? (values as ObjectType).end, - }, - }, - }); - } - break; - } - return filterArray; - } - private constructFinalQuery( - filter: QueryContainer[], - should: QueryContainer, - query: QueryContainer[], + accessFilter: QueryContainer[], + textQuery: QueryContainer[], ) { const finalQuery = { query: { bool: { - filter: [...filter, should], - must: query, + filter: accessFilter, + must: textQuery, }, }, }; - return finalQuery; - } - - public buildFullFacetPipeline( - facetFields = this.facetFields, - ): Record { - const pipeline: Record = { - all: { - value_count: { field: "pid" }, - }, - }; - for (const field of facetFields) { - pipeline[field] = { - terms: { - field, - order: { - _count: "desc", - }, - }, - }; - } - return pipeline; + return finalQuery; } } diff --git a/test/ElasticSearch.js b/test/ElasticSearch.js deleted file mode 100644 index 3742493d1..000000000 --- a/test/ElasticSearch.js +++ /dev/null @@ -1,254 +0,0 @@ -"use strict"; -const { faker } = require("@faker-js/faker"); -const utils = require("./LoginUtils"); -const { TestData } = require("./TestData"); -require("dotenv").config(); - -let accessTokenAdminIngestor = null, - accessTokenArchiveManager = null, - - pid = null; - -const isESenabled = process.env.ELASTICSEARCH_ENABLED == "yes"; - -const Relation = { - GREATER_THAN: "GREATER_THAN", - LESS_THAN: "LESS_THAN", - EQUAL_TO_NUMERIC: "EQUAL_TO_NUMERIC", - EQUAL_TO_STRING: "EQUAL_TO_STRING", -}; - -const scientificMetadataFieldName = { - keyValue: "with_key_value", - unitAndValue: "with_unit_and_value_si", - number: "with_number", - string: "with_string", -}; - -const scientificMetadata = (values) => { - const scientificQuery = values.map((value) => { - return { - lhs: value.lhs, - relation: value.relation, - rhs: value.rhs, - unit: value.unit, - }; - }); - - return { - scientific: scientificQuery, - }; -}; - -(isESenabled ? describe : describe.skip)( - "ElastiSearch: CRUD, filtering and search test case", - () => { - before(async () => { - db.collection("Dataset").deleteMany({}); - - accessTokenAdminIngestor = await utils.getToken(appUrl, { - username: "adminIngestor", - password: TestData.Accounts["adminIngestor"]["password"], - }); - - accessTokenArchiveManager = await utils.getToken(appUrl, { - username: "archiveManager", - password: TestData.Accounts["archiveManager"]["password"], - }); - }); - - it("0010: adds a new raw dataset with scientificMetadata", async () => { - return request(appUrl) - .post("/api/v3/Datasets") - .send(TestData.ScientificMetadataForElasticSearch) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.EntryCreatedStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.should.have - .property("scientificMetadata") - .which.is.an("object") - .that.has.all.keys( - scientificMetadataFieldName.keyValue, - scientificMetadataFieldName.unitAndValue, - scientificMetadataFieldName.number, - scientificMetadataFieldName.string, - ); - pid = encodeURIComponent(res.body["pid"]); - }); - }); - - it("0020: should fetch dataset with correct unitSI and ValueSI condition for scientific filter", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send( - scientificMetadata([ - { - lhs: scientificMetadataFieldName.unitAndValue, - relation: Relation.GREATER_THAN, - rhs: 99, - unit: "mbar l/s/cm^2", - }, - { - lhs: scientificMetadataFieldName.unitAndValue, - relation: Relation.EQUAL_TO_NUMERIC, - rhs: 100, - unit: "mbar l/s/cm^2", - }, - { - lhs: scientificMetadataFieldName.unitAndValue, - relation: Relation.LESS_THAN, - rhs: 101, - unit: "mbar l/s/cm^2", - }, - ]), - ) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.include(decodeURIComponent(pid)); - }); - }); - - it("0027: should fetch dataset with correct numeric value for scientific filter", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send( - scientificMetadata([ - { - lhs: scientificMetadataFieldName.number, - relation: Relation.EQUAL_TO_NUMERIC, - rhs: 111, - unit: "", - }, - ]), - ) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.include(decodeURIComponent(pid)); - }); - }); - - it("0028: should fetch dataset with correct string value for the scientific filter", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send( - scientificMetadata([ - { - lhs: scientificMetadataFieldName.string, - relation: Relation.EQUAL_TO_STRING, - rhs: "222", - unit: "", - }, - ]), - ) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.include(decodeURIComponent(pid)); - }); - }); - - it("0029: should fail when fetching dataset with incorrect relation type and value type for the scientific filter", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send( - scientificMetadata([ - { - lhs: scientificMetadataFieldName.number, - relation: Relation.EQUAL_TO_NUMERIC, - rhs: "111", - unit: "", - }, - ]), - ) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.be.length(0); - }); - }); - - it("0030: should fetching dataset with correct proposalIds and size", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send({ - proposalIds: TestData.ScientificMetadataForElasticSearch.proposalId, - size: TestData.ScientificMetadataForElasticSearch.size, - }) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.include(decodeURIComponent(pid)); - }); - }); - - it("0031: should fail fetching dataset with correct proposalIds but wrong size", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send({ - proposalIds: [TestData.ScientificMetadataForElasticSearch.proposalId], - size: faker.number.int({ min: 100000001, max: 100400000 }), - }) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.be.length(0); - }); - }); - it("0032: should fail fetching dataset with wrong proposalIds but correct size", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send({ - proposalIds: ["wrongProposalId"], - size: TestData.ScientificMetadataForElasticSearch.size, - }) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.be.length(0); - }); - }); - - it("0033: should fail fetching dataset with incorrect proposalIds and size", async () => { - return request(appUrl) - .post("/api/v3/elastic-search/search") - .send({ - proposalIds: ["wrongProposalId"], - size: faker.number.int({ min: 100000001, max: 100400000 }), - }) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) - .expect(TestData.SuccessfulPostStatusCode) - .expect("Content-Type", /json/) - .then((res) => { - res.body.data.should.be.length(0); - }); - }); - - it("0034: should delete this raw dataset", async () => { - return request(appUrl) - .delete("/api/v3/datasets/" + pid) - .set("Accept", "application/json") - .set({ Authorization: `Bearer ${accessTokenArchiveManager}` }) - .expect(TestData.SuccessfulDeleteStatusCode) - .expect("Content-Type", /json/); - }); - }, -); diff --git a/test/OpenSearch.js b/test/OpenSearch.js new file mode 100644 index 000000000..b34050834 --- /dev/null +++ b/test/OpenSearch.js @@ -0,0 +1,61 @@ +"use strict"; +const { faker } = require("@faker-js/faker"); +const utils = require("./LoginUtils"); +const { TestData } = require("./TestData"); +require("dotenv").config(); + +let accessTokenAdminIngestor = null, + accessTokenArchiveManager = null, + pid = null; + +const isESenabled = process.env.ELASTICSEARCH_ENABLED == "yes"; + +(isESenabled ? describe : describe.skip)( + "ElastiSearch: CRUD, filtering and search test case", + () => { + before(async () => { + db.collection("Dataset").deleteMany({}); + + accessTokenAdminIngestor = await utils.getToken(appUrl, { + username: "adminIngestor", + password: TestData.Accounts["adminIngestor"]["password"], + }); + + accessTokenArchiveManager = await utils.getToken(appUrl, { + username: "archiveManager", + password: TestData.Accounts["archiveManager"]["password"], + }); + }); + + it("0010: adds a new raw dataset", async () => { + return request(appUrl) + .post("/api/v3/Datasets") + .send(TestData.ScientificMetadataForElasticSearch) + .set("Accept", "application/json") + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(TestData.EntryCreatedStatusCode) + .expect("Content-Type", /json/) + .then((res) => { + res.body.should.have + .property("scientificMetadata") + .which.is.an("object") + .that.has.all.keys( + scientificMetadataFieldName.keyValue, + scientificMetadataFieldName.unitAndValue, + scientificMetadataFieldName.number, + scientificMetadataFieldName.string, + ); + pid = encodeURIComponent(res.body["pid"]); + }); + }); + + it("0034: should delete this raw dataset", async () => { + return request(appUrl) + .delete("/api/v3/datasets/" + pid) + .set("Accept", "application/json") + .set({ Authorization: `Bearer ${accessTokenArchiveManager}` }) + .expect(TestData.SuccessfulDeleteStatusCode) + .expect("Content-Type", /json/); + }); + }, +); diff --git a/test/TestData.js b/test/TestData.js index 36280b8bd..4ddcc0573 100644 --- a/test/TestData.js +++ b/test/TestData.js @@ -1,6 +1,6 @@ "use strict"; const { faker } = require("@faker-js/faker"); -const _ = require("lodash") +const _ = require("lodash"); const RawTestAccounts = require("../test/config/functionalAccounts.json"); const TestAccounts = Object.fromEntries( @@ -335,15 +335,13 @@ const TestData = { publishedOn: "JEST_ANY", retrievable: false, retrieveIntegrityCheck: false, - retrieveStatusMessage: "" + retrieveStatusMessage: "", }, description: "None, The ultimate test", endTime: "JEST_ANY", inputDatasets: [], instrumentId: "1f016ec4-7a73-11ef-ae3e-439013069377", - instrumentIds: [ - "1f016ec4-7a73-11ef-ae3e-439013069377" - ], + instrumentIds: ["1f016ec4-7a73-11ef-ae3e-439013069377"], isPublished: false, keywords: ["sls", "protein"], license: "CC BY-SA 4.0", @@ -355,20 +353,14 @@ const TestData = { ownerGroup: "p13388", packedSize: 0, pid: "JEST_ANY", - principalInvestigators: [ - "scicatingestor@your.site" - ], + principalInvestigators: ["scicatingestor@your.site"], principalInvestigator: "scicatingestor@your.site", proposalId: "JEST_ANY", - proposalIds: [ - "JEST_ANY" - ], + proposalIds: ["JEST_ANY"], relationships: [], runNumber: "123456", sampleId: "20c32b4e-7a73-11ef-9aec-5b9688aa3791i", - sampleIds: [ - "20c32b4e-7a73-11ef-9aec-5b9688aa3791i" - ], + sampleIds: ["20c32b4e-7a73-11ef-9aec-5b9688aa3791i"], scientificMetadata: { File_Prefix: "817b_B2_", approx_distance_range: { @@ -376,43 +368,43 @@ const TestData = { unit: "cm", unitSI: "m", value: [1, 2], - valueSI: [0.01, 0.02] + valueSI: [0.01, 0.02], }, approx_file_size_mb: { unit: "", - value: 8500 + value: 8500, }, beamlineParameters: { "Beam energy": { u: "eV", v: 22595, unitSI: "(kg m^2) / s^2", - valueSI: 3.6201179486175005e-15 + valueSI: 3.6201179486175005e-15, }, Monostripe: "Ru/C", "Ring current": { u: "A", v: 0.402246, unitSI: "A", - valueSI: 0.402246 - } + valueSI: 0.402246, + }, }, detectorParameters: { "Exposure time": { u: "s", v: 0.4, unitSI: "s", - valueSI: 0.4 + valueSI: 0.4, }, Objective: 20, - Scintillator: "LAG 20um" + Scintillator: "LAG 20um", }, scanParameters: { "Angular step": { u: "deg", v: 0.1, unitSI: "rad", - valueSI: 0.0017453292519943296 + valueSI: 0.0017453292519943296, }, "File Prefix": "817b_B2_", "Flat frequency": 0, @@ -425,28 +417,28 @@ const TestData = { u: "deg", v: 180, unitSI: "rad", - valueSI: 3.141592653589793 + valueSI: 3.141592653589793, }, "Rot Y min position": { u: "deg", v: 0, unitSI: "rad", - valueSI: 0 + valueSI: 0, }, "Sample In": { u: "m", v: 0, unitSI: "m", - valueSI: 0 + valueSI: 0, }, "Sample Out": { u: "m", v: -0.005, unitSI: "m", - valueSI: -0.005 + valueSI: -0.005, }, - "Sample folder": "/ramjet/817b_B2_" - } + "Sample folder": "/ramjet/817b_B2_", + }, }, sharedWith: [], size: 0, @@ -1424,79 +1416,11 @@ const TestData = { PatchDataQualityMetricsInvalid: { dataQualityMetrics: "test", }, - - ScientificMetadataForElasticSearch: { - ownerGroup: faker.company.name(), - creationLocation: faker.location.city(), - principalInvestigator: faker.internet.username(), - type: "raw", - datasetName: faker.string.sample(), - creationTime: faker.date.past(), - sourceFolder: faker.system.directoryPath(), - owner: faker.internet.username(), - size: faker.number.int({ min: 0, max: 100000000 }), - proposalId: faker.string.numeric(6), - contactEmail: faker.internet.email(), - scientificMetadata: { - with_key_value: "some text", - with_unit_and_value_si: { - value: 100, - unit: "mbar l/s/cm^2", - valueSI: 100000, - unitSI: "kg / s^3", - }, - with_number: { - value: 111, - unit: "", - }, - with_string: { - value: "222", - unit: "", - }, - }, - }, - - ScientificMetadataForElasticSearchV4: { - ownerGroup: faker.company.name(), - creationLocation: faker.location.city(), - type: "raw", - datasetName: faker.string.sample(), - creationTime: faker.date.past(), - sourceFolder: faker.system.directoryPath(), - owner: faker.internet.username(), - size: faker.number.int({ min: 0, max: 100000000 }), - contactEmail: faker.internet.email(), - scientificMetadata: { - with_key_value: "some text", - with_unit_and_value_si: { - value: 100, - unit: "mbar l/s/cm^2", - valueSI: 100000, - unitSI: "kg / s^3", - }, - with_number: { - value: 111, - unit: "", - }, - with_string: { - value: "222", - unit: "", - }, - with_no_unit: { - value: 333, - }, - with_undefined_unit: { - value: 777, - unit: undefined, - }, - }, - }, }; -const isEqualWithAny = (actual, expected) => +const isEqualWithAny = (actual, expected) => _.isEqualWith(actual, expected, (actualValue, expectedValue) => { - if (expectedValue === "JEST_ANY") - return true; -}); + if (expectedValue === "JEST_ANY") return true; + }); module.exports = { TestData, isEqualWithAny }; From 802b389252054822151bb99b8fe6392ea7223370 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 10 Mar 2026 17:50:29 +0100 Subject: [PATCH 05/46] load settings & mappings from json file --- .gitignore | 1 + opensearchConfig.example.json | 54 ++++++ src/casl/casl-ability.factory.ts | 6 +- src/config/configuration.ts | 12 +- src/datasets/datasets.controller.ts | 8 +- src/datasets/datasets.module.ts | 7 +- src/datasets/datasets.service.ts | 128 +++++++++----- .../utils/dataset-opensearch.utils.ts | 31 ++++ .../configuration/datasetFieldMapping.ts | 30 ---- src/opensearch/configuration/indexSetting.ts | 51 ------ src/opensearch/dto/create-index.dto.ts | 23 ++- src/opensearch/dto/delete-index.dto.ts | 13 -- src/opensearch/dto/get-index.dto.ts | 13 -- src/opensearch/dto/index.ts | 13 -- src/opensearch/dto/search.dto.ts | 91 ---------- src/opensearch/dto/sync-data.dto.ts | 13 -- src/opensearch/dto/update-index.dto.ts | 21 ++- src/opensearch/opensearch.controller.ts | 134 ++++++++++----- src/opensearch/opensearch.service.ts | 157 ++++++++++++------ src/opensearch/opensearch.subject.ts | 1 + 20 files changed, 422 insertions(+), 385 deletions(-) create mode 100644 opensearchConfig.example.json create mode 100644 src/datasets/utils/dataset-opensearch.utils.ts delete mode 100644 src/opensearch/configuration/datasetFieldMapping.ts delete mode 100644 src/opensearch/configuration/indexSetting.ts delete mode 100644 src/opensearch/dto/delete-index.dto.ts delete mode 100644 src/opensearch/dto/get-index.dto.ts delete mode 100644 src/opensearch/dto/index.ts delete mode 100644 src/opensearch/dto/search.dto.ts delete mode 100644 src/opensearch/dto/sync-data.dto.ts create mode 100644 src/opensearch/opensearch.subject.ts diff --git a/.gitignore b/.gitignore index 3adbab30e..13e09a0a3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ jobConfig.json jobConfig.yaml metricsConfig.json publishedDataConfig.json +openSearchConfig.json # Configs .env diff --git a/opensearchConfig.example.json b/opensearchConfig.example.json new file mode 100644 index 000000000..ad9c3d0da --- /dev/null +++ b/opensearchConfig.example.json @@ -0,0 +1,54 @@ +{ + "settings": { + "index": { + "max_result_window": 2000000, + "number_of_replicas": 0 + }, + "analysis": { + "analyzer": { + "autocomplete": { + "type": "custom", + "tokenizer": "autocomplete", + "filter": ["lowercase"] + }, + "autocomplete_search": { + "type": "custom", + "tokenizer": "lowercase" + } + }, + "tokenizer": { + "autocomplete": { + "type": "edge_ngram", + "min_gram": 2, + "max_gram": 40, + "token_chars": ["letter", "digit", "symbol", "punctuation"] + } + } + } + }, + "mappings": { + "properties": { + "description": { + "type": "text", + "analyzer": "autocomplete", + "search_analyzer": "autocomplete_search" + }, + "datasetName": { + "type": "text", + "analyzer": "autocomplete", + "search_analyzer": "autocomplete_search" + }, + "isPublished": { + "type": "boolean" + }, + "ownerGroup": { + "type": "keyword", + "ignore_above": 256 + }, + "accessGroups": { + "type": "keyword", + "ignore_above": 256 + } + } + } +} diff --git a/src/casl/casl-ability.factory.ts b/src/casl/casl-ability.factory.ts index 6db62764a..b95cfd510 100644 --- a/src/casl/casl-ability.factory.ts +++ b/src/casl/casl-ability.factory.ts @@ -31,7 +31,7 @@ import { Action } from "./action.enum"; import { RuntimeConfig } from "src/config/runtime-config/schemas/runtime-config.schema"; import { accessibleBy } from "@casl/mongoose"; import { MetadataKeyClass } from "src/metadata-keys/schemas/metadatakey.schema"; -import { OpensearchActions } from "src/opensearch/dto"; +import { Opensearch } from "src/opensearch/opensearch.subject"; type Subjects = | string @@ -50,7 +50,7 @@ type Subjects = | typeof User | typeof UserIdentity | typeof UserSettings - | typeof OpensearchActions + | typeof Opensearch | typeof Datablock | typeof RuntimeConfig | typeof MetadataKeyClass @@ -340,7 +340,7 @@ export class CaslAbilityFactory { /* / user that belongs to any of the group listed in ADMIN_GROUPS */ - can(Action.Manage, OpensearchActions); + can(Action.Manage, Opensearch); } return build({ detectSubjectType: (item) => diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 18ffc5aa5..0bdc732c7 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -72,6 +72,7 @@ const configuration = () => { const jsonConfigMap: { [key: string]: object | object[] | boolean } = { datasetTypes: {}, proposalTypes: {}, + opensearchConfig: {}, }; const jsonConfigFileList: { [key: string]: string } = { frontendConfig: @@ -84,6 +85,8 @@ const configuration = () => { metricsConfig: process.env.METRICS_CONFIG_FILE || "metricsConfig.json", publishedDataConfig: process.env.PUBLISHED_DATA_CONFIG_FILE || "publishedDataConfig.json", + opensearchConfig: + process.env.OPENSEARCH_CONFIG_FILE || "opensearchConfig.json", }; Object.keys(jsonConfigFileList).forEach((key) => { const filePath = jsonConfigFileList[key]; @@ -98,7 +101,11 @@ const configuration = () => { jsonConfigMap[key] = false; } } else { - if (key === "publishedDataConfig") { + const configsWithExampleFallback = [ + "publishedDataConfig", + "opensearchConfig", + ]; + if (configsWithExampleFallback.includes(key)) { console.warn( `Configuration file ${filePath} does not exist. Trying to use the example ${key}.example.json file`, ); @@ -387,7 +394,7 @@ const configuration = () => { 10, ), mongoDBCollection: process.env.MONGODB_COLLECTION, - defaultIndex: process.env.OPENSEARCH_INDEX ?? "dataset", + defaultIndex: process.env.OPENSEARCH_DEFAULT_INDEX ?? "dataset", }, metrics: { // Note: `process.env.METRICS_ENABLED` is directly used for conditional module loading in @@ -426,6 +433,7 @@ const configuration = () => { frontendConfig: jsonConfigMap.frontendConfig, frontendTheme: jsonConfigMap.frontendTheme, publishedDataConfig: jsonConfigMap.publishedDataConfig, + opensearchConfig: jsonConfigMap.opensearchConfig, }; return merge(config, localconfiguration); }; diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index 024b70d9b..147f9aaf8 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -994,10 +994,8 @@ export class DatasetsController { const osEnabled = this.configService.get("opensearch.enabled"); const textSearch = parsedFilters.fields?.text; - const isOpenSearchPopulated = - await this.datasetsService.isOpenSearchPopulated(); - if (osEnabled && textSearch && isOpenSearchPopulated) { + if (osEnabled && textSearch) { const isAdmin = canViewAny; datasets = await this.datasetsService.opensearchQuery( parsedFilters, @@ -1083,10 +1081,8 @@ export class DatasetsController { const osEnabled = this.configService.get("opensearch.enabled"); const textSearch = parsedFilters.fields?.text; - const isOpenSearchPopulated = - await this.datasetsService.isOpenSearchPopulated(); - if (osEnabled && textSearch && isOpenSearchPopulated) { + if (osEnabled && textSearch) { const isAdmin = canViewAny; return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); } diff --git a/src/datasets/datasets.module.ts b/src/datasets/datasets.module.ts index d29b78bbd..0dbf18028 100644 --- a/src/datasets/datasets.module.ts +++ b/src/datasets/datasets.module.ts @@ -19,7 +19,7 @@ import { GenericHistory, GenericHistorySchema, } from "src/common/schemas/generic-history.schema"; -import { ConfigModule, ConfigService } from "@nestjs/config"; +import { ConditionalModule, ConfigModule, ConfigService } from "@nestjs/config"; import { applyHistoryPluginOnce } from "src/common/mongoose/plugins/history.plugin.util"; import { ProposalsModule } from "src/proposals/proposals.module"; import { HistoryModule } from "src/history/history.module"; @@ -35,7 +35,10 @@ import { OpensearchModule } from "src/opensearch/opensearch.module"; InitialDatasetsModule, HistoryModule, MetadataKeysModule, - OpensearchModule, + ConditionalModule.registerWhen( + OpensearchModule, + (env: NodeJS.ProcessEnv) => env.OPENSEARCH_ENABLED === "yes", + ), ProposalsModule, forwardRef(() => LogbooksModule), MongooseModule.forFeatureAsync([ diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 6acdeec5f..731ffd0e6 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -63,10 +63,15 @@ import { MetadataSourceDoc, } from "src/metadata-keys/metadatakeys.service"; import { OpensearchService } from "src/opensearch/opensearch.service"; +import { + DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY, + sanitizeDatasetForOpensearch, +} from "./utils/dataset-opensearch.utils"; +import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { - private searchService: OpensearchService | null = null; constructor( private configService: ConfigService, @InjectModel(DatasetClass.name) @@ -77,13 +82,7 @@ export class DatasetsService { private opensearchService: OpensearchService, private metadataKeysService: MetadataKeysService, private proposalService: ProposalsService, - ) { - // TODO: Verify if this connection check is needed. - // OpenSearch should fallback to MongoDB if unavailable. - if (this.opensearchService.connected) { - this.searchService = this.opensearchService; - } - } + ) {} private createMetadataKeysInstance( doc: UpdateQuery, @@ -188,8 +187,10 @@ export class DatasetsService { const savedDataset = await createdDataset.save(); - if (this.searchService && createdDataset) { - await this.searchService.updateInsertDocument(savedDataset.toObject()); + if (this.opensearchService && createdDataset) { + await this.opensearchService.updateInsertDocument( + sanitizeDatasetForOpensearch(savedDataset.toObject()), + ); } if (savedDataset.proposalIds && savedDataset.proposalIds.length > 0) { @@ -303,10 +304,15 @@ export class DatasetsService { filter: IFilters, isAdmin: boolean, ): Promise { - // if no text field is provided, we fallback to full query - if (!this.searchService || !filter.fields) { + const defaultOsIndex = + this.configService.get("opensearch.defaultIndex") || "dataset"; + if ( + !this.opensearchService.connected() || + !(await this.opensearchService.isPopulated()) + ) { return this.fullquery(filter); } + const { text, userGroups } = filter.fields || {}; const mongoQuery: FilterQuery = @@ -318,16 +324,16 @@ export class DatasetsService { const modifiers: QueryOptions = parseLimitFilters(filter.limits); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { $text, ...otherQueries } = mongoQuery; + delete mongoQuery.$text; - const osResult = await this.searchService.search( + const osResult = await this.opensearchService.search( { text, userGroups, isAdmin: isAdmin }, + defaultOsIndex, modifiers.limit, modifiers.skip, ); const datasets = await this.datasetModel - .find({ pid: { $in: osResult.data }, ...otherQueries }) + .find({ pid: { $in: osResult.data }, ...mongoQuery }) .sort(modifiers.sort) .exec(); @@ -355,23 +361,43 @@ export class DatasetsService { filters: IFacets, isAdmin: boolean, ): Promise[]> { - if (!this.searchService || !filters.fields) { + const defaultOsIndex = + this.configService.get("opensearch.defaultIndex") || "dataset"; + const osConfig = + this.configService.get<{ + settings: IndexSettings; + mappings: TypeMapping; + }>("opensearchConfig") || null; + const osMaxResultWindow = Number( + osConfig?.settings?.index?.max_result_window, + ); + + if ( + !this.opensearchService.connected() || + !(await this.opensearchService.isPopulated()) + ) { return this.fullFacet(filters); } - const { text, userGroups } = filters.fields || {}; - const osResult = await this.searchService.search({ - text, - userGroups, - isAdmin: isAdmin, - }); + const fields = filters.fields ?? {}; + const facets = filters.facets ?? []; - filters.fields.openSearchIdList = osResult.data; - delete filters.fields.text; + const osResult = await this.opensearchService.search( + { + text: fields.text, + userGroups: fields.userGroups, + isAdmin: isAdmin, + }, + defaultOsIndex, + osMaxResultWindow, + ); + + fields.openSearchIdList = osResult.data; + delete fields.text; const pipeline = createFullfacetPipeline( this.datasetModel, "pid", - filters.fields ?? {}, - filters.facets ?? [], + fields, + facets, "", ); @@ -455,8 +481,12 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.searchService) { - await this.searchService.updateInsertDocument(updatedDataset.toObject()); + if (this.opensearchService) { + await this.opensearchService.updateInsertDocument( + sanitizeDatasetForOpensearch( + updatedDataset.toObject(), + ), + ); } await this.metadataKeysService.replaceManyFromSource( @@ -501,8 +531,12 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.searchService) { - await this.searchService.updateInsertDocument(patchedDataset.toObject()); + if (this.opensearchService) { + await this.opensearchService.updateInsertDocument( + sanitizeDatasetForOpensearch( + patchedDataset.toObject(), + ), + ); } await this.metadataKeysService.replaceManyFromSource( @@ -524,8 +558,8 @@ export class DatasetsService { throw new NotFoundException(`Dataset #${id} not found`); } - if (this.searchService) { - await this.searchService.deleteDocument(id); + if (this.opensearchService) { + await this.opensearchService.deleteDocument(id); } if (deletedDataset?.proposalIds && deletedDataset.proposalIds.length > 0) { @@ -542,15 +576,6 @@ export class DatasetsService { return deletedDataset; } - // GET datasets without _id which is used for elastic search data synchronization - async getDatasetsWithoutId(): Promise { - try { - const datasets = this.datasetModel.find({}, { _id: 0 }).lean().exec(); - return datasets; - } catch (error) { - throw new NotFoundException(error); - } - } // Get metadata keys async metadataKeys( @@ -616,9 +641,20 @@ export class DatasetsService { } } - async isOpenSearchPopulated() { - if (!this.searchService) return; - const count = await this.searchService.getCount(); - return count.body.count > 0; + async syncDatasetsToOpensearch(index: string) { + try { + await this.opensearchService.checkIndexExists(index); + + const datasets = await this.datasetModel + .find({}, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) + .lean() + .exec(); + return await this.opensearchService.performBulkOperation( + datasets, + index, + ); + } catch (error) { + throw new Error(`OpenSearch sync failed: ${error}`); + } } } diff --git a/src/datasets/utils/dataset-opensearch.utils.ts b/src/datasets/utils/dataset-opensearch.utils.ts new file mode 100644 index 000000000..12470cb83 --- /dev/null +++ b/src/datasets/utils/dataset-opensearch.utils.ts @@ -0,0 +1,31 @@ +// It controlles which fields of the dataset document should be excluded +// when sending it to Opensearch. +const DATASET_OPENSEARCH_EXCLUDED_FIELDS = [ + "scientificMetadata", + "history", + "datasetlifecycle", +] as const; + +// OUTPUT EXAMPLE: +// DATASET_OPENSEARCH_EXCLUDED_FIELDS[0] // "scientificMetadata" +// DATASET_OPENSEARCH_EXCLUDED_FIELDS[1] // "history" ...etc +type ExcludedDatasetField = (typeof DATASET_OPENSEARCH_EXCLUDED_FIELDS)[number]; + +// OUTPUT EXAMPLE: +// { scientificMetadata: 0, history: 0, datasetlifecycle: 0 } +export const DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY = Object.fromEntries( + DATASET_OPENSEARCH_EXCLUDED_FIELDS.map((field) => [field, 0]), +) as Record; + +// It removes the fields that are defined in DATASET_OPENSEARCH_EXCLUDED_FIELDS +// from the given document before sending it to Opensearch. +export function sanitizeDatasetForOpensearch( + doc: T, +): Omit { + const result = { ...doc } as Record; + for (const field of DATASET_OPENSEARCH_EXCLUDED_FIELDS) { + delete result[field]; + } + + return result as Omit; +} diff --git a/src/opensearch/configuration/datasetFieldMapping.ts b/src/opensearch/configuration/datasetFieldMapping.ts deleted file mode 100644 index 5f0d3ded9..000000000 --- a/src/opensearch/configuration/datasetFieldMapping.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MappingObject } from "../interfaces/mappingInterface.type"; - -export const datasetMappings: MappingObject = { - description: { - type: "text", - analyzer: "autocomplete", - search_analyzer: "autocomplete_search", - }, - datasetName: { - type: "text", - analyzer: "autocomplete", - search_analyzer: "autocomplete_search", - }, - pid: { - type: "keyword", - ignore_above: 256, - }, - - isPublished: { - type: "boolean", - }, - ownerGroup: { - type: "keyword", - ignore_above: 256, - }, - accessGroups: { - type: "keyword", - ignore_above: 256, - }, -}; diff --git a/src/opensearch/configuration/indexSetting.ts b/src/opensearch/configuration/indexSetting.ts deleted file mode 100644 index 6bf247e5b..000000000 --- a/src/opensearch/configuration/indexSetting.ts +++ /dev/null @@ -1,51 +0,0 @@ -//Tokenizers -export const autocomplete_tokenizer = { - type: "edge_ngram", - min_gram: 2, - max_gram: 40, - token_chars: ["letter", "digit", "symbol", "punctuation"], -}; - -//Filters -export const special_character_filter = { - pattern: "[^A-Za-z0-9]", - type: "pattern_replace", - replacement: "", -}; - -//Index Settings -export const defaultOpensearchSettings = { - index: { - max_result_window: process.env.ES_MAX_RESULT || 2000000, - number_of_replicas: 0, - mapping: { - total_fields: { - limit: process.env.ES_FIELDS_LIMIT || 2000000, - }, - nested_fields: { - limit: 1000, - }, - }, - }, - analysis: { - analyzer: { - autocomplete: { - type: "custom", - tokenizer: "autocomplete", - filter: ["lowercase"], - }, - autocomplete_search: { - type: "custom", - tokenizer: "lowercase", - }, - case_sensitive: { - type: "custom", - tokenizer: "standard", - filter: [], - }, - }, - tokenizer: { - autocomplete: autocomplete_tokenizer, - }, - }, -}; diff --git a/src/opensearch/dto/create-index.dto.ts b/src/opensearch/dto/create-index.dto.ts index 3f63ac65d..516d23d2f 100644 --- a/src/opensearch/dto/create-index.dto.ts +++ b/src/opensearch/dto/create-index.dto.ts @@ -1,13 +1,20 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsString } from "class-validator"; +import { IsObject, IsOptional } from "class-validator"; +import { UpdateIndexDto } from "./update-index.dto"; +import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; -export class CreateIndexDto { +export class CreateIndexDto extends UpdateIndexDto { @ApiProperty({ - type: String, - required: true, - default: "dataset", - description: "Create an index with this name", + description: "Index mappings", + type: Object, + example: { + properties: { + datasetName: { type: "text" }, + ownerGroup: { type: "keyword" }, + }, + }, }) - @IsString() - readonly index: string; + @IsObject() + @IsOptional() + mappings: Partial; } diff --git a/src/opensearch/dto/delete-index.dto.ts b/src/opensearch/dto/delete-index.dto.ts deleted file mode 100644 index 433511d9b..000000000 --- a/src/opensearch/dto/delete-index.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { IsString } from "class-validator"; - -export class DeleteIndexDto { - @ApiProperty({ - type: String, - required: true, - default: "dataset", - description: "Delete an index with this name", - }) - @IsString() - readonly index: string; -} diff --git a/src/opensearch/dto/get-index.dto.ts b/src/opensearch/dto/get-index.dto.ts deleted file mode 100644 index 3ce9849ad..000000000 --- a/src/opensearch/dto/get-index.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { IsString } from "class-validator"; - -export class GetIndexDto { - @ApiProperty({ - type: String, - required: true, - default: "dataset", - description: "Get an index with this name", - }) - @IsString() - readonly index: string; -} diff --git a/src/opensearch/dto/index.ts b/src/opensearch/dto/index.ts deleted file mode 100644 index 844e68d25..000000000 --- a/src/opensearch/dto/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { CreateIndexDto } from "./create-index.dto"; -import { DeleteIndexDto } from "./delete-index.dto"; -import { GetIndexDto } from "./get-index.dto"; -import { SyncDatabaseDto } from "./sync-data.dto"; -import { UpdateIndexDto } from "./update-index.dto"; - -export class OpensearchActions { - createIndex: CreateIndexDto; - deleteIndex: DeleteIndexDto; - updateIndex: UpdateIndexDto; - syncDatabase: SyncDatabaseDto; - getIndexSettings: GetIndexDto; -} diff --git a/src/opensearch/dto/search.dto.ts b/src/opensearch/dto/search.dto.ts deleted file mode 100644 index 13270eba5..000000000 --- a/src/opensearch/dto/search.dto.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { IsArray, IsOptional } from "class-validator"; - -export class SearchDto { - @ApiProperty({ - type: String, - required: false, - default: "", - description: "text query", - }) - readonly text: string; - - @ApiProperty({ - type: Array, - required: false, - default: [], - description: "ownerGroup", - }) - @IsOptional() - readonly ownerGroup: []; - - @ApiProperty({ - type: Array, - required: false, - default: [], - description: "creationLocation", - }) - @IsOptional() - readonly creationLocation: []; - - @ApiProperty({ - type: Array, - required: false, - default: [], - description: "type", - }) - @IsOptional() - readonly type: []; - - @ApiProperty({ - type: Array, - required: false, - default: [], - description: "keywords", - }) - @IsOptional() - readonly keywords: []; - - @ApiProperty({ - type: Boolean, - required: false, - default: false, - description: "isPublished", - }) - @IsOptional() - readonly isPublished: boolean; - - @ApiProperty({ - type: Array, - items: { - type: "object", - properties: { - lhs: { type: "string" }, - relation: { type: "string" }, - rhs: { - type: "number", - description: "This can be either a number or a string", - }, - unit: { type: "string" }, - }, - }, - required: false, - default: [ - { - lhs: "", - relation: "", - rhs: 0, - unit: "", - }, - ], - description: "scientificMetadata condition", - }) - @IsOptional() - @IsArray() - readonly scientific: Array<{ - lhs: string; - relation: string; - rhs: number | string; - unit: string; - }>; -} diff --git a/src/opensearch/dto/sync-data.dto.ts b/src/opensearch/dto/sync-data.dto.ts deleted file mode 100644 index 1bdf3ff34..000000000 --- a/src/opensearch/dto/sync-data.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger"; -import { IsString } from "class-validator"; - -export class SyncDatabaseDto { - @ApiProperty({ - type: String, - required: true, - default: "dataset", - description: "Sync datasets into elastic search with this index", - }) - @IsString() - readonly index: string; -} diff --git a/src/opensearch/dto/update-index.dto.ts b/src/opensearch/dto/update-index.dto.ts index b6f264e24..9ef74235c 100644 --- a/src/opensearch/dto/update-index.dto.ts +++ b/src/opensearch/dto/update-index.dto.ts @@ -1,5 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsString } from "class-validator"; +import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import { IsObject, IsOptional, IsString } from "class-validator"; export class UpdateIndexDto { @ApiProperty({ @@ -9,5 +10,21 @@ export class UpdateIndexDto { description: "Update an index with this name", }) @IsString() - readonly index: string; + @IsOptional() + index: string; + + @ApiProperty({ + description: "Index settings to update", + type: Object, + example: { + index: { + number_of_replicas: 1, + refresh_interval: "1s", + max_result_window: 1000000, + }, + }, + }) + @IsObject() + @IsOptional() + settings: IndexSettings; } diff --git a/src/opensearch/opensearch.controller.ts b/src/opensearch/opensearch.controller.ts index 52365f3f3..d451486a4 100644 --- a/src/opensearch/opensearch.controller.ts +++ b/src/opensearch/opensearch.controller.ts @@ -7,8 +7,16 @@ import { Query, UseInterceptors, UseGuards, + Body, } from "@nestjs/common"; -import { ApiTags, ApiBearerAuth, ApiResponse, ApiQuery } from "@nestjs/swagger"; +import { + ApiTags, + ApiBearerAuth, + ApiResponse, + ApiQuery, + ApiBody, + ApiOperation, +} from "@nestjs/swagger"; import { Action } from "src/casl/action.enum"; import { AppAbility } from "src/casl/casl-ability.factory"; import { CheckPolicies } from "src/casl/decorators/check-policies.decorator"; @@ -16,13 +24,10 @@ import { PoliciesGuard } from "src/casl/guards/policies.guard"; import { DatasetsService } from "src/datasets/datasets.service"; import { SubDatasetsPublicInterceptor } from "src/datasets/interceptors/datasets-public.interceptor"; import { CreateIndexDto } from "./dto/create-index.dto"; -import { DeleteIndexDto } from "./dto/delete-index.dto"; -import { GetIndexDto } from "./dto/get-index.dto"; -import { SyncDatabaseDto } from "./dto/sync-data.dto"; import { UpdateIndexDto } from "./dto/update-index.dto"; -import { OpensearchActions } from "./dto"; import { OpensearchService } from "./opensearch.service"; +import { Opensearch } from "./opensearch.subject"; @ApiBearerAuth() @ApiTags("opensearch") @@ -35,40 +40,56 @@ export class OpensearchController { @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) @HttpCode(HttpStatus.CREATED) + @ApiBody({ + description: `If settings and mappings are not provided, + they will be loaded from the opensearchConfig.json file. + To use the default config, simply omit settings and mappings from the request body.`, + type: CreateIndexDto, + }) @ApiResponse({ status: HttpStatus.CREATED, description: "Create index", }) @Post("/create-index") - async createIndex(@Query() { index }: CreateIndexDto) { - const esIndex = index.trim(); - - return this.opensearchService.createIndex(esIndex); + async createIndex(@Body() createIndexDto: CreateIndexDto) { + return this.opensearchService.createIndex(createIndexDto); } @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) + @ApiOperation({ + summary: "Sync data from MongoDB to OpenSearch", + description: `Syncs the dataset collection to the specified OpenSearch index. + Defaults to the index configured in OPENSEARCH_DEFAULT_INDEX. + Currently only supports the dataset collection.`, + }) + @ApiQuery({ + name: "index", + description: "The OpenSearch index name to sync the data into", + default: "dataset", + type: String, + }) @HttpCode(HttpStatus.OK) @ApiResponse({ status: 200, - description: "Sync data to the index", + description: "Successfully synced data from MongoDB to OpenSearch", }) @Post("/sync-database") - async syncDatabase(@Query() { index }: SyncDatabaseDto) { + async syncDatabase(@Query("index") index: string) { const esIndex = index.trim(); - const collectionData = await this.datasetService.getDatasetsWithoutId(); - - return this.opensearchService.syncDatabase(collectionData, esIndex); + // NOTE: for now, we will only sync datasets to opensearch, + // but this can be easily extended to other data in the future if needed + return await this.datasetService.syncDatasetsToOpensearch(esIndex); } @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) @HttpCode(HttpStatus.OK) @UseInterceptors(SubDatasetsPublicInterceptor) @@ -77,60 +98,99 @@ export class OpensearchController { description: "Partial search text for datasetName and description fields", type: String, }) + @ApiQuery({ + name: "index", + description: "The index name to search", + default: "dataset", + required: false, + type: String, + }) + @ApiQuery({ + name: "limit", + description: "The maximum number of results to return", + required: false, + type: Number, + }) + @ApiQuery({ + name: "skip", + description: "The number of results to skip", + required: false, + type: Number, + }) @ApiResponse({ status: 200, - description: "Search with opensearch to get results in PIDs", + description: + "Successfully retrieved search results in _ids from Opensearch.", }) @Post("/search") - async fetchOSResults(@Query("textQuery") textQuery: string) { - return this.opensearchService.search({ text: textQuery }); + async fetchOSResults( + @Query("index") index: string, + @Query("limit") limit: number, + @Query("skip") skip: number, + @Query("textQuery") text: string, + ) { + return this.opensearchService.search({ text }, index, limit, skip); } @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) @HttpCode(HttpStatus.OK) + @ApiQuery({ + name: "index", + description: "The index name to delete", + default: "dataset", + type: String, + }) @ApiResponse({ status: 200, description: "Delete index", }) @Post("/delete-index") - async deleteIndex(@Query() { index }: DeleteIndexDto) { - const esIndex = index.trim(); - - return this.opensearchService.deleteIndex(esIndex); + async deleteIndex(@Query("index") index: string) { + return this.opensearchService.deleteIndex(index.trim()); } @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) @HttpCode(HttpStatus.OK) + @ApiQuery({ + name: "index", + description: "The index name to get the config for", + default: "dataset", + type: String, + }) @ApiResponse({ status: 200, - description: "Get current index setting", + description: "Get index config including settings and mappings", }) @Get("/get-index") - async getIndex(@Query() { index }: GetIndexDto) { - const esIndex = index.trim(); - - return this.opensearchService.getIndexSettings(esIndex); + async getIndex(@Query("index") index: string) { + return this.opensearchService.getIndexConfig(index.trim()); } @UseGuards(PoliciesGuard) @CheckPolicies("opensearch", (ability: AppAbility) => - ability.can(Action.Manage, OpensearchActions), + ability.can(Action.Manage, Opensearch), ) @HttpCode(HttpStatus.OK) + @ApiBody({ + description: ` + If settings are not provided, + they will be loaded from the opensearchConfig.json file. + To use the default config, simply omit settings from the request body. + for more details: https://docs.opensearch.org/latest/install-and-configure/configuring-opensearch/index-settings/`, + type: UpdateIndexDto, + }) @ApiResponse({ status: 200, - description: "Update index to the latest settings", + description: "Successfully updated index settings", }) @Post("/update-index") - async updateIndex(@Query() { index }: UpdateIndexDto) { - const esIndex = index.trim(); - - return this.opensearchService.updateIndex(esIndex); + async updateIndex(@Body() updateIndexDto: UpdateIndexDto) { + return this.opensearchService.updateIndexSettings(updateIndexDto); } } diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index fa3b8279e..79d471317 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -9,8 +9,6 @@ import { Client } from "@opensearch-project/opensearch"; import { SearchQueryService } from "./providers/query-builder.service"; -import { defaultOpensearchSettings } from "./configuration/indexSetting"; -import { datasetMappings } from "./configuration/datasetFieldMapping"; import { DatasetClass, DatasetDocument, @@ -20,6 +18,9 @@ import { sleep } from "src/common/utils"; import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; import { ISearchFilter } from "./interfaces/os-common.type"; +import { CreateIndexDto } from "./dto/create-index.dto"; +import { UpdateIndexDto } from "./dto/update-index.dto"; +import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; @Injectable() export class OpensearchService implements OnModuleInit { @@ -28,9 +29,12 @@ export class OpensearchService implements OnModuleInit { private username: string; private password: string; private refresh: "false" | "wait_for"; + private osConfigs: { + settings: IndexSettings; + mappings: TypeMapping; + } | null; + public defaultIndex: string; - public esEnabled: boolean; - public connected = false; constructor( private readonly searchService: SearchQueryService, @@ -39,43 +43,48 @@ export class OpensearchService implements OnModuleInit { this.host = this.configService.get("opensearch.host") || ""; this.username = this.configService.get("opensearch.username") || ""; this.password = this.configService.get("opensearch.password") || ""; - this.esEnabled = - this.configService.get("opensearch.enabled") === "yes" - ? true - : false; + this.refresh = this.configService.get<"false" | "wait_for">("opensearch.refresh") || "false"; this.defaultIndex = - this.configService.get("opensearch.defaultIndex") || ""; - - if ( - this.esEnabled && - (!this.host || !this.username || !this.password || !this.defaultIndex) - ) { - Logger.error( - "Missing ENVIRONMENT variables for opensearch connection", + this.configService.get("opensearch.defaultIndex") || "dataset"; + + this.osConfigs = + this.configService.get<{ + settings: IndexSettings; + mappings: TypeMapping; + }>("opensearchConfig") || null; + + if (!this.host || !this.username || !this.password || !this.defaultIndex) { + Logger.warn( + `Missing Opensearch configuration for host: ${this.host}, username: ${this.username}, + password: ${this.password} or defaultIndex: ${this.defaultIndex}`, + "Opensearch", + ); + } + if (!this.osConfigs) { + Logger.warn( + `Missing Opensearch index configuration, using default settings and mappings`, "Opensearch", ); } } async onModuleInit() { - if (!this.esEnabled) { - this.connected = false; - return; - } - try { await this.retryConnection(3, 3000); const isIndexExists = await this.isIndexExists(this.defaultIndex); if (!isIndexExists) { - await this.createIndex(this.defaultIndex); + await this.createIndex({ + index: this.defaultIndex, + settings: this.osConfigs?.settings || {}, + mappings: this.osConfigs?.mappings || {}, + }); Logger.log(`New index ${this.defaultIndex}is created `, "Opensearch"); } - this.connected = true; Logger.log("Opensearch Connected", "Opensearch"); } catch (error) { Logger.error(error, "onModuleInit failed-> OpensearchService"); @@ -119,26 +128,45 @@ export class OpensearchService implements OnModuleInit { } } + connected() { + return !!this.osClient; + } + async isIndexExists(index = this.defaultIndex) { return await this.osClient.indices.exists({ index, }); } - async createIndex(index = this.defaultIndex) { + async isPopulated(index = this.defaultIndex) { + const { body } = await this.getCount(index); + + if (body.count > 0) { + return true; + } + Logger.error( + `Opensearch is enabled but index ${index} is empty`, + "Opensearch", + ); + + return false; + } + + async createIndex(createIndexDto: CreateIndexDto) { + const index = createIndexDto.index.trim(); + const { settings, mappings } = createIndexDto; + try { - await this.osClient.indices.create({ + const newIndex = await this.osClient.indices.create({ index, body: { - settings: defaultOpensearchSettings as IndexSettings, - mappings: { - dynamic: "false", - properties: datasetMappings, - }, + settings: settings || this.osConfigs?.settings, + mappings: mappings || this.osConfigs?.mappings, }, }); Logger.log(`Opensearch Index Created-> Index: ${index}`, "Opensearch"); - return HttpStatus.CREATED; + + return newIndex; } catch (error) { throw new HttpException( `createIndex failed-> OpensearchService ${error}`, @@ -173,41 +201,47 @@ export class OpensearchService implements OnModuleInit { } } - async updateIndex(index = this.defaultIndex) { + async updateIndexSettings(updateIndexDto: UpdateIndexDto) { + const index = updateIndexDto.index.trim(); + try { await this.osClient.indices.close({ index, }); await this.osClient.indices.putSettings({ index, - body: { settings: defaultOpensearchSettings as IndexSettings }, - }); - - await this.osClient.indices.putMapping({ - index, - body: { - properties: datasetMappings, - }, + body: { settings: updateIndexDto.settings || this.osConfigs?.settings }, }); await this.osClient.indices.open({ index, }); - Logger.log(`Opensearch Index Updated-> Index: ${index}`, "Opensearch"); + + const { settings } = await this.getIndexConfig(index); + + return settings; } catch (error) { throw new HttpException( - `updateIndex failed-> OpensearchService ${error}`, + `updateIndexSettings failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } } - async getIndexSettings(index = this.defaultIndex) { + async getIndexConfig(index = this.defaultIndex) { try { - return await this.osClient.indices.getSettings({ index }); + const [settings, mappings] = await Promise.all([ + this.osClient.indices.getSettings({ index }), + this.osClient.indices.getMapping({ index }), + ]); + + return { + settings: settings.body[index].settings, + mappings: mappings.body[index].mappings, + }; } catch (error) { throw new HttpException( - `getIndexSettings failed-> OpensearchService ${error}`, + `getIndexConfig failed-> OpensearchService ${error}`, HttpStatus.BAD_REQUEST, ); } @@ -228,7 +262,8 @@ export class OpensearchService implements OnModuleInit { async search( filter: ISearchFilter, - limit = 20, + index = this.defaultIndex, + limit = 1000, skip = 0, ): Promise<{ totalCount: number; data: (string | undefined)[] }> { try { @@ -248,7 +283,7 @@ export class OpensearchService implements OnModuleInit { }; const { body } = await this.osClient.search({ - index: this.defaultIndex, + index, body: searchOptions, }); @@ -292,15 +327,15 @@ export class OpensearchService implements OnModuleInit { } } - async deleteDocument(id: string) { + async deleteDocument(id: string, index = this.defaultIndex) { try { await this.osClient.delete({ - index: this.defaultIndex, + index, id, refresh: this.refresh, }); Logger.log( - `Document Deleted-> Document_id: ${id} deleted on index: ${this.defaultIndex}`, + `Document Deleted-> Document_id: ${id} deleted on index: ${index}`, "Opensearch", ); } catch (error) { @@ -311,26 +346,38 @@ export class OpensearchService implements OnModuleInit { } } + async checkIndexExists(index: string) { + const { body: indexExists } = await this.osClient.indices.exists({ index }); + + if (!indexExists) { + throw new Error(`Index ${index} not found`); + } + } + // *** NOTE: below are helper methods *** - async performBulkOperation(collection: DatasetClass[], index: string) { + async performBulkOperation( + collection: T[], + index: string, + ) { const result = await this.osClient.helpers.bulk({ retries: 5, wait: 10000, datasource: collection, - onDocument(doc: DatasetClass) { + onDocument(doc: T) { + const { _id: mongoId, ...body } = doc; return [ { index: { _index: index, - _id: doc.pid, + _id: mongoId, }, }, - doc, + body, ]; }, onDrop(doc) { - console.debug(`${doc.document.pid}`, doc.error?.reason); + console.debug(`${doc.document._id}`, doc.error?.reason); }, }); return result; diff --git a/src/opensearch/opensearch.subject.ts b/src/opensearch/opensearch.subject.ts new file mode 100644 index 000000000..f55619a9f --- /dev/null +++ b/src/opensearch/opensearch.subject.ts @@ -0,0 +1 @@ +export class Opensearch {} From 364d050cba12aefb172216a5666d6d2c79f7db47 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 10 Mar 2026 18:12:22 +0100 Subject: [PATCH 06/46] Set opensearchService optional in datasetService --- src/datasets/datasets.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 731ffd0e6..212443529 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -3,6 +3,7 @@ import { Inject, Injectable, NotFoundException, + Optional, Scope, } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; @@ -79,7 +80,7 @@ export class DatasetsService { @Inject(REQUEST) private request: Request, private datasetsAccessService: DatasetsAccessService, - private opensearchService: OpensearchService, + @Optional() private opensearchService: OpensearchService, private metadataKeysService: MetadataKeysService, private proposalService: ProposalsService, ) {} From a2ee660c28bdfa3ed63856e8d87621c23fcb22e3 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 10:11:46 +0100 Subject: [PATCH 07/46] fix type error --- src/datasets/datasets.service.ts | 4 ++-- src/opensearch/dto/create-index.dto.ts | 2 +- src/opensearch/dto/update-index.dto.ts | 2 +- src/opensearch/opensearch.service.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 212443529..b056993e9 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -68,8 +68,8 @@ import { DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY, sanitizeDatasetForOpensearch, } from "./utils/dataset-opensearch.utils"; -import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; -import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; +import type { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { diff --git a/src/opensearch/dto/create-index.dto.ts b/src/opensearch/dto/create-index.dto.ts index 516d23d2f..bca218c3c 100644 --- a/src/opensearch/dto/create-index.dto.ts +++ b/src/opensearch/dto/create-index.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from "@nestjs/swagger"; import { IsObject, IsOptional } from "class-validator"; import { UpdateIndexDto } from "./update-index.dto"; -import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; +import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; export class CreateIndexDto extends UpdateIndexDto { @ApiProperty({ diff --git a/src/opensearch/dto/update-index.dto.ts b/src/opensearch/dto/update-index.dto.ts index 9ef74235c..0d278a0e8 100644 --- a/src/opensearch/dto/update-index.dto.ts +++ b/src/opensearch/dto/update-index.dto.ts @@ -1,5 +1,5 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import type { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; import { IsObject, IsOptional, IsString } from "class-validator"; export class UpdateIndexDto { diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 79d471317..3c4d1d394 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -16,11 +16,11 @@ import { import { ConfigService } from "@nestjs/config"; import { sleep } from "src/common/utils"; -import { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; +import type { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; import { ISearchFilter } from "./interfaces/os-common.type"; import { CreateIndexDto } from "./dto/create-index.dto"; import { UpdateIndexDto } from "./dto/update-index.dto"; -import { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; +import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; @Injectable() export class OpensearchService implements OnModuleInit { From 50974020d8ea8f5e25db652625387cea72ebd0fb Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 11:23:15 +0100 Subject: [PATCH 08/46] fix unit test --- src/datasets/datasets.service.spec.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/datasets/datasets.service.spec.ts b/src/datasets/datasets.service.spec.ts index a38c9fabc..d5cfef7ab 100644 --- a/src/datasets/datasets.service.spec.ts +++ b/src/datasets/datasets.service.spec.ts @@ -4,7 +4,6 @@ import { Test, TestingModule } from "@nestjs/testing"; import { Model } from "mongoose"; import { InitialDatasetsService } from "src/initial-datasets/initial-datasets.service"; import { LogbooksService } from "src/logbooks/logbooks.service"; -import { ElasticSearchService } from "src/elastic-search/elastic-search.service"; import { DatasetsService } from "./datasets.service"; import { DatasetClass } from "./schemas/dataset.schema"; import { CaslAbilityFactory } from "src/casl/casl-ability.factory"; @@ -14,6 +13,7 @@ import { CreateDatasetDto } from "./dto/create-dataset.dto"; import { plainToInstance } from "class-transformer"; import { ProposalsService } from "src/proposals/proposals.service"; import { MetadataKeysService } from "src/metadata-keys/metadatakeys.service"; +import { OpensearchService } from "src/opensearch/opensearch.service"; class InitialDatasetsServiceMock {} @@ -21,8 +21,6 @@ class LogbooksServiceMock {} class CaslAbilityFactoryMock {} -class ElasticSearchServiceMock {} - class MetadataKeysServiceMock { insertManyFromSource = jest.fn().mockResolvedValue([]); replaceManyFromSource = jest.fn().mockResolvedValue(undefined); @@ -124,7 +122,7 @@ describe("DatasetsService", () => { useClass: InitialDatasetsServiceMock, }, { provide: LogbooksService, useClass: LogbooksServiceMock }, - { provide: ElasticSearchService, useClass: ElasticSearchServiceMock }, + { provide: OpensearchService, useValue: null }, { provide: MetadataKeysService, useClass: MetadataKeysServiceMock }, { provide: CaslAbilityFactory, useClass: CaslAbilityFactoryMock }, { provide: ProposalsService, useClass: ProposalsServiceMock }, From bf1d914367e2b41cc1ce3005484b9f34b9c389ae Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 13:38:40 +0100 Subject: [PATCH 09/46] update api test for opensearch --- .github/workflows/test.yml | 13 ++++++------- CI/ESS/docker-compose.api.yaml | 21 ++++++++++----------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48931c943..580044ab7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -103,14 +103,14 @@ jobs: run: npm run build api_tests: - name: API tests with ElasticSearch enabled - ${{ matrix.elasticsearch_enabled }} + name: API tests with OpenSearch enabled - ${{ matrix.opensearch_enabled }} needs: [install-and-cache] runs-on: ubuntu-latest strategy: fail-fast: false matrix: - elasticsearch_enabled: ["yes", "no"] + opensearch_enabled: ["yes", "no"] steps: - name: Checkout code @@ -130,11 +130,10 @@ jobs: - name: API tests env: - ELASTICSEARCH_ENABLED: ${{ matrix.elasticsearch_enabled }} - COMPOSE_PROFILES: ${{ matrix.elasticsearch_enabled == 'yes' && 'elasticsearch' || ''}} - CLUSTER_NAME: es-cluster - ES_PASSWORD: duo-password - MEM_LIMIT: 4G + OPENSEARCH_ENABLED: ${{ matrix.opensearch_enabled }} + COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} + CLUSTER_NAME: os-test-cluster + OPENSEARCH_PASSWORD: apiTestPassword STACK_VERSION: 8.8.2 # Start mongo container and app before running api tests run: | diff --git a/CI/ESS/docker-compose.api.yaml b/CI/ESS/docker-compose.api.yaml index f969b2e09..e6713a5c8 100644 --- a/CI/ESS/docker-compose.api.yaml +++ b/CI/ESS/docker-compose.api.yaml @@ -5,22 +5,22 @@ services: - mongodb_data:/bitnami ports: - "27017:27017" - es01: + opensearch: profiles: - - elasticsearch + - opensearch depends_on: - mongodb - image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} + image: opensearchproject/opensearch:3.5.0 ports: - "9200:9200" environment: - - node.name=es01 - - ES_JAVA_OPTS=-Xms2g -Xmx2g - - cluster.name=${CLUSTER_NAME} - - cluster.initial_master_nodes=es01 - - ELASTIC_PASSWORD=${ES_PASSWORD} + - node.name=opensearch + - discovery.type=single-node + - cluster.name=os-test-cluster - bootstrap.memory_lock=true - mem_limit: ${MEM_LIMIT} + - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=apiTestPassword + mem_limit: 4G ulimits: memlock: soft: -1 @@ -28,5 +28,4 @@ services: volumes: mongodb_data: driver: local - es01: - driver: local + From fe1adc1b6af4693e38da9f65375a3eb43e0afca9 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 13:45:20 +0100 Subject: [PATCH 10/46] fix opensearch api test --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 580044ab7..d89650698 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -137,6 +137,7 @@ jobs: STACK_VERSION: 8.8.2 # Start mongo container and app before running api tests run: | + cp opensearchConfig.example.json opensearchConfig.json cp CI/ESS/docker-compose.api.yaml docker-compose.yaml docker compose up --build -d npm run test:api From 0ce95e987c3c1a62cbe856a8e2766b2247e6a9ba Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 13:53:47 +0100 Subject: [PATCH 11/46] added missing host for api test --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d89650698..179f9d0e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -133,6 +133,7 @@ jobs: OPENSEARCH_ENABLED: ${{ matrix.opensearch_enabled }} COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} CLUSTER_NAME: os-test-cluster + OPENSEARCH_HOST: https://localhost:9200 OPENSEARCH_PASSWORD: apiTestPassword STACK_VERSION: 8.8.2 # Start mongo container and app before running api tests From abef90e9604137386afa2332232f8c46beb1ccb6 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 14:03:16 +0100 Subject: [PATCH 12/46] fix api test --- .github/workflows/test.yml | 2 +- CI/ESS/docker-compose.api.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 179f9d0e9..956e1b2d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -134,7 +134,7 @@ jobs: COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} CLUSTER_NAME: os-test-cluster OPENSEARCH_HOST: https://localhost:9200 - OPENSEARCH_PASSWORD: apiTestPassword + OPENSEARCH_PASSWORD: apiTestPassword1_ STACK_VERSION: 8.8.2 # Start mongo container and app before running api tests run: | diff --git a/CI/ESS/docker-compose.api.yaml b/CI/ESS/docker-compose.api.yaml index e6713a5c8..5fc0a7e58 100644 --- a/CI/ESS/docker-compose.api.yaml +++ b/CI/ESS/docker-compose.api.yaml @@ -19,7 +19,7 @@ services: - cluster.name=os-test-cluster - bootstrap.memory_lock=true - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=apiTestPassword + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=apiTestPassword1_ mem_limit: 4G ulimits: memlock: From f1e56ba0d776160e01444f4add3ab06a2db18680 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 14:27:55 +0100 Subject: [PATCH 13/46] isIndexExists to return boolean value instead of body & fix opensearch API test --- .github/workflows/test.yml | 4 ---- src/opensearch/opensearch.service.ts | 5 ++--- test/config/.env | 11 +++-------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 956e1b2d3..381fcbc3e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -132,10 +132,6 @@ jobs: env: OPENSEARCH_ENABLED: ${{ matrix.opensearch_enabled }} COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} - CLUSTER_NAME: os-test-cluster - OPENSEARCH_HOST: https://localhost:9200 - OPENSEARCH_PASSWORD: apiTestPassword1_ - STACK_VERSION: 8.8.2 # Start mongo container and app before running api tests run: | cp opensearchConfig.example.json opensearchConfig.json diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 3c4d1d394..8807e581d 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -133,9 +133,8 @@ export class OpensearchService implements OnModuleInit { } async isIndexExists(index = this.defaultIndex) { - return await this.osClient.indices.exists({ - index, - }); + const { body: indexExists } = await this.osClient.indices.exists({ index }); + return indexExists; } async isPopulated(index = this.defaultIndex) { diff --git a/test/config/.env b/test/config/.env index ae29b1da9..8942ef593 100644 --- a/test/config/.env +++ b/test/config/.env @@ -28,14 +28,9 @@ DOI_PREFIX="10.17199/" REGISTER_DOI_URI="https://api.test.datacite.org/dois" REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" -ES_HOST=https://localhost:9200 -ES_USERNAME=elastic -ES_PASSWORD=duo-password -MONGODB_COLLECTION=Dataset -ES_MAX_RESULT=10000 -ES_FIELDS_LIMIT=1000 -ES_INDEX=dataset -ES_REFRESH=wait_for + +OPENSEARCH_HOST=https://localhost:9200 +OPENSEARCH_PASSWORD=apiTestPassword1_ #history TRACKABLE_STRATEGY=delta From cd31ac6da94aa4b8b778075063818651745d78f6 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 14:37:16 +0100 Subject: [PATCH 14/46] revert removed testData --- test/DatasetV4.js | 54 ++++++++++++++++++++++---------------- test/TestData.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 22 deletions(-) diff --git a/test/DatasetV4.js b/test/DatasetV4.js index 36bb6aed0..e8e5d71da 100644 --- a/test/DatasetV4.js +++ b/test/DatasetV4.js @@ -314,7 +314,7 @@ describe("2500: Datasets v4 tests", () => { it("0126: adds a new dataset with scientificMetadata", async () => { return request(appUrl) .post("/api/v4/datasets") - .send(TestData.ScientificMetadataForElasticSearchV4) + .send(TestData.ScientificMetadataForOpensearchV4) .auth(accessTokenAdminIngestor, { type: "bearer" }) .expect(TestData.EntryCreatedStatusCode) .expect("Content-Type", /json/) @@ -331,7 +331,7 @@ describe("2500: Datasets v4 tests", () => { it("0127: should be able to add a new dataset with non-empty datasetLifecycle", async () => { const newDataset = { - ...TestData.ScientificMetadataForElasticSearchV4, + ...TestData.ScientificMetadataForOpensearchV4, datasetlifecycle: { archivable: false, retrievable: true, @@ -423,7 +423,7 @@ describe("2500: Datasets v4 tests", () => { .send(proposalBody) .auth(accessTokenAdminIngestor, { type: "bearer" }); const proposalId = proposalRes.body.proposalId; - + const dataset = { ...TestData.DerivedCorrectMinV4, proposalIds: [proposalId], @@ -442,7 +442,7 @@ describe("2500: Datasets v4 tests", () => { .post("/api/v4/datasets") .send({ ...TestData.DerivedCorrectMinV4, proposalIds: [] }) .auth(accessTokenAdminIngestor, { type: "bearer" }); - + const res2 = await request(appUrl) .post("/api/v4/datasets") .send({ ...TestData.DerivedCorrectMinV4 }) @@ -458,9 +458,14 @@ describe("2500: Datasets v4 tests", () => { const proposalAfter = await request(appUrl) .get(`/api/v3/proposals/${encodeURIComponent(proposalId)}`) .auth(accessTokenAdminIngestor, { type: "bearer" }); - console.log("DEBUG numberOfDatasets: ", proposalAfter.body.numberOfDatasets); + console.log( + "DEBUG numberOfDatasets: ", + proposalAfter.body.numberOfDatasets, + ); console.log("DEBUG initialCount: ", initialCount); - proposalAfter.body.should.have.property("numberOfDatasets").and.equal(initialCount); + proposalAfter.body.should.have + .property("numberOfDatasets") + .and.equal(initialCount); }); }); @@ -775,13 +780,15 @@ describe("2500: Datasets v4 tests", () => { it("0211: should fetch dataset relation fields if provided in the filter as obj and add scopes", async () => { const filter = { where: { pid: derivedDatasetMinPid }, - include: [{ - relation: "instruments", - scope: { - where: - { uniqueName: TestData.InstrumentCorrect1.uniqueName }, fields: ["uniqueName"] - } - }], + include: [ + { + relation: "instruments", + scope: { + where: { uniqueName: TestData.InstrumentCorrect1.uniqueName }, + fields: ["uniqueName"], + }, + }, + ], }; const instrument1 = await request(appUrl) @@ -790,7 +797,7 @@ describe("2500: Datasets v4 tests", () => { .set("Accept", "application/json") .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) .expect(TestData.EntryCreatedStatusCode) - .expect("Content-Type", /json/) + .expect("Content-Type", /json/); const instrument2 = await request(appUrl) .post("/api/v3/Instruments") @@ -798,7 +805,7 @@ describe("2500: Datasets v4 tests", () => { .set("Accept", "application/json") .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) .expect(TestData.EntryCreatedStatusCode) - .expect("Content-Type", /json/) + .expect("Content-Type", /json/); await request(appUrl) .patch(`/api/v4/datasets/${encodeURIComponent(derivedDatasetMinPid)}`) @@ -818,18 +825,19 @@ describe("2500: Datasets v4 tests", () => { const [firstDataset] = res.body; firstDataset.should.have.property("pid"); - firstDataset.should.have.property("instruments").eql([{ - _id: instrument1.body.id, - uniqueName: TestData.InstrumentCorrect1.uniqueName - }] - ); + firstDataset.should.have.property("instruments").eql([ + { + _id: instrument1.body.id, + uniqueName: TestData.InstrumentCorrect1.uniqueName, + }, + ]); firstDataset.should.not.have.property("datablocks"); }); }); it("0212: should fetch specific dataset fields excluding fields provided in field", async () => { const filter = { - fields: {datasetName: 0, contactEmail: 1}, + fields: { datasetName: 0, contactEmail: 1 }, }; return request(appUrl) @@ -1096,7 +1104,9 @@ describe("2500: Datasets v4 tests", () => { .then((res) => { res.body.should.be.a("object"); res.body.should.have.property("message"); - res.body.message.should.match(/Invalid \$project :: caused by :: Path collision at origdatablocks/); + res.body.message.should.match( + /Invalid \$project :: caused by :: Path collision at origdatablocks/, + ); }); }); }); diff --git a/test/TestData.js b/test/TestData.js index 4ddcc0573..f9b5e5096 100644 --- a/test/TestData.js +++ b/test/TestData.js @@ -1416,6 +1416,73 @@ const TestData = { PatchDataQualityMetricsInvalid: { dataQualityMetrics: "test", }, + + ScientificMetadataForOpensearch: { + ownerGroup: faker.company.name(), + creationLocation: faker.location.city(), + principalInvestigator: faker.internet.username(), + type: "raw", + datasetName: faker.string.sample(), + creationTime: faker.date.past(), + sourceFolder: faker.system.directoryPath(), + owner: faker.internet.username(), + size: faker.number.int({ min: 0, max: 100000000 }), + proposalId: faker.string.numeric(6), + contactEmail: faker.internet.email(), + scientificMetadata: { + with_key_value: "some text", + with_unit_and_value_si: { + value: 100, + unit: "mbar l/s/cm^2", + valueSI: 100000, + unitSI: "kg / s^3", + }, + with_number: { + value: 111, + unit: "", + }, + with_string: { + value: "222", + unit: "", + }, + }, + }, + + ScientificMetadataForOpensearchV4: { + ownerGroup: faker.company.name(), + creationLocation: faker.location.city(), + type: "raw", + datasetName: faker.string.sample(), + creationTime: faker.date.past(), + sourceFolder: faker.system.directoryPath(), + owner: faker.internet.username(), + size: faker.number.int({ min: 0, max: 100000000 }), + contactEmail: faker.internet.email(), + scientificMetadata: { + with_key_value: "some text", + with_unit_and_value_si: { + value: 100, + unit: "mbar l/s/cm^2", + valueSI: 100000, + unitSI: "kg / s^3", + }, + with_number: { + value: 111, + unit: "", + }, + with_string: { + value: "222", + unit: "", + }, + with_no_unit: { + value: 333, + }, + with_undefined_unit: { + value: 777, + unit: undefined, + }, + }, + }, }; const isEqualWithAny = (actual, expected) => From b5b9aa410f73e3c9744694b6d157e240ff16dc95 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 15:05:22 +0100 Subject: [PATCH 15/46] fix api test --- .github/workflows/test.yml | 3 +-- src/datasets/datasets.controller.ts | 11 +++++++---- test/config/.env | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 381fcbc3e..1fe3cd63a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -130,11 +130,10 @@ jobs: - name: API tests env: - OPENSEARCH_ENABLED: ${{ matrix.opensearch_enabled }} COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} # Start mongo container and app before running api tests run: | cp opensearchConfig.example.json opensearchConfig.json cp CI/ESS/docker-compose.api.yaml docker-compose.yaml docker compose up --build -d - npm run test:api + OPENSEARCH_ENABLED=${{ matrix.opensearch_enabled }} npm run test:api diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index 147f9aaf8..c196e277b 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -992,10 +992,12 @@ export class DatasetsController { let datasets: DatasetDocument[] | null; - const osEnabled = this.configService.get("opensearch.enabled"); + const osEnabled = + this.configService.get("opensearch.enabled") || "no"; + const textSearch = parsedFilters.fields?.text; - if (osEnabled && textSearch) { + if (osEnabled != "no" && textSearch) { const isAdmin = canViewAny; datasets = await this.datasetsService.opensearchQuery( parsedFilters, @@ -1079,10 +1081,11 @@ export class DatasetsController { facets: JSON.parse(filters.facets ?? "[]"), }; - const osEnabled = this.configService.get("opensearch.enabled"); + const osEnabled = + this.configService.get("opensearch.enabled") || "no"; const textSearch = parsedFilters.fields?.text; - if (osEnabled && textSearch) { + if (osEnabled != "no" && textSearch) { const isAdmin = canViewAny; return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); } diff --git a/test/config/.env b/test/config/.env index 8942ef593..93ed85662 100644 --- a/test/config/.env +++ b/test/config/.env @@ -31,6 +31,7 @@ REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" OPENSEARCH_HOST=https://localhost:9200 OPENSEARCH_PASSWORD=apiTestPassword1_ +OPENSEARCH_ENABLED=no #history TRACKABLE_STRATEGY=delta From 08da04809aeef645f457c42fe9801ceeeea6e1d4 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 15:17:17 +0100 Subject: [PATCH 16/46] update unit tests --- src/opensearch/opensearch.service.spec.ts | 10 +++++----- src/opensearch/providers/query-builder.service.spec.ts | 6 ------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/opensearch/opensearch.service.spec.ts b/src/opensearch/opensearch.service.spec.ts index 7b04c1c6f..0f327b5b7 100644 --- a/src/opensearch/opensearch.service.spec.ts +++ b/src/opensearch/opensearch.service.spec.ts @@ -13,11 +13,11 @@ describe("OpensearchService", () => { const mockConfigService = { get: () => ({ - "elasticSearch.host": "fake", - "elasticSearch.username": "fake", - "elasticSearch.password": "fake", - "elasticSearch.enabled": "yes", - "elasticSearch.defaultIndex": "fake", + "opensearch.host": "fake", + "opensearch.username": "fake", + "opensearch.password": "fake", + "opensearch.enabled": "yes", + "opensearch.defaultIndex": "fake", }), }; diff --git a/src/opensearch/providers/query-builder.service.spec.ts b/src/opensearch/providers/query-builder.service.spec.ts index bb65f6bda..d47ade637 100644 --- a/src/opensearch/providers/query-builder.service.spec.ts +++ b/src/opensearch/providers/query-builder.service.spec.ts @@ -7,18 +7,12 @@ describe("SearchQueryService", () => { const mockSearchQueryWithoutFilters = { text: "fake text", ownerGroup: ["fake"], - creationLocation: ["fake"], - type: ["fake"], - keywords: ["fake"], isPublished: false, }; const mockSearcQueryhWitoutText = { text: "", ownerGroup: ["fake"], - creationLocation: ["fake"], - type: ["fake"], - keywords: ["fake"], isPublished: false, }; From 7d1d5a720034877de319fe1ff22c0eed9c5d5ae1 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 15:31:49 +0100 Subject: [PATCH 17/46] minor type correction for createFullfacetPipeline --- src/common/utils.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/common/utils.ts b/src/common/utils.ts index 5f2567af8..9d118df66 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -700,13 +700,13 @@ export const createFullqueryFilter = ( }; const pipelineHandler = { - handleOpensearchIdList: ( + handleOpensearchIdList: ( pipeline: PipelineStage[], - fields: T, + fields: Y, key: string, ) => { const match = { - $match: { _id: { $in: fields[key as keyof T] as string[] } }, + $match: { _id: { $in: fields[key as keyof Y] as string[] } }, }; return pipeline.unshift(match); }, @@ -848,13 +848,13 @@ export const createFullfacetPipeline = ( switch (key) { case "openSearchIdList": - pipelineHandler.handleOpensearchIdList(pipeline, fields, key); + pipelineHandler.handleOpensearchIdList(pipeline, fields, key); break; case "text": - pipelineHandler.handleTextSearch(pipeline, model, fields, key); + pipelineHandler.handleTextSearch(pipeline, model, fields, key); break; case idField: - pipelineHandler.handleIdFieldSearch( + pipelineHandler.handleIdFieldSearch( pipeline, model, fields, @@ -863,17 +863,22 @@ export const createFullfacetPipeline = ( ); break; case "mode": - pipelineHandler.handleModeSearch(pipeline, fields, key, idField); + pipelineHandler.handleModeSearch(pipeline, fields, key, idField); break; case "userGroups": - pipelineHandler.handleUserGroupSearch(pipeline, model, fields, key); + pipelineHandler.handleUserGroupSearch( + pipeline, + model, + fields, + key, + ); break; case "scientific": case "sampleCharacteristics": - pipelineHandler.handleScientificQuery(pipeline, fields, key); + pipelineHandler.handleScientificQuery(pipeline, fields, key); break; default: - pipelineHandler.handleGenericSearch(pipeline, model, fields, key); + pipelineHandler.handleGenericSearch(pipeline, model, fields, key); } }); From 355816b3e0a36d06dd5c6d141c515a866e4a47ea Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 15:51:04 +0100 Subject: [PATCH 18/46] add interface IDatasetOpenSearchPipeline for opensearch fullfacet pipe builder --- src/datasets/datasets.service.ts | 12 +++++------- src/datasets/interfaces/dataset-filters.interface.ts | 4 ++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index b056993e9..f0a526c32 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -50,6 +50,7 @@ import { IDatasetFields, IDatasetFilters, IDatasetFiltersV4, + IDatasetOpenSearchPipeline, IDatasetRelation, IDatasetScopes, } from "./interfaces/dataset-filters.interface"; @@ -394,13 +395,10 @@ export class DatasetsService { fields.openSearchIdList = osResult.data; delete fields.text; - const pipeline = createFullfacetPipeline( - this.datasetModel, - "pid", - fields, - facets, - "", - ); + const pipeline = createFullfacetPipeline< + DatasetDocument, + IDatasetOpenSearchPipeline + >(this.datasetModel, "pid", fields, facets, ""); return await this.datasetModel.aggregate(pipeline).exec(); } diff --git a/src/datasets/interfaces/dataset-filters.interface.ts b/src/datasets/interfaces/dataset-filters.interface.ts index ebbe4ac1f..7d1954482 100644 --- a/src/datasets/interfaces/dataset-filters.interface.ts +++ b/src/datasets/interfaces/dataset-filters.interface.ts @@ -43,6 +43,10 @@ export interface IDatasetFields { [key: string]: unknown; } +export type IDatasetOpenSearchPipeline = Omit & { + openSearchIdList?: string[]; +}; + export type IDatasetScopesV4 = | IOrigDatablockFiltersV4 | IFiltersV4 From ff37d33dfb98c102e22337fc3dc0f1b8dc76ff0b Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 11 Mar 2026 17:57:14 +0100 Subject: [PATCH 19/46] minor check change for osEnabled --- src/datasets/datasets.controller.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index c196e277b..dff10094e 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -993,11 +993,11 @@ export class DatasetsController { let datasets: DatasetDocument[] | null; const osEnabled = - this.configService.get("opensearch.enabled") || "no"; + this.configService.get("opensearch.enabled") || "no"; const textSearch = parsedFilters.fields?.text; - if (osEnabled != "no" && textSearch) { + if (osEnabled === "yes" && textSearch) { const isAdmin = canViewAny; datasets = await this.datasetsService.opensearchQuery( parsedFilters, @@ -1082,10 +1082,10 @@ export class DatasetsController { }; const osEnabled = - this.configService.get("opensearch.enabled") || "no"; + this.configService.get("opensearch.enabled") || "no"; const textSearch = parsedFilters.fields?.text; - if (osEnabled != "no" && textSearch) { + if (osEnabled === "yes" && textSearch) { const isAdmin = canViewAny; return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); } From c9982e20ac0ebae54b09fc5e7eb035a75ba5c396 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 12 Mar 2026 15:24:23 +0100 Subject: [PATCH 20/46] remove unused configs & update env example --- .env.example | 21 +++++++++++---------- src/config/configuration.ts | 9 --------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index df65e82ec..cf8950142 100644 --- a/.env.example +++ b/.env.example @@ -84,16 +84,17 @@ MS365_CLIENT_ID= MS365_CLIENT_SECRET= POLICY_PUBLICATION_SHIFT=3 years POLICY_RETENTION_SHIFT=-1 (indefinitely) -ELASTICSEARCH_ENABLED=<"yes"|"no"> -ES_HOST="https://localhost:9200" -ES_USERNAME="elastic" -ES_PASSWORD="duo-password" -ES_PORT=9200 -MONGODB_COLLECTION="Dataset" -ES_MAX_RESULT=100000 -ES_FIELDS_LIMIT=400000 -ES_INDEX="dataset" -ES_REFRESH=<"wait_for"|"false"> +OPENSEARCH_ENABLED=<"yes"|"no"> +OPENSEARCH_ENABLED="https://localhost:9200" +OPENSEARCH_ENABLED="admin" +OPENSEARCH_ENABLED="Scicat-password2026" +OPENSEARCH_DEFAULT_INDEX="dataset" +# use wait_for for testing and development to ensure that the index is updated before the next operation, +# use false for production to improve performance by not waiting for the index to refresh before returning a +OPENSEARCH_REFRESH=<"wait_for"|"false"> + + + STACK_VERSION="8.8.2" CLUSTER_NAME="es-cluster" MEM_LIMIT="4G" diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 0bdc732c7..4580d2104 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -385,15 +385,6 @@ const configuration = () => { password: process.env.OPENSEARCH_PASSWORD, host: process.env.OPENSEARCH_HOST, refresh: process.env.OPENSEARCH_REFRESH, - maxResultWindow: parseInt( - process.env.OPENSEARCH_MAX_RESULT || "100000", - 10, - ), - fieldsLimit: parseInt( - process.env.OPENSEARCH_FIELDS_LIMIT || "100000", - 10, - ), - mongoDBCollection: process.env.MONGODB_COLLECTION, defaultIndex: process.env.OPENSEARCH_DEFAULT_INDEX ?? "dataset", }, metrics: { From b3f3216f0c555066637f30e73db08f16ae230365 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Mon, 16 Mar 2026 15:17:55 +0100 Subject: [PATCH 21/46] remove unused compose files & replace es image from all compose files & replace ES envs with OS envs --- CI/E2E/backend.env | 15 +++++------ CI/E2E/docker-compose-local-no-es.yaml | 27 ------------------- CI/E2E/docker-compose.yaml | 37 +++++++++++--------------- CI/ESS/docker-compose.api.yaml | 2 +- CI/ESS/docker-compose.yaml | 25 ++++++++++------- docker-compose.dev.yaml | 4 +-- 6 files changed, 39 insertions(+), 71 deletions(-) delete mode 100644 CI/E2E/docker-compose-local-no-es.yaml diff --git a/CI/E2E/backend.env b/CI/E2E/backend.env index 9a294163b..8abb4fd28 100644 --- a/CI/E2E/backend.env +++ b/CI/E2E/backend.env @@ -36,12 +36,9 @@ DATASET_CREATION_VALIDATION_REGEX="^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][ PROPOSAL_GROUPS="proposalingestor" SAMPLE_GROUPS="" -ELASTICSEARCH_ENABLED='yes' -ES_HOST=http://es01:9200 -ES_USERNAME=elastic -ES_PASSWORD=Scicat-password2026 -MONGODB_COLLECTION=Dataset -ES_MAX_RESULT=210000 -ES_FIELDS_LIMIT=400000 -ES_INDEX="dataset" -ES_REFRESH="wait_for" +OPENSEARCH_ENABLED=yes +OPENSEARCH_DEFAULT_INDEX="dataset" +OPENSEARCH_HOST=https://localhost:9200 +OPENSEARCH_USERNAME=admin +OPENSEARCH_PASSWORD=Scicat-password2026 +OPENSEARCH_REFRESH="wait_for" \ No newline at end of file diff --git a/CI/E2E/docker-compose-local-no-es.yaml b/CI/E2E/docker-compose-local-no-es.yaml deleted file mode 100644 index 473cd43e4..000000000 --- a/CI/E2E/docker-compose-local-no-es.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3.2" -services: - mongodb: - image: bitnami/mongodb:latest - volumes: - - "mongodb_data:/bitnami" - ports: - - "27017:27017" - scichat-loopback: - image: dacat/scichat-loopback:e2e - command: - [ - "./wait-for-it.sh", - "mongodb:27017", - "--", - "node", - "-r", - "dotenv/config", - "." - ] - volumes: - - ".env.scichat-loopback:/home/node/app/.env" - depends_on: - - mongodb -volumes: - mongodb_data: - driver: local diff --git a/CI/E2E/docker-compose.yaml b/CI/E2E/docker-compose.yaml index 9435844ef..b5405000e 100644 --- a/CI/E2E/docker-compose.yaml +++ b/CI/E2E/docker-compose.yaml @@ -8,11 +8,11 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock mongodb: - image: "bitnami/mongodb:latest" + image: "mongo:latest" ports: - "27017:27017" volumes: - - "mongodb_data:/bitnami" + - "mongodb_data:/data/db" healthcheck: test: echo 'db.runCommand("ping").ok' | mongosh mongodb:27017/test --quiet interval: 10s @@ -63,35 +63,28 @@ services: - "traefik.http.routers.frontend.rule=PathPrefix(`/`)" - "traefik.http.routers.frontend.entrypoints=web" - es01: - image: docker.elastic.co/elasticsearch/elasticsearch:8.8.2 + opensearch: + depends_on: + - mongodb + image: opensearchproject/opensearch:3.5.0 ports: - 9200:9200 environment: - - xpack.security.enabled=false - - node.name=es01 - - ES_JAVA_OPTS=-Xms512m -Xmx512m - - cluster.name=es-cluster - - cluster.initial_master_nodes=es01 + - node.name=opensearch + - discovery.type=single-node + - cluster.name=os-cluster - bootstrap.memory_lock=true - mem_limit: 1g + - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 + volumes: + - opensearch_data:/usr/share/opensearch/data + mem_limit: 4G ulimits: memlock: soft: -1 hard: -1 - healthcheck: - test: - [ - "CMD-SHELL", - "curl -s -X GET 'http://es01:9200/_cluster/health?pretty' | grep status | grep -q '\\(green\\|yellow\\)'" - ] - interval: 30s - timeout: 10s - start_period: 60s - retries: 4 - volumes: mongodb_data: driver: local - es01: + opensearch_data: driver: local diff --git a/CI/ESS/docker-compose.api.yaml b/CI/ESS/docker-compose.api.yaml index 5fc0a7e58..5dbc7c67f 100644 --- a/CI/ESS/docker-compose.api.yaml +++ b/CI/ESS/docker-compose.api.yaml @@ -2,7 +2,7 @@ services: mongodb: image: mongo:latest volumes: - - mongodb_data:/bitnami + - "mongodb_data:/data/db" ports: - "27017:27017" opensearch: diff --git a/CI/ESS/docker-compose.yaml b/CI/ESS/docker-compose.yaml index 2e8f9796f..f076a64bb 100644 --- a/CI/ESS/docker-compose.yaml +++ b/CI/ESS/docker-compose.yaml @@ -27,22 +27,27 @@ services: depends_on: - mongodb - es01: + opensearch: depends_on: - mongodb - image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} + image: opensearchproject/opensearch:3.5.0 + ports: + - 9200:9200 environment: - - node.name=es01 - - ES_JAVA_OPTS=-Xms2g -Xmx2g - - cluster.name=${CLUSTER_NAME} - - cluster.initial_master_nodes=es01 - - ELASTIC_PASSWORD=${ES_PASSWORD} + - node.name=opensearch + - discovery.type=single-node + - cluster.name=os-cluster - bootstrap.memory_lock=true - mem_limit: ${MEM_LIMIT} + - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 + volumes: + - opensearch_data:/usr/share/opensearch/data + mem_limit: 4G ulimits: memlock: soft: -1 hard: -1 + # catanie: # build: # context: catanie @@ -114,5 +119,5 @@ volumes: driver: local rabbitmq_data: driver: local - es01: - driver: local + opensearch_data: + driver: local \ No newline at end of file diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index bf2d7cf96..dfdcb76ac 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -1,8 +1,8 @@ services: mongodb: - image: docker.io/bitnami/mongodb:4.2 + image: mongo:latest volumes: - - mongodb_data:/bitnami + - "mongodb_data:/data/db" backend: build: context: . From 838d6a7d56fb5e5195f8793d48dae6165f904637 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Mon, 16 Mar 2026 15:52:42 +0100 Subject: [PATCH 22/46] fix for permission filter --- src/opensearch/providers/query-builder.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opensearch/providers/query-builder.service.ts b/src/opensearch/providers/query-builder.service.ts index 9b3e52f00..82b8f4199 100644 --- a/src/opensearch/providers/query-builder.service.ts +++ b/src/opensearch/providers/query-builder.service.ts @@ -27,7 +27,7 @@ export class SearchQueryService { private buildFilterFields(fields: ISearchFilter): QueryContainer[] { const filter: QueryContainer[] = []; - if (fields.userGroups) { + if (fields.userGroups && fields.userGroups.length > 0) { filter.push({ bool: { should: [ From 710975f65292aacd2ffc8cf9a55e1ce44c302a60 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 17 Mar 2026 15:03:51 +0100 Subject: [PATCH 23/46] update default OS password for local development and remove .env.opensearch file --- .env.example | 2 +- CI/E2E/.env.open-search | 3 --- CI/E2E/backend.env | 2 +- CI/E2E/docker-compose-local.yaml | 24 ++++++++++++------------ CI/E2E/docker-compose.yaml | 2 +- CI/ESS/docker-compose.api.yaml | 2 +- CI/ESS/docker-compose.yaml | 2 +- package.json | 2 +- test/config/.env | 2 +- 9 files changed, 19 insertions(+), 22 deletions(-) delete mode 100644 CI/E2E/.env.open-search diff --git a/.env.example b/.env.example index cf8950142..373523608 100644 --- a/.env.example +++ b/.env.example @@ -87,7 +87,7 @@ POLICY_RETENTION_SHIFT=-1 (indefinitely) OPENSEARCH_ENABLED=<"yes"|"no"> OPENSEARCH_ENABLED="https://localhost:9200" OPENSEARCH_ENABLED="admin" -OPENSEARCH_ENABLED="Scicat-password2026" +OPENSEARCH_ENABLED="Scicat_default_password_2026" OPENSEARCH_DEFAULT_INDEX="dataset" # use wait_for for testing and development to ensure that the index is updated before the next operation, # use false for production to improve performance by not waiting for the index to refresh before returning a diff --git a/CI/E2E/.env.open-search b/CI/E2E/.env.open-search deleted file mode 100644 index 0973fa08f..000000000 --- a/CI/E2E/.env.open-search +++ /dev/null @@ -1,3 +0,0 @@ -MEM_LIMIT=4G -OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g -OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 \ No newline at end of file diff --git a/CI/E2E/backend.env b/CI/E2E/backend.env index 8abb4fd28..f97fe1c0f 100644 --- a/CI/E2E/backend.env +++ b/CI/E2E/backend.env @@ -40,5 +40,5 @@ OPENSEARCH_ENABLED=yes OPENSEARCH_DEFAULT_INDEX="dataset" OPENSEARCH_HOST=https://localhost:9200 OPENSEARCH_USERNAME=admin -OPENSEARCH_PASSWORD=Scicat-password2026 +OPENSEARCH_PASSWORD=Scicat_default_password_2026 OPENSEARCH_REFRESH="wait_for" \ No newline at end of file diff --git a/CI/E2E/docker-compose-local.yaml b/CI/E2E/docker-compose-local.yaml index e38f9369f..e8ce2a091 100644 --- a/CI/E2E/docker-compose-local.yaml +++ b/CI/E2E/docker-compose-local.yaml @@ -35,11 +35,11 @@ services: - discovery.type=single-node - cluster.name=os-cluster - bootstrap.memory_lock=true - - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat_default_password_2026 volumes: - opensearch_data:/usr/share/opensearch/data - mem_limit: ${MEM_LIMIT} + mem_limit: 4G ulimits: memlock: soft: -1 @@ -49,15 +49,15 @@ services: # Web UI for OpenSearch # Enable this service if you want to use OpenSearch Dashboards to visualize and debug your OpenSearch data. # Access it at http://localhost:5601 - opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:3.5.0 - ports: - - "5601:5601" - environment: - - OPENSEARCH_HOSTS=https://opensearch:9200 - - OPENSEARCH_USERNAME=admin - - OPENSEARCH_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} - - OPENSEARCH_SSL_VERIFICATIONMODE=none + # opensearch-dashboards: + # image: opensearchproject/opensearch-dashboards:3.5.0 + # ports: + # - "5601:5601" + # environment: + # - OPENSEARCH_HOSTS=https://opensearch:9200 + # - OPENSEARCH_USERNAME=admin + # - OPENSEARCH_PASSWORD=Scicat_default_password_2026 + # - OPENSEARCH_SSL_VERIFICATIONMODE=none volumes: mongodb_data: diff --git a/CI/E2E/docker-compose.yaml b/CI/E2E/docker-compose.yaml index b5405000e..f5d10923d 100644 --- a/CI/E2E/docker-compose.yaml +++ b/CI/E2E/docker-compose.yaml @@ -75,7 +75,7 @@ services: - cluster.name=os-cluster - bootstrap.memory_lock=true - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat_default_password_2026 volumes: - opensearch_data:/usr/share/opensearch/data mem_limit: 4G diff --git a/CI/ESS/docker-compose.api.yaml b/CI/ESS/docker-compose.api.yaml index 5dbc7c67f..b7236fea3 100644 --- a/CI/ESS/docker-compose.api.yaml +++ b/CI/ESS/docker-compose.api.yaml @@ -19,7 +19,7 @@ services: - cluster.name=os-test-cluster - bootstrap.memory_lock=true - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=apiTestPassword1_ + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat_default_password_2026 mem_limit: 4G ulimits: memlock: diff --git a/CI/ESS/docker-compose.yaml b/CI/ESS/docker-compose.yaml index f076a64bb..b412a700f 100644 --- a/CI/ESS/docker-compose.yaml +++ b/CI/ESS/docker-compose.yaml @@ -39,7 +39,7 @@ services: - cluster.name=os-cluster - bootstrap.memory_lock=true - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g - - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat-password2026 + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=Scicat_default_password_2026 volumes: - opensearch_data:/usr/share/opensearch/data mem_limit: 4G diff --git a/package.json b/package.json index a5905e895..394280046 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "test:api": "npm run test:api:jest --maxWorkers=50% && concurrently -k -s first \"wait-on http://localhost:3000/explorer/ && npm run test:api:mocha\" \"npm run start:test\"", "test:api:jest": "dotenv -o -e test/config/.env -e test/config/.env.override -- jest --config ./test/config/jest-e2e.json --maxWorkers=50%", "test:api:mocha": "dotenv -o -e test/config/.env -e test/config/.env.override -- mocha --config ./test/config/.mocharc.js -r chai/register-should.js", - "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml --env-file CI/E2E/.env.open-search up -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" + "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" }, "dependencies": { "@casl/ability": "^6.3.2", diff --git a/test/config/.env b/test/config/.env index 93ed85662..7e48108b9 100644 --- a/test/config/.env +++ b/test/config/.env @@ -30,7 +30,7 @@ REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" OPENSEARCH_HOST=https://localhost:9200 -OPENSEARCH_PASSWORD=apiTestPassword1_ +OPENSEARCH_PASSWORD=Scicat_default_password_2026 OPENSEARCH_ENABLED=no #history From f97d8df276775a785992841520d20a0a6d9f41e8 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 17 Mar 2026 15:05:37 +0100 Subject: [PATCH 24/46] correct prepare:local command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 394280046..8dcf35405 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "test:api": "npm run test:api:jest --maxWorkers=50% && concurrently -k -s first \"wait-on http://localhost:3000/explorer/ && npm run test:api:mocha\" \"npm run start:test\"", "test:api:jest": "dotenv -o -e test/config/.env -e test/config/.env.override -- jest --config ./test/config/jest-e2e.json --maxWorkers=50%", "test:api:mocha": "dotenv -o -e test/config/.env -e test/config/.env.override -- mocha --config ./test/config/.mocharc.js -r chai/register-should.js", - "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" + "prepare:local": "docker-compose -f CI/E2E/docker-compose-local.yaml up -d && cp test/config/functionalAccounts.json functionalAccounts.json && cp proposalTypes.example.json proposalTypes.json && cp publishedDataConfig.example.json publishedDataConfig.json" }, "dependencies": { "@casl/ability": "^6.3.2", From 905fee3b7649ff841caa584f4bd7137ca09ad261 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 17 Mar 2026 16:12:15 +0100 Subject: [PATCH 25/46] update opensearch doc --- docs/developer-guide/opensearch.md | 155 +++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 docs/developer-guide/opensearch.md diff --git a/docs/developer-guide/opensearch.md b/docs/developer-guide/opensearch.md new file mode 100644 index 000000000..840e9da50 --- /dev/null +++ b/docs/developer-guide/opensearch.md @@ -0,0 +1,155 @@ +--- +title: OpenSearch Integration +audience: Technical +created_by: Junjie Quan +created_on: 2026-03-17 +--- + +# OpenSearch Integration + +## Overview + +SciCat now includes an OpenSearch integration to support search functionality within the platform. The transition from Elasticsearch to OpenSearch was made following the Elasticsearch license change in order to maintain the use of fully open-source components in the project. + +The current OpenSearch integration supports partial text search for datasets. + +Text search currently targets only the following fields: + +- `datasetName` +- `description` + +All other filters and queries continue to be executed using MongoDB queries. + +SciCat uses the official OpenSearch JavaScript client `@opensearch-project/opensearch@^3.5.1` for indexing, synchronization, and search operations. This version is compatible with OpenSearch v3.5.0. + +To be able to start application with OpenSearch you will need to: + +## Getting Started + +To start the application with OpenSearch: + +1. Run `npm run prepare:local` to start the OpenSearch cluster as a Docker container. +2. Set `OPENSEARCH_ENABLED=yes` and provide values for all required environment variables: `OPENSEARCH_HOST`, `OPENSEARCH_USERNAME`, and `OPENSEARCH_PASSWORD`. + > **Note:** `OPENSEARCH_PASSWORD` must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` set when creating the OpenSearch container. +3. Start the application with `npm run start`. On successful connection you will see: + +``` + [Nest] 80126 - 03/17/2026, 3:09:41 PM LOG [Opensearch] Opensearch Connected +``` + +## Environment Configuration + +OpenSearch behavior is controlled using environment variables. +To enable OpenSearch, set `OPENSEARCH_ENABLED=yes` and provide values for all required environment variables: `OPENSEARCH_HOST`, `OPENSEARCH_USERNAME`, and `OPENSEARCH_PASSWORD`. + +| Variable | Example | Description | Required | +| -------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| `OPENSEARCH_ENABLED` | `"yes" \| "no"` | Controls whether OpenSearch is enabled on application startup. If not provided or set to `no`, OpenSearch will not be instantiated. | Yes | +| `OPENSEARCH_DEFAULT_INDEX` | `dataset` | Specifies the default index. If not provided, a default index named `dataset` will be created automatically. | No | +| `OPENSEARCH_HOST` | `https://localhost:9200` | Specifies the OpenSearch server endpoint. | Yes | +| `OPENSEARCH_USERNAME` | `"admin"` | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | Yes | +| `OPENSEARCH_PASSWORD` | `Scicat-password2026` | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | Yes | +| `OPENSEARCH_REFRESH` | `"wait_for" \| "false"` | Controls index refresh behavior. `wait_for` waits for the next refresh cycle before returning, which is useful for development and testing. `false` skips waiting and is recommended for production. Defaults to false. | No | + +## Index Configuration + +OpenSearch index settings and mappings can be customized using a configuration file. + +An optional `opensearchConfig.json` file can be mounted to `/home/node/app/opensearchConfig.json` in the container (or placed in the project root when running locally) to define custom index settings and mappings. + +If not provided, a default configuration will be loaded from `opensearchConfig.example.json` in the root, supporting partial text search on `datasetName` and `description`. + +For full configuration options, see: +https://docs.opensearch.org/latest/install-and-configure/configuring-opensearch/index/ + +## Query Logic + +Search queries follow the process below. + +### Access Filtering + +Before performing text search, results are filtered based on user access permissions: + +1. If the user belongs to one or more `userGroups`, these are applied as access filters. +2. If the user has no groups and is not an admin, the `isPublished` filter is applied. +3. Admin users bypass group-based access filters, which means queries are executed against a larger dataset. This is a known limitation and may result in slower response times for admin search requests. + +### Text Search + +After access filtering, OpenSearch performs a partial text search on the following fields: + +- `datasetName` +- `description` + +These fields are currently hardcoded in the `MustFields` enum (`src/opensearch/providers/fields.enum.ts`) and are not configurable. + +## API Endpoints + +All OpenSearch endpoints are restricted to admin users. + +### Create Index + +**POST** `/opensearch/create-index` + +- `index` (body, required): Target index name. default: `dataset` +- `settings` (body, optional): Index settings to apply. If not provided, defaults from `opensearchConfig.json` are used. + +--- + +### Sync Database + +**POST** `/opensearch/sync-database` + +Synchronizes datasets from MongoDB to OpenSearch. + +- `index` (query, optional): Index name. default: `dataset` + +--- + +### Search + +**POST** `/opensearch/search` + +Performs partial text search on datasets. + +- `textQuery` (query): Search text (applies to `datasetName` and `description`) +- `index` (query, optional): Target index name. default: `dataset` +- `limit` (query, optional): Max results +- `skip` (query, optional): Offset + +Returns matching dataset `_id`s. + +--- + +### Delete Index + +**POST** `/opensearch/delete-index` + +Deletes an index. + +- `index` (query): Target index name. + +--- + +### Get Index Configuration + +**GET** `/opensearch/get-index` + +Retrieves index settings and mappings. + +- `index` (query): Target index name. default: `dataset` + +--- + +### Update Index Settings + +**POST** `/opensearch/update-index` + +Updates index settings. Closes the index, applies the new settings, then reopens it. + +- `index` (body, required): Target index name. default: `dataset` +- `settings` (body, optional): Index settings to apply. If not provided, defaults from `opensearchConfig.json` are used. + +--- + +If you encounter performance issues related to OpenSearch, please open a [GitHub issue](https://github.com/SciCatProject/backend/issues) or report them at the SciCat meeting. From ed4e3cdcd971c5e34cd2c12add400cda2180a3df Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 17 Mar 2026 16:16:45 +0100 Subject: [PATCH 26/46] update documentation --- ...opensearch.md => opensearch-guidelines.md} | 18 ++ src/opensearch/opensearch_guide.md | 211 ------------------ 2 files changed, 18 insertions(+), 211 deletions(-) rename docs/developer-guide/{opensearch.md => opensearch-guidelines.md} (93%) delete mode 100644 src/opensearch/opensearch_guide.md diff --git a/docs/developer-guide/opensearch.md b/docs/developer-guide/opensearch-guidelines.md similarity index 93% rename from docs/developer-guide/opensearch.md rename to docs/developer-guide/opensearch-guidelines.md index 840e9da50..3bf592215 100644 --- a/docs/developer-guide/opensearch.md +++ b/docs/developer-guide/opensearch-guidelines.md @@ -37,6 +37,24 @@ To start the application with OpenSearch: [Nest] 80126 - 03/17/2026, 3:09:41 PM LOG [Opensearch] Opensearch Connected ``` +4. Open the Swagger page at `http://localhost:3000/explorer` and authorize with an `admin` token. +5. Execute `POST /opensearch/create-index` to create the index. + > **Note:** This step can be skipped if you want to use the default index `dataset`. +6. Execute `POST /opensearch/sync-database` to sync data from MongoDB into OpenSearch. On success you will see: + +```json +{ + "total": 21670, + "failed": 0, + "retry": 0, + "successful": 21670, + "noop": 0, + "time": 4777, + "bytes": 279159021, + "aborted": false +} +``` + ## Environment Configuration OpenSearch behavior is controlled using environment variables. diff --git a/src/opensearch/opensearch_guide.md b/src/opensearch/opensearch_guide.md deleted file mode 100644 index 1b43155cd..000000000 --- a/src/opensearch/opensearch_guide.md +++ /dev/null @@ -1,211 +0,0 @@ -# Getting started - -To be able to start application with Elasticsearch you will need to: - -1. Start with `npm prepare:local` to executes docker-compose file which pull and run Elasticsearch cluster as a docker container. -2. Start the application with `npm run start` - -# Check the .env file - -Following environment variables are required to run Elasticsearch with Scicat Backend: - -| `Variables` | Values | Description | -| ------------------------ | :--------------------- | :------------------------------------------------------------------------------------------------------------- | -| `ELASTICSEARCH_ENABLED` | `yes` or `no` | Flag to enable/disable the ElasticSearch service | -| `ES_HOST ` | https://localhost:9200 | Use `http` if `xpack.security.enabled` set to `false` | -| `ES_USERNAME` (optional) | elastic | Elasticsearch cluster username. Can be customized with docker image environment values with `ELASTIC_USERNAME` | -| `ES_PASSWORD` | password | Elasticsearch cluster password. Can be customized with docker image environment values `ELASTIC_PASSWORD` | -| `MONGODB_COLLECTION ` | Dataset | Collection name to be mapped into specified Elasticsearch index | -| `ES_MAX_RESULT ` | 210000 | Maximum records can be indexed into Elasticsearch. If not set, default is `10000` | -| `ES_FIELDS_LIMIT ` | 400000 | The total number of fields in an index. If not set, default is `1000` | -| `ES_INDEX ` | dataset | Setting default index for the application | -| `ES_REFRESH ` | wait_for | Wait for the change to become visible - CRUD actions | -| | | - -## \*\* NOTE \*\* - -`ES_REFRESH="wait_for"` is used for better testing flow. For production it should be set to false. Detailed docs about this setting can be found: [docs-refresh](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-refresh.html) - -# Steps to synchronize data with Elasitcsearch - -To enable search queries in Elasticsearch, you should adhere to the following steps: - -1. Open Swagger page `http://localhost:3000/explorer`, Authorize with `admin` token -2. Open elastic-search endpoints and execute `create-index`. - _**NOTE:**_ This step can be skipped if you want to use default index `dataset` -3. execute `sync-database` to sync data into the given index. - -If you see the following response, it means Elasticsearch is ready for use. - -``` -{ - "total": 21670, - "failed": 0, - "retry": 0, - "successful": 21670, - "noop": 0, - "time": 4777, - "bytes": 279159021, - "aborted": false -} -``` - -# Index, analysis and mapping settings - -Index settings in Elasticsearch are used to define various configurations and behaviors for an index. Index settings are vital for optimizing Elasticsearch's performance, relevance of search results, and resource management, ensuring that the index operates efficiently and effectively according to the specific needs of the application using it. - -Pre-defined index settings & dynamic mapping for nested objects can be found `/src/elastic-search/configuration/indexSetting.ts` - -Fields to be mapped into Elasticsearch index can be found `/src/elastic-search/configuration/datasetFieldMapping.ts` - -**Index settings:** - -``` -export const defaultElasticSettings = { - index: { - max_result_window: process.env.ES_MAX_RESULT || 2000000, - number_of_replicas: 0, - mapping: { - total_fields: { - limit: process.env.ES_FIELDS_LIMIT || 2000000, - }, - nested_fields: { - limit: 1000, - }, - }, - }, - analysis: { - analyzer: { - autocomplete: { - tokenizer: "autocomplete", - filter: ["lowercase"], - }, - autocomplete_search: { - tokenizer: "lowercase", - }, - }, - tokenizer: { - autocomplete: autocomplete_tokenizer, - }, - }, -} as IndicesIndexSettingsAnalysis; -``` - -| `Object` | Description | -| ----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `index` | It contains settings that determine how an Elasticsearch index works, like how many results you can get from a search and how many copies of the data it should store. More details in [index modules](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html) | -| `index.max_result_window` | Determines the maximum number of documents returned in a query. `Default is 10000` | -| `index.number_of_replicas` | Sets the number of replica shards. `Default is 1` | -| `index.mapping.total_fields.limit` | Sets the maximum number of fields an index can have. `Default is 1000` | -| `index.mapping.nested_fields.limit` | Sets the maximum number of nested fields. `Default is 50` | -| `analysis` | It defines how text data is processed before it is indexed, including custom analyzers and tokenizers which control the conversion of text into tokens or terms. More details in [analyzer](https://www.elastic.co/guide/en/elasticsearch/reference/current/analyzer.html) and [analysis](https://www.elastic.co/guide/en/elasticsearch/reference/8.8/analysis.html) | - -
- -**Dynamic mapping:** - -Dynamic mapping is currently only used for scientific field. More details in [Dynamic template](https://www.elastic.co/guide/en/elasticsearch/reference/8.8/dynamic-templates.html) - -``` -export const dynamic_template: Record[] = [ - { - string_as_keyword: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "string", - mapping: { - type: "keyword", - ignore_above: 256, - }, - }, - }, - { - long_as_double: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "long", - mapping: { - type: "double", - coerce: true, - ignore_malformed: true, - }, - }, - }, - { - date_as_keyword: { - path_match: "scientificMetadata.*.*", - match_mapping_type: "date", - mapping: { - type: "keyword", - ignore_above: 256, - }, - }, - }, -]; -``` - -# APIs - -`NOTE: All requests require admin permissions.` - -For search query integration it is suggested to use client search service directly, which can be found `/src/elastic-search/elastic-search.service.ts` - -**Following table is existing controller endpoints:** - -| endpoint | parameters | request | description | -| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | ------------------------------------------------- | -| /api/v3/elastic-search/create-index?index= | `string` | query | Create an index with this name | -| /api/v3/elastic-search/sync-database?index= | `string` | query | Sync datasets into elastic search with this index | -| /api/v3/elastic-search/search | `{ "text": "", "ownerGroup": [], "creationLocation": [], "type": [], "keywords": [], "isPublished": false, "scientific": [{"lhs": "", "relation": "", "rhs": 0, "unit": ""}]}` | body | Search query | -| /api/v3/elastic-search/delete-index | `string` | query | Delete an index with this name | -| /api/v3/elastic-search/get-index | `string` | query | Get an index settings with this name | -| /api/v3/elastic-search/update-index | `to be updated` | to be updated | to be updated | - -# Setting up a local Elasticsearch cluster - -## Docker Configuration - -For local development, you can spin up an Elasticsearch cluster using Docker with the following configuration: - -_Elasticsearch image configuration can be found in `./CI/E2E/docker-compose-local.yaml`_ - -``` -es01: - image: docker.elastic.co/elasticsearch/elasticsearch:8.8.2 - ports: - - 9200:9200 - environment: - - xpack.security.enabled=false - - node.name=es01 - - ES_JAVA_OPTS=-Xms2g -Xmx2g - - cluster.name=es-cluster - - cluster.initial_master_nodes=es01 - - ELASTIC_PASSWORD=password - - bootstrap.memory_lock=true - mem_limit: 4g - ulimits: - memlock: - soft: -1 - hard: -1 -``` - -| **`Settings`** | **`Description`** | -| ---------------------------- | -------------------------------------------------------------------------------------- | -| Image Version | Docker image for Elasticsearch: `docker.elastic.co/elasticsearch/elasticsearch:8.8.2`. | -| Port | Container-to-host port mapping: `9200:9200`. | -| mem_limit | Maximum container memory, influenced by `ES_JAVA_OPTS`. | -| ulimits.memlock (soft, hard) | Memory locking limits: `-1` for unlimited. | -| **`Variables`** | **`Description`** | -| xpack.security.enabled | Toggle for Elasticsearch security; `false` disables authentication for development. | -| node.name | Unique Elasticsearch node name; essential for cluster node identification. | -| ES_JAVA_OPTS | JVM heap memory settings for Elasticsearch performance (e.g., `-Xms2g -Xmx2g`). | -| cluster.name | Name of the Elasticsearch cluster for node grouping. | -| ELASTIC_PASSWORD | Password for the 'elastic' user; not needed if security is disabled. | -| bootstrap.memory_lock | Prevents swapping Elasticsearch memory to disk when enabled. | - -## Using Helm for Kubernetes - -For those looking to deploy on Kubernetes, refer to the official [Helm chart documentation](https://github.com/elastic/helm-charts/tree/main/elasticsearch) for Elasticsearch. Helm charts provide a more production-ready setup and can manage complex configurations. - -**More configuration information:** - -- [More complicated examples with docker-compose configuration](https://www.elastic.co/guide/en/elasticsearch/reference/8.8/docker.html) -- [More service configures for production](https://www.elastic.co/guide/en/elasticsearch/reference/8.8/important-settings.html) From 0cd3c6fb90923224f740dadf2ed6b0c95d2e4f23 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 13:49:13 +0100 Subject: [PATCH 27/46] add api test for search query --- .env.example | 12 +- CI/E2E/.env.backend-next.example | 15 +- README.md | 19 +- docs/index.md | 106 ++++----- opensearchConfig.example.json | 16 +- src/opensearch/dto/create-index.dto.ts | 2 +- .../interfaces/mappingInterface.type.ts | 5 - src/opensearch/opensearch.service.ts | 2 +- src/opensearch/providers/fields.enum.ts | 4 - .../providers/query-builder.service.ts | 5 - test/OpenSearch.js | 224 ++++++++++++++++-- test/config/.env | 2 +- 12 files changed, 290 insertions(+), 122 deletions(-) delete mode 100644 src/opensearch/interfaces/mappingInterface.type.ts diff --git a/.env.example b/.env.example index 373523608..47f1dfe0f 100644 --- a/.env.example +++ b/.env.example @@ -84,20 +84,16 @@ MS365_CLIENT_ID= MS365_CLIENT_SECRET= POLICY_PUBLICATION_SHIFT=3 years POLICY_RETENTION_SHIFT=-1 (indefinitely) + +# "wait_for": waits for index refresh before returning, recommended for development and testing. +# "false": returns immediately without waiting, recommended for production. +OPENSEARCH_REFRESH=<"wait_for"|"false"> OPENSEARCH_ENABLED=<"yes"|"no"> OPENSEARCH_ENABLED="https://localhost:9200" OPENSEARCH_ENABLED="admin" OPENSEARCH_ENABLED="Scicat_default_password_2026" OPENSEARCH_DEFAULT_INDEX="dataset" -# use wait_for for testing and development to ensure that the index is updated before the next operation, -# use false for production to improve performance by not waiting for the index to refresh before returning a -OPENSEARCH_REFRESH=<"wait_for"|"false"> - - -STACK_VERSION="8.8.2" -CLUSTER_NAME="es-cluster" -MEM_LIMIT="4G" FRONTEND_CONFIG_FILE="./src/config/frontend.config.json" FRONTEND_THEME_FILE="./src/config/frontend.theme.json" LOGGERS_CONFIG_FILE="loggers.json" diff --git a/CI/E2E/.env.backend-next.example b/CI/E2E/.env.backend-next.example index 2d9a8d313..652ab6cc5 100644 --- a/CI/E2E/.env.backend-next.example +++ b/CI/E2E/.env.backend-next.example @@ -39,12 +39,9 @@ DATASET_CREATION_VALIDATION_REGEX="^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][ PROPOSAL_GROUPS="proposalingestor" SAMPLE_GROUPS="" -ELASTICSEARCH_ENABLED='yes' -ES_HOST=http://es01:9200 -ES_USERNAME=elastic -ES_PASSWORD=duo-password -MONGODB_COLLECTION=Dataset -ES_MAX_RESULT=210000 -ES_FIELDS_LIMIT=400000 -ES_INDEX="dataset" -ES_REFRESH="wait_for" \ No newline at end of file +OPENSEARCH_ENABLED='yes' +OPENSEARCH_DEFAULT_INDEX="dataset" +OPENSEARCH_HOST=https://localhost:9200 +OPENSEARCH_USERNAME=admin +OPENSEARCH_PASSWORD=Scicat_default_password_2026 +OPENSEARCH_REFRESH="wait_for" \ No newline at end of file diff --git a/README.md b/README.md index 7bfc22269..681281eba 100644 --- a/README.md +++ b/README.md @@ -237,19 +237,12 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) |`MS365_CLIENT_SECRET`| string | Yes | Client Secret for sending emails over Microsoft Graph API | | |`POLICY_PUBLICATION_SHIFT`| integer | Yes | Embargo period expressed in years. | 3 years | |`POLICY_RETENTION_SHIFT`| integer | Yes | Retention period (how long the facility will hold on to data) expressed in years. | -1 (indefinitely) | -|`ELASTICSEARCH_ENABLED`| string | | Flag to enable/disable the Elasticsearch endpoints. Values "yes" or "no". | "no" | -|`ES_HOST`| string | | Host of Elasticsearch server instance. |"https://localhost:9200"| -|`ES_USERNAME`| string | Yes | Elasticsearch username. | "elastic" | -|`ES_PASSWORD`| string | | Elasticsearch password. |"duo-password"| -|`ES_PORT`| number | | Elasticsearch port. |9200| -|`MONGODB_COLLECTION`| string | | Collection name to be mapped into specified Elasticsearch index. Used for data synchronization between MongoDB and Elasticsearch index. |"Dataset"| -|`ES_MAX_RESULT`| number | Yes | Maximum records that can be indexed into Elasticsearch. | 10000 | -|`ES_FIELDS_LIMIT`| number | Yes | The total number of fields in an index. | 1000 | -|`ES_INDEX`| string | | Setting default index for the application |"dataset"| -|`ES_REFRESH`| string | | If set to`wait_for`, Elasticsearch will wait till data is inserted into the specified index before returning a response. | false | -|`STACK_VERSION` | string | Yes | Defines the Elasticsearch version to deploy | "8.8.2" | -|`CLUSTER_NAME` | string | Yes | Sets the name of the Elasticsearch cluster | "es-cluster" | -|`MEM_LIMIT`| string | Yes | Specifies the max memory for Elasticsearch container (or process) | "4G" | +|`OPENSEARCH_ENABLED`| string | | Controls whether OpenSearch is enabled on application startup. If not provided or set to `no`, OpenSearch will not be instantiated.| "no" | +|`OPENSEARCH_DEFAULT_INDEX`| string | | Specifies the default index. If not provided, a default index named `dataset` will be created automatically. | "dataset" | +|`OPENSEARCH_HOST`| string | | Host of Opensearch server instance. | | +|`OPENSEARCH_USERNAME`| string | Yes | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | "admin" | +|`OPENSEARCH_PASSWORD`| string | | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | | +|`OPENSEARCH_REFRESH`| string | | Controls index refresh behavior. `wait_for`waits for the next refresh cycle before returning, which is useful for development and testing.`false`skips waiting and is recommended for production. Defaults to false. | false | |`FRONTEND_CONFIG_FILE`| string | | The file name for frontend configuration, located in the`/src/config`directory by default. | "./src/config/frontend.config.json" | |`FRONTEND_THEME_FILE`| string | | The file name for frontend theme, located in the`/src/config`directory by default. | "./src/config/frontend.theme.json" | |`LOGGERS_CONFIG_FILE`| string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | diff --git a/docs/index.md b/docs/index.md index 6946b0615..22e9632ac 100644 --- a/docs/index.md +++ b/docs/index.md @@ -131,7 +131,7 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) | `JWT_SECRET` | string | | The secret for your JWT token, used for authorization. | | | `JWT_EXPIRES_IN` | number | Yes | How long, in seconds, the JWT token is valid. | 3600 | | `LDAP_URL` | string | Yes | The URL to your LDAP server. | | -| `LDAP_BIND_DN` | string | Yes | Bind_DN for your LDAP server. | | +| `LDAP_BIND_DN` | string | Yes | Bind*DN for your LDAP server. | | | `LDAP_BIND_CREDENTIALS` | string | Yes | Credentials for your LDAP server. | | | `LDAP_SEARCH_BASE` | string | Yes | Search base for your LDAP server. | | | `LDAP_SEARCH_FILTER` | string | Yes | Search filter for your LDAP server. | | @@ -142,59 +142,57 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) | `OIDC_SCOPE` | string | Yes | Space-separated list of info returned by the OIDC service. Example: "openid profile email". | | | `OIDC_SUCCESS_URL` | string | Yes | SciCat Frontend auth-callback URL. Required to pass user credentials to SciCat Frontend after OIDC login. Example: https://myscicatfrontend/auth-callback. Must be `frontend-base-url/auth-callback` or `frontend-base-url/login` for the official SciCat frontend. | | | `OIDC_RETURN_URL` | string | Yes | The path segment within the SciCat Frontend to redirect to, passed as query param in `OIDC_SUCCESS_URL` and handled by frontend. Example: /datasets. | | -| `OIDC_FRONTEND_CLIENTS` | string | Yes | Comma separated list of additional frontend OIDC clients for this backend. Example: scilog,maxiv. Their success and return URLs can be configured by setting `OIDC_${CLIENT}_SUCCESS_URL` (E.g. `OIDC_SCILOG_SUCCESS_URL`) and `OIDC_${CLIENT}_RETURN_URL` | | -| `OIDC_ACCESS_GROUPS` | string | Yes | Functionality is still unclear. | | -| `OIDC_ACCESS_GROUPS_PROPERTY` | string | Yes | Target field to get the access groups value from OIDC response. | | -| `OIDC_USERINFO_MAPPING_FIELD_USERNAME` | string | Yes | Comma-separated list of fields from the OIDC response to use as the user's profile username. Example: `OIDC_USERINFO_MAPPING_FIELD_USERNAME="iss, sub"`. | "preferred_username" \|\| "name" | -| `OIDC_USERINFO_MAPPING_FIELD_DISPLAYNAME` | string | Yes | Field from the OIDC response to use as the user's profile display name. Example: `OIDC_USERINFO_MAPPING_FIELD_DISPLAYNAME="preferred_username"`. | "name" | -| `OIDC_USERINFO_MAPPING_FIELD_EMAIL` | string | Yes | Field from the OIDC response to use as the user's profile email. | "email" | -| `OIDC_USERINFO_MAPPING_FIELD_FAMILYNAME` | string | Yes | Field from the OIDC response to use as the user's profile family name. | "family_name" | -| `OIDC_USERINFO_MAPPING_FIELD_ID` | string | Yes | Field from the OIDC response to use as the user's profile ID. | "sub" \|\| "user_id" | -| `OIDC_USERINFO_MAPPING_FIELD_THUMBNAILPHOTO`| string | Yes | Field from the OIDC response to use as the user's profile thumbnail photo. | "thumbnailPhoto" | -| `OIDC_USERINFO_MAPPING_FIELD_PROVIDER` | string | Yes | Field from the OIDC response to use as the user's profile provider. | "iss" | -| `OIDC_USERINFO_MAPPING_FIELD_GROUP` | string | Yes | Field from the OIDC response to use as the user's profile group. | "groups" | -| `OIDC_USERQUERY_OPERATOR` | string | Yes | Specifies the operator ("or" or "and") for UserModel.findOne queries, determining the logic used to match fields like "username" or "email". Example: `UserModel.findOne({$or: {"username":"testUser", "email":"test@test.com"}})`. | "or" | -| `OIDC_USERQUERY_FILTER` | string | Yes | Defines key-value pairs for UserModel.findOne queries, using a "key:value" format. Example: `OIDC_USERQUERY_FILTER="username:sub, email:email"`. | "username:username, email:email" | -| `LOGBOOK_ENABLED` | string | Yes | Flag to enable/disable the Logbook endpoints. Values "yes" or "no". | "no" | -| `LOGBOOK_BASE_URL` | string | Yes | The base URL to the Logbook API. Only required if Logbook is enabled. | | -| `METADATA_KEYS_RETURN_LIMIT` | number | Yes | The return limit for the `/Datasets/metadataKeys` endpoint. | | -| `METADATA_PARENT_INSTANCES_RETURN_LIMIT` | number | Yes | The return limit of Datasets to extract metadata keys from for the `/Datasets/metadataKeys` endpoint. | | -| `MONGODB_URI` | string | | The URI for your MongoDB instance. | | -| `OAI_PROVIDER_ROUTE` | string | Yes | URI to OAI provider, used for the `/publisheddata/:id/resync` endpoint. | | -| `PID_PREFIX` | string | | The facility PID prefix, with trailing slash. | | -| `PUBLIC_URL_PREFIX` | string | | The base URL to the facility Landing Page. | | -| `PORT` | number | Yes | The port on which you want to access the app. | 3000 | -| `RABBITMQ_ENABLED` | string | Yes | Flag to enable/disable RabbitMQ consumer. Values "yes" or "no". | "no" | -| `RABBITMQ_HOSTNAME` | string | Yes | The hostname of the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | -| `RABBITMQ_USERNAME` | string | Yes | The username used to authenticate to the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | -| `RABBITMQ_PASSWORD` | string | Yes | The password used to authenticate to the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | -| `REGISTER_DOI_URI` | string | | URI to the organization that registers the facility's DOIs. | | -| `REGISTER_METADATA_URI` | string | | URI to the organization that registers the facility's published data metadata. | | -| `SITE` | string | | The name of your site. | | -| `EMAIL_TYPE` | string | Yes | The type of your email provider. Options are "smtp" or "ms365". | "smtp" | -| `EMAIL_FROM` | string | Yes | Email address that emails should be sent from. | | -| `EMAIL_REPLYTO` | string | Yes | Email 'Reply-To' field. | | -| `SMTP_HOST` | string | Yes | Host of SMTP server. | | -| `SMTP_MESSAGE_FROM` | string | Yes | (Deprecated) Alternate spelling of EMAIL_FROM.| | -| `SMTP_PORT` | number | Yes | Port of SMTP server. | 587 | -| `SMTP_SECURE` | bool | Yes | Use encrypted SMTPS. | "no" | -| `MS365_TENANT_ID` | string | Yes | Tenant ID for sending emails over Microsoft Graph API. | | -| `MS365_CLIENT_ID` | string | Yes | Client ID for sending emails over Microsoft Graph API | | -| `MS365_CLIENT_SECRET` | string | Yes | Client Secret for sending emails over Microsoft Graph API | | -| `POLICY_PUBLICATION_SHIFT` | integer | Yes | Embargo period expressed in years. | 3 years | -| `POLICY_RETENTION_SHIFT` | integer | Yes | Retention period (how long the facility will hold on to data) expressed in years. | -1 (indefinitely) | -| `ELASTICSEARCH_ENABLED` | string | | Flag to enable/disable the Elasticsearch endpoints. Values "yes" or "no". | "no" | -| `ES_HOST` | string | | Host of Elasticsearch server instance. | | -| `ES_USERNAME` | string | Yes | Elasticsearch username. | "elastic" | -| `ES_PASSWORD` | string | | Elasticsearch password. | | -| `MONGODB_COLLECTION` | string | | Collection name to be mapped into specified Elasticsearch index. Used for data synchronization between MongoDB and Elasticsearch index. | | -| `ES_MAX_RESULT` | number | | Maximum records that can be indexed into Elasticsearch. | 10000 | -| `ES_FIELDS_LIMIT` | number | | The total number of fields in an index. | 1000 | -| `ES_REFRESH` | string | | If set to `wait_for`, Elasticsearch will wait till data is inserted into the specified index before returning a response. | false | -| `LOGGERS_CONFIG_FILE` | string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | -| `PROPOSAL_TYPES_FILE` | string | | The file name for proposal types configuration, located in the project root directory. | "proposalTypes.json" | -| `SWAGGER_PATH` | string | Yes | swaggerPath is the path where the swagger UI will be available. | "explorer"| -| `MAX_FILE_UPLOAD_SIZE` | string | Yes | Maximum allowed file upload size. | "16mb"| +| `OIDC_FRONTEND_CLIENTS` | string | Yes | Comma separated list of additional frontend OIDC clients for this backend. Example: scilog,maxiv. Their success and return URLs can be configured by setting `OIDC*${CLIENT}_SUCCESS_URL` (E.g. `OIDC_SCILOG_SUCCESS_URL`) and `OIDC_${CLIENT}\_RETURN_URL`| | +|`OIDC_ACCESS_GROUPS`| string | Yes | Functionality is still unclear. | | +|`OIDC_ACCESS_GROUPS_PROPERTY`| string | Yes | Target field to get the access groups value from OIDC response. | | +|`OIDC_USERINFO_MAPPING_FIELD_USERNAME`| string | Yes | Comma-separated list of fields from the OIDC response to use as the user's profile username. Example:`OIDC_USERINFO_MAPPING_FIELD_USERNAME="iss, sub"`. | "preferred_username" \|\| "name" | +| `OIDC_USERINFO_MAPPING_FIELD_DISPLAYNAME`| string | Yes | Field from the OIDC response to use as the user's profile display name. Example:`OIDC_USERINFO_MAPPING_FIELD_DISPLAYNAME="preferred_username"`. | "name" | +| `OIDC_USERINFO_MAPPING_FIELD_EMAIL`| string | Yes | Field from the OIDC response to use as the user's profile email. | "email" | +|`OIDC_USERINFO_MAPPING_FIELD_FAMILYNAME`| string | Yes | Field from the OIDC response to use as the user's profile family name. | "family_name" | +|`OIDC_USERINFO_MAPPING_FIELD_ID`| string | Yes | Field from the OIDC response to use as the user's profile ID. | "sub" \|\| "user_id" | +|`OIDC_USERINFO_MAPPING_FIELD_THUMBNAILPHOTO`| string | Yes | Field from the OIDC response to use as the user's profile thumbnail photo. | "thumbnailPhoto" | +| `OIDC_USERINFO_MAPPING_FIELD_PROVIDER`| string | Yes | Field from the OIDC response to use as the user's profile provider. | "iss" | +|`OIDC_USERINFO_MAPPING_FIELD_GROUP`| string | Yes | Field from the OIDC response to use as the user's profile group. | "groups" | +|`OIDC_USERQUERY_OPERATOR`| string | Yes | Specifies the operator ("or" or "and") for UserModel.findOne queries, determining the logic used to match fields like "username" or "email". Example:`UserModel.findOne({$or: {"username":"testUser", "email":"test@test.com"}})`. | "or" | +| `OIDC_USERQUERY_FILTER`| string | Yes | Defines key-value pairs for UserModel.findOne queries, using a "key:value" format. Example:`OIDC_USERQUERY_FILTER="username:sub, email:email"`. | "username:username, email:email" | +| `LOGBOOK_ENABLED`| string | Yes | Flag to enable/disable the Logbook endpoints. Values "yes" or "no". | "no" | +|`LOGBOOK_BASE_URL`| string | Yes | The base URL to the Logbook API. Only required if Logbook is enabled. | | +|`METADATA_KEYS_RETURN_LIMIT`| number | Yes | The return limit for the`/Datasets/metadataKeys`endpoint. | | +|`METADATA_PARENT_INSTANCES_RETURN_LIMIT`| number | Yes | The return limit of Datasets to extract metadata keys from for the`/Datasets/metadataKeys`endpoint. | | +|`MONGODB_URI`| string | | The URI for your MongoDB instance. | | +|`OAI_PROVIDER_ROUTE`| string | Yes | URI to OAI provider, used for the`/publisheddata/:id/resync`endpoint. | | +|`PID_PREFIX`| string | | The facility PID prefix, with trailing slash. | | +|`PUBLIC_URL_PREFIX`| string | | The base URL to the facility Landing Page. | | +|`PORT`| number | Yes | The port on which you want to access the app. | 3000 | +|`RABBITMQ_ENABLED`| string | Yes | Flag to enable/disable RabbitMQ consumer. Values "yes" or "no". | "no" | +|`RABBITMQ_HOSTNAME`| string | Yes | The hostname of the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | +|`RABBITMQ_USERNAME`| string | Yes | The username used to authenticate to the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | +|`RABBITMQ_PASSWORD`| string | Yes | The password used to authenticate to the RabbitMQ message broker. Only required if RabbitMQ is enabled. | | +|`REGISTER_DOI_URI`| string | | URI to the organization that registers the facility's DOIs. | | +|`REGISTER_METADATA_URI`| string | | URI to the organization that registers the facility's published data metadata. | | +|`SITE`| string | | The name of your site. | | +|`EMAIL_TYPE`| string | Yes | The type of your email provider. Options are "smtp" or "ms365". | "smtp" | +|`EMAIL_FROM`| string | Yes | Email address that emails should be sent from. | | +|`EMAIL_REPLYTO`| string | Yes | Email 'Reply-To' field. | | +|`SMTP_HOST`| string | Yes | Host of SMTP server. | | +|`SMTP_MESSAGE_FROM`| string | Yes | (Deprecated) Alternate spelling of EMAIL_FROM.| | +|`SMTP_PORT`| number | Yes | Port of SMTP server. | 587 | +|`SMTP_SECURE`| bool | Yes | Use encrypted SMTPS. | "no" | +|`MS365_TENANT_ID`| string | Yes | Tenant ID for sending emails over Microsoft Graph API. | | +|`MS365_CLIENT_ID`| string | Yes | Client ID for sending emails over Microsoft Graph API | | +|`MS365_CLIENT_SECRET`| string | Yes | Client Secret for sending emails over Microsoft Graph API | | +|`POLICY_PUBLICATION_SHIFT`| integer | Yes | Embargo period expressed in years. | 3 years | +|`POLICY_RETENTION_SHIFT`| integer | Yes | Retention period (how long the facility will hold on to data) expressed in years. | -1 (indefinitely) | +|`OPENSEARCH_ENABLED`| string | | Controls whether OpenSearch is enabled on application startup. If not provided or set to `no`, OpenSearch will not be instantiated.| "no" | +|`OPENSEARCH_DEFAULT_INDEX`| string | | Specifies the default index. If not provided, a default index named `dataset` will be created automatically. | "dataset" | +|`OPENSEARCH_HOST`| string | | Host of Opensearch server instance. | | +|`OPENSEARCH_USERNAME`| string | Yes | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | "admin" | +|`OPENSEARCH_PASSWORD`| string | | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | | +|`OPENSEARCH_REFRESH`| string | | Controls index refresh behavior. `wait_for`waits for the next refresh cycle before returning, which is useful for development and testing.`false`skips waiting and is recommended for production. Defaults to false. | false | +|`LOGGERS_CONFIG_FILE`| string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | +|`PROPOSAL_TYPES_FILE`| string | | The file name for proposal types configuration, located in the project root directory. | "proposalTypes.json" | +|`SWAGGER_PATH`| string | Yes | swaggerPath is the path where the swagger UI will be available. | "explorer"| +|`MAX_FILE_UPLOAD_SIZE` | string | Yes | Maximum allowed file upload size. | "16mb"| ## Migrating from the old SciCat Backend diff --git a/opensearchConfig.example.json b/opensearchConfig.example.json index ad9c3d0da..5e851410a 100644 --- a/opensearchConfig.example.json +++ b/opensearchConfig.example.json @@ -9,20 +9,30 @@ "autocomplete": { "type": "custom", "tokenizer": "autocomplete", - "filter": ["lowercase"] + "filter": ["word_delimiter", "lowercase"] }, "autocomplete_search": { "type": "custom", - "tokenizer": "lowercase" + "tokenizer": "keyword", + "filter": ["lowercase"] } }, "tokenizer": { "autocomplete": { "type": "edge_ngram", "min_gram": 2, - "max_gram": 40, + "max_gram": 150, "token_chars": ["letter", "digit", "symbol", "punctuation"] } + }, + "filter": { + "word_delimiter": { + "type": "word_delimiter_graph", + "split_on_case_change": false, + "split_on_numerics": false, + "preserve_original": true, + "type_table": [". => ALPHA"] + } } } }, diff --git a/src/opensearch/dto/create-index.dto.ts b/src/opensearch/dto/create-index.dto.ts index bca218c3c..2812b90c5 100644 --- a/src/opensearch/dto/create-index.dto.ts +++ b/src/opensearch/dto/create-index.dto.ts @@ -16,5 +16,5 @@ export class CreateIndexDto extends UpdateIndexDto { }) @IsObject() @IsOptional() - mappings: Partial; + mappings: TypeMapping; } diff --git a/src/opensearch/interfaces/mappingInterface.type.ts b/src/opensearch/interfaces/mappingInterface.type.ts deleted file mode 100644 index b05a89e7e..000000000 --- a/src/opensearch/interfaces/mappingInterface.type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type MappingProperty = Record; - -export interface MappingObject { - [key: string]: MappingProperty; -} diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 8807e581d..91fe6f097 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -276,7 +276,7 @@ export class OpensearchService implements OnModuleInit { query: searchQuery.query, from: skip, size: limit, - min_score: 1, + min_score: 0.1, track_total_hits: true, _source: [""], }; diff --git a/src/opensearch/providers/fields.enum.ts b/src/opensearch/providers/fields.enum.ts index 637db68a6..027a3d454 100644 --- a/src/opensearch/providers/fields.enum.ts +++ b/src/opensearch/providers/fields.enum.ts @@ -2,7 +2,3 @@ export enum MustFields { DatasetName = "datasetName", Description = "description", } - -export enum ShouldFields { - UserGroups = "userGroups", -} diff --git a/src/opensearch/providers/query-builder.service.ts b/src/opensearch/providers/query-builder.service.ts index 82b8f4199..becfcde79 100644 --- a/src/opensearch/providers/query-builder.service.ts +++ b/src/opensearch/providers/query-builder.service.ts @@ -14,10 +14,6 @@ export class SearchQueryService { const accessFilter = this.buildFilterFields(filter); const textQuery = this.buildTextQuery(filter); - // NOTE: The final query flow is as follows: - // step 1. Build filter fields conditions must match all filter fields - // step 2. Build should fields conditions must match at least one should field - // step 3. Build text query conditions must match all text query fields return this.constructFinalQuery(accessFilter, textQuery); } catch (err) { Logger.error("Open search build search query failed", err); @@ -51,7 +47,6 @@ export class SearchQueryService { private buildTextQuery(filter: ISearchFilter): QueryContainer[] { let wildcardQueries: QueryContainer[] = []; - //NOTE: if text field is present, we query both datasetName and description fields if (filter.text) { wildcardQueries = this.buildWildcardQueries(filter.text); } diff --git a/test/OpenSearch.js b/test/OpenSearch.js index b34050834..d51d5d547 100644 --- a/test/OpenSearch.js +++ b/test/OpenSearch.js @@ -6,12 +6,42 @@ require("dotenv").config(); let accessTokenAdminIngestor = null, accessTokenArchiveManager = null, - pid = null; + pid1 = null, + pid2 = null; -const isESenabled = process.env.ELASTICSEARCH_ENABLED == "yes"; +const commonName = "common"; -(isESenabled ? describe : describe.skip)( - "ElastiSearch: CRUD, filtering and search test case", +const datasetName1 = `XRD_Si02_Beam3_2024A_thetaScan_dSpacing3.14_run07 ${commonName}`; +const datasetName2 = `Neutron_CeO2_400K_P12bar_timeOfFlight_lambda1.8_seq42 ${commonName}`; + +const datasetName1Tokens = [ + "XRD", + "Si02", + "Beam3", + "2024A", + "thetaScan", + "dSpacing3.14", + "run07", +]; +const datasetName2Tokens = [ + "Neutron", + "CeO2", + "400K", + "P12bar", + "timeOfFlight", + "lambda1.8", + "seq42", +]; + +const randomToken1 = + datasetName1Tokens[Math.floor(Math.random() * datasetName1Tokens.length)]; +const randomToken2 = + datasetName2Tokens[Math.floor(Math.random() * datasetName2Tokens.length)]; + +const isOSenabled = process.env.OPENSEARCH_ENABLED == "yes"; + +(isOSenabled ? describe : describe.skip)( + "Opensearch: CRUD, filtering and search test case", () => { before(async () => { db.collection("Dataset").deleteMany({}); @@ -27,31 +57,189 @@ const isESenabled = process.env.ELASTICSEARCH_ENABLED == "yes"; }); }); - it("0010: adds a new raw dataset", async () => { + it("0010: adds a new raw dataset -1 ", async () => { + const dataset1 = { + ...TestData.ScientificMetadataForOpensearch, + datasetName: datasetName1, + isPublished: true, + }; + + return request(appUrl) + .post("/api/v3/Datasets") + .send(dataset1) + .set("Accept", "application/json") + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(TestData.EntryCreatedStatusCode) + .expect("Content-Type", /json/) + .then((res) => { + pid1 = encodeURIComponent(res.body["pid"]); + }); + }); + + it("0011: adds a new raw dataset -2 ", async () => { + const dataset2 = { + ...TestData.ScientificMetadataForOpensearch, + datasetName: datasetName2, + isPublished: true, + }; + return request(appUrl) .post("/api/v3/Datasets") - .send(TestData.ScientificMetadataForElasticSearch) + .send(dataset2) .set("Accept", "application/json") .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) .expect(TestData.EntryCreatedStatusCode) .expect("Content-Type", /json/) .then((res) => { - res.body.should.have - .property("scientificMetadata") - .which.is.an("object") - .that.has.all.keys( - scientificMetadataFieldName.keyValue, - scientificMetadataFieldName.unitAndValue, - scientificMetadataFieldName.number, - scientificMetadataFieldName.string, - ); - pid = encodeURIComponent(res.body["pid"]); + pid2 = encodeURIComponent(res.body["pid"]); + }); + }); + + it("0020: finds the dataset1 by partial text search", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: randomToken1 }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .expect(200) + .then((res) => { + const found = res.body.some((d) => d.datasetName === datasetName1); + found.should.equal(true); + }); + }); + + it("0021: finds the dataset2 by partial text search", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: randomToken2 }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .expect(200) + .then((res) => { + const found = res.body.some((d) => d.datasetName === datasetName2); + found.should.equal(true); + }); + }); + + it("0022: finds the dataset1 by full text search", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: datasetName1 }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .expect(200) + .then((res) => { + const found = res.body.some((d) => d.datasetName === datasetName2); + found.should.equal(true); }); }); - it("0034: should delete this raw dataset", async () => { + it("0023: finds the dataset2 by full text search", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: datasetName2 }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .expect(200) + .then((res) => { + const found = res.body.some((d) => d.datasetName === datasetName2); + found.should.equal(true); + }); + }); + + it("0024: finds both datasets by shared common text", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: commonName }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(200) + .then((res) => { + const foundDataset1 = res.body.some( + (d) => d.datasetName === datasetName1, + ); + const foundDataset2 = res.body.some( + (d) => d.datasetName === datasetName2, + ); + foundDataset1.should.equal(true); + foundDataset2.should.equal(true); + }); + }); + + it("0025: should finds both datasets by shared common text", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: commonName }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(200) + .then((res) => { + const foundDataset1 = res.body.some( + (d) => d.datasetName === datasetName1, + ); + const foundDataset2 = res.body.some( + (d) => d.datasetName === datasetName2, + ); + foundDataset1.should.equal(true); + foundDataset2.should.equal(true); + }); + }); + + it("0026: returns no datasets for irrelevant search text", async () => { + return request(appUrl) + .get("/api/v3/datasets/fullquery") + .query({ + fields: JSON.stringify({ text: "shouldnotmatchanything" }), + limits: JSON.stringify({ + skip: 0, + limit: 10, + }), + }) + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(200) + .then((res) => { + res.body.length.should.equal(0); + }); + }); + + it("0034: should delete dataset1", async () => { + return request(appUrl) + .delete("/api/v3/datasets/" + pid1) + .set("Accept", "application/json") + .set({ Authorization: `Bearer ${accessTokenArchiveManager}` }) + .expect(TestData.SuccessfulDeleteStatusCode) + .expect("Content-Type", /json/); + }); + + it("0035: should delete dataset2", async () => { return request(appUrl) - .delete("/api/v3/datasets/" + pid) + .delete("/api/v3/datasets/" + pid2) .set("Accept", "application/json") .set({ Authorization: `Bearer ${accessTokenArchiveManager}` }) .expect(TestData.SuccessfulDeleteStatusCode) diff --git a/test/config/.env b/test/config/.env index 7e48108b9..30f3398b6 100644 --- a/test/config/.env +++ b/test/config/.env @@ -31,7 +31,7 @@ REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" OPENSEARCH_HOST=https://localhost:9200 OPENSEARCH_PASSWORD=Scicat_default_password_2026 -OPENSEARCH_ENABLED=no +OPENSEARCH_ENABLED=yes #history TRACKABLE_STRATEGY=delta From c0ef935e7d721747052b545d04a38a2ce20e225f Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 14:59:12 +0100 Subject: [PATCH 28/46] centralize os query condition in one place --- src/datasets/datasets.controller.ts | 33 ++++++----------------------- src/datasets/datasets.service.ts | 12 +++++------ 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index dff10094e..f5a0efa0c 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -990,22 +990,12 @@ export class DatasetsController { } } - let datasets: DatasetDocument[] | null; + const isAdmin = canViewAny; - const osEnabled = - this.configService.get("opensearch.enabled") || "no"; - - const textSearch = parsedFilters.fields?.text; - - if (osEnabled === "yes" && textSearch) { - const isAdmin = canViewAny; - datasets = await this.datasetsService.opensearchQuery( - parsedFilters, - isAdmin, - ); - } else { - datasets = await this.datasetsService.fullquery(parsedFilters); - } + const datasets = await this.datasetsService.opensearchQuery( + parsedFilters, + isAdmin, + ); let outputDatasets: OutputDatasetObsoleteDto[] = []; @@ -1080,17 +1070,8 @@ export class DatasetsController { fields: fields, facets: JSON.parse(filters.facets ?? "[]"), }; - - const osEnabled = - this.configService.get("opensearch.enabled") || "no"; - const textSearch = parsedFilters.fields?.text; - - if (osEnabled === "yes" && textSearch) { - const isAdmin = canViewAny; - return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); - } - - return this.datasetsService.fullFacet(parsedFilters); + const isAdmin = canViewAny; + return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); } // GET /datasets/metadataKeys diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index f0a526c32..8898d825c 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -306,9 +306,9 @@ export class DatasetsService { filter: IFilters, isAdmin: boolean, ): Promise { - const defaultOsIndex = - this.configService.get("opensearch.defaultIndex") || "dataset"; if ( + this.configService.get("opensearch.enabled") !== "yes" || + !filter.fields?.text || !this.opensearchService.connected() || !(await this.opensearchService.isPopulated()) ) { @@ -330,7 +330,7 @@ export class DatasetsService { const osResult = await this.opensearchService.search( { text, userGroups, isAdmin: isAdmin }, - defaultOsIndex, + this.configService.get("opensearch.defaultIndex") || "dataset", modifiers.limit, modifiers.skip, ); @@ -363,8 +363,6 @@ export class DatasetsService { filters: IFacets, isAdmin: boolean, ): Promise[]> { - const defaultOsIndex = - this.configService.get("opensearch.defaultIndex") || "dataset"; const osConfig = this.configService.get<{ settings: IndexSettings; @@ -375,6 +373,8 @@ export class DatasetsService { ); if ( + this.configService.get("opensearch.enabled") !== "yes" || + !filters.fields?.text || !this.opensearchService.connected() || !(await this.opensearchService.isPopulated()) ) { @@ -389,7 +389,7 @@ export class DatasetsService { userGroups: fields.userGroups, isAdmin: isAdmin, }, - defaultOsIndex, + this.configService.get("opensearch.defaultIndex") || "dataset", osMaxResultWindow, ); From 5e12c27ff2a7e0399cd1b35284768cc9af45e6ce Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 15:14:22 +0100 Subject: [PATCH 29/46] test1 --- .github/workflows/test.yml | 5 ++++- test/config/.env | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fe3cd63a..7f08ec895 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -131,9 +131,12 @@ jobs: - name: API tests env: COMPOSE_PROFILES: ${{ matrix.opensearch_enabled == 'yes' && 'opensearch' || ''}} + OPENSEARCH_ENABLED: ${{ matrix.opensearch_enabled }} # Start mongo container and app before running api tests run: | cp opensearchConfig.example.json opensearchConfig.json cp CI/ESS/docker-compose.api.yaml docker-compose.yaml + echo "COMPOSE_PROFILES=$COMPOSE_PROFILES" + docker compose config docker compose up --build -d - OPENSEARCH_ENABLED=${{ matrix.opensearch_enabled }} npm run test:api + npm run test:api diff --git a/test/config/.env b/test/config/.env index 30f3398b6..bc105a6dd 100644 --- a/test/config/.env +++ b/test/config/.env @@ -31,7 +31,6 @@ REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" OPENSEARCH_HOST=https://localhost:9200 OPENSEARCH_PASSWORD=Scicat_default_password_2026 -OPENSEARCH_ENABLED=yes #history TRACKABLE_STRATEGY=delta From 2d703c59fe446f6a0264424c98d3919a35fba131 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 15:24:41 +0100 Subject: [PATCH 30/46] test --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f08ec895..2aca9946b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,7 +136,5 @@ jobs: run: | cp opensearchConfig.example.json opensearchConfig.json cp CI/ESS/docker-compose.api.yaml docker-compose.yaml - echo "COMPOSE_PROFILES=$COMPOSE_PROFILES" - docker compose config docker compose up --build -d - npm run test:api + OPENSEARCH_ENABLED=${{ matrix.opensearch_enabled }} npm run test:api From a1b3f61e5fa6de908878dc546bef1a4396417f34 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 15:31:18 +0100 Subject: [PATCH 31/46] change dataset name in TestData.js used for api test --- test/DatasetV4.js | 4 ++-- test/OpenSearch.js | 4 ++-- test/TestData.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/DatasetV4.js b/test/DatasetV4.js index e8e5d71da..6755a54cb 100644 --- a/test/DatasetV4.js +++ b/test/DatasetV4.js @@ -314,7 +314,7 @@ describe("2500: Datasets v4 tests", () => { it("0126: adds a new dataset with scientificMetadata", async () => { return request(appUrl) .post("/api/v4/datasets") - .send(TestData.ScientificMetadataForOpensearchV4) + .send(TestData.DatasetWithScientificMetadataV4) .auth(accessTokenAdminIngestor, { type: "bearer" }) .expect(TestData.EntryCreatedStatusCode) .expect("Content-Type", /json/) @@ -331,7 +331,7 @@ describe("2500: Datasets v4 tests", () => { it("0127: should be able to add a new dataset with non-empty datasetLifecycle", async () => { const newDataset = { - ...TestData.ScientificMetadataForOpensearchV4, + ...TestData.DatasetWithScientificMetadataV4, datasetlifecycle: { archivable: false, retrievable: true, diff --git a/test/OpenSearch.js b/test/OpenSearch.js index d51d5d547..d8e645dad 100644 --- a/test/OpenSearch.js +++ b/test/OpenSearch.js @@ -59,7 +59,7 @@ const isOSenabled = process.env.OPENSEARCH_ENABLED == "yes"; it("0010: adds a new raw dataset -1 ", async () => { const dataset1 = { - ...TestData.ScientificMetadataForOpensearch, + ...TestData.DatasetWithScientificMetadata, datasetName: datasetName1, isPublished: true, }; @@ -78,7 +78,7 @@ const isOSenabled = process.env.OPENSEARCH_ENABLED == "yes"; it("0011: adds a new raw dataset -2 ", async () => { const dataset2 = { - ...TestData.ScientificMetadataForOpensearch, + ...TestData.DatasetWithScientificMetadata, datasetName: datasetName2, isPublished: true, }; diff --git a/test/TestData.js b/test/TestData.js index f9b5e5096..aa6a59de9 100644 --- a/test/TestData.js +++ b/test/TestData.js @@ -1417,7 +1417,7 @@ const TestData = { dataQualityMetrics: "test", }, - ScientificMetadataForOpensearch: { + DatasetWithScientificMetadata: { ownerGroup: faker.company.name(), creationLocation: faker.location.city(), principalInvestigator: faker.internet.username(), @@ -1448,7 +1448,7 @@ const TestData = { }, }, - ScientificMetadataForOpensearchV4: { + DatasetWithScientificMetadataV4: { ownerGroup: faker.company.name(), creationLocation: faker.location.city(), type: "raw", From 504a7ab8d962f4b285ac1291f5322d293d5fa503 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 15:40:08 +0100 Subject: [PATCH 32/46] add OPENSEARCH_REFRESH="wait_for" for API test --- test/config/.env | 1 + 1 file changed, 1 insertion(+) diff --git a/test/config/.env b/test/config/.env index bc105a6dd..e7b09a5ca 100644 --- a/test/config/.env +++ b/test/config/.env @@ -31,6 +31,7 @@ REGISTER_DOI_URI_V3="https://mds.test.datacite.org/doi" OPENSEARCH_HOST=https://localhost:9200 OPENSEARCH_PASSWORD=Scicat_default_password_2026 +OPENSEARCH_REFRESH="wait_for" #history TRACKABLE_STRATEGY=delta From 81afa3396b075afc46008161b3df8d18ae1fdef7 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 15:54:56 +0100 Subject: [PATCH 33/46] avoid multi os config loading --- src/datasets/datasets.service.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 8898d825c..401806d5f 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -74,6 +74,9 @@ import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_com @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { + private readonly osDefaultIndex: string; + private readonly isOsEnabled: boolean; + constructor( private configService: ConfigService, @InjectModel(DatasetClass.name) @@ -84,7 +87,12 @@ export class DatasetsService { @Optional() private opensearchService: OpensearchService, private metadataKeysService: MetadataKeysService, private proposalService: ProposalsService, - ) {} + ) { + this.osDefaultIndex = + this.configService.get("opensearch.defaultIndex") || "dataset"; + this.isOsEnabled = + this.configService.get("opensearch.enabled") === "yes" || false; + } private createMetadataKeysInstance( doc: UpdateQuery, @@ -307,7 +315,7 @@ export class DatasetsService { isAdmin: boolean, ): Promise { if ( - this.configService.get("opensearch.enabled") !== "yes" || + !this.isOsEnabled || !filter.fields?.text || !this.opensearchService.connected() || !(await this.opensearchService.isPopulated()) @@ -330,7 +338,7 @@ export class DatasetsService { const osResult = await this.opensearchService.search( { text, userGroups, isAdmin: isAdmin }, - this.configService.get("opensearch.defaultIndex") || "dataset", + this.osDefaultIndex, modifiers.limit, modifiers.skip, ); @@ -373,7 +381,7 @@ export class DatasetsService { ); if ( - this.configService.get("opensearch.enabled") !== "yes" || + !this.isOsEnabled || !filters.fields?.text || !this.opensearchService.connected() || !(await this.opensearchService.isPopulated()) @@ -389,7 +397,7 @@ export class DatasetsService { userGroups: fields.userGroups, isAdmin: isAdmin, }, - this.configService.get("opensearch.defaultIndex") || "dataset", + this.osDefaultIndex, osMaxResultWindow, ); From f7ed6fda6c9ef1e8ebdfb944da520dea28ca1a21 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 18 Mar 2026 16:25:39 +0100 Subject: [PATCH 34/46] run os connection retry on background --- src/opensearch/opensearch.service.ts | 61 +++++++++++++++------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 91fe6f097..8528b480f 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -72,23 +72,8 @@ export class OpensearchService implements OnModuleInit { } } - async onModuleInit() { - try { - await this.retryConnection(3, 3000); - const isIndexExists = await this.isIndexExists(this.defaultIndex); - - if (!isIndexExists) { - await this.createIndex({ - index: this.defaultIndex, - settings: this.osConfigs?.settings || {}, - mappings: this.osConfigs?.mappings || {}, - }); - Logger.log(`New index ${this.defaultIndex}is created `, "Opensearch"); - } - Logger.log("Opensearch Connected", "Opensearch"); - } catch (error) { - Logger.error(error, "onModuleInit failed-> OpensearchService"); - } + onModuleInit() { + this.initWithRetry(); } private async connect() { @@ -107,25 +92,45 @@ export class OpensearchService implements OnModuleInit { this.osClient = connection; } - private async retryConnection(maxRetries: number, interval: number) { + private async initWithRetry( + maxRetries = 10, + initialDelayMs = 5000, + maxDelayMs = 60000, + ) { + let delayMs = initialDelayMs; let retryCount = 0; - while (maxRetries > retryCount) { - await sleep(interval); + + while (retryCount < maxRetries) { try { await this.connect(); - break; + + const isIndexExists = await this.isIndexExists(this.defaultIndex); + if (!isIndexExists) { + await this.createIndex({ + index: this.defaultIndex, + settings: this.osConfigs?.settings || {}, + mappings: this.osConfigs?.mappings || {}, + }); + Logger.log(`New index ${this.defaultIndex} is created`, "Opensearch"); + } + + Logger.log("Opensearch Connected", "Opensearch"); + return; } catch (error) { - Logger.error(`Retry attempt ${retryCount + 1} failed:`, error); retryCount++; + Logger.warn( + `Opensearch connection failed (attempt ${retryCount}/${maxRetries}), retrying in ${delayMs / 1000}s...`, + error, + ); + await sleep(delayMs); + delayMs = Math.min(delayMs * 2, maxDelayMs); } } - if (retryCount === maxRetries) { - throw new HttpException( - "Max retries reached; check Opensearch config.", - HttpStatus.BAD_REQUEST, - ); - } + Logger.error( + `Opensearch failed to connect after ${maxRetries} attempts, running without it`, + "Opensearch", + ); } connected() { From e0421ce90250a5f68ab395013b1452e83b0ecee0 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 10:46:16 +0100 Subject: [PATCH 35/46] remove isAdmin --- src/datasets/datasets.controller.ts | 10 ++-------- src/datasets/datasets.service.ts | 8 +++----- src/opensearch/interfaces/os-common.type.ts | 2 +- src/opensearch/providers/query-builder.service.ts | 3 ++- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index f5a0efa0c..3254364d0 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -990,12 +990,7 @@ export class DatasetsController { } } - const isAdmin = canViewAny; - - const datasets = await this.datasetsService.opensearchQuery( - parsedFilters, - isAdmin, - ); + const datasets = await this.datasetsService.opensearchQuery(parsedFilters); let outputDatasets: OutputDatasetObsoleteDto[] = []; @@ -1070,8 +1065,7 @@ export class DatasetsController { fields: fields, facets: JSON.parse(filters.facets ?? "[]"), }; - const isAdmin = canViewAny; - return this.datasetsService.opensearchFacet(parsedFilters, isAdmin); + return this.datasetsService.opensearchFacet(parsedFilters); } // GET /datasets/metadataKeys diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 401806d5f..0d20bfccd 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -312,7 +312,6 @@ export class DatasetsService { async opensearchQuery( filter: IFilters, - isAdmin: boolean, ): Promise { if ( !this.isOsEnabled || @@ -323,7 +322,7 @@ export class DatasetsService { return this.fullquery(filter); } - const { text, userGroups } = filter.fields || {}; + const { text, isPublished, userGroups } = filter.fields || {}; const mongoQuery: FilterQuery = createFullqueryFilter( @@ -337,7 +336,7 @@ export class DatasetsService { delete mongoQuery.$text; const osResult = await this.opensearchService.search( - { text, userGroups, isAdmin: isAdmin }, + { text, userGroups, isPublished }, this.osDefaultIndex, modifiers.limit, modifiers.skip, @@ -369,7 +368,6 @@ export class DatasetsService { async opensearchFacet( filters: IFacets, - isAdmin: boolean, ): Promise[]> { const osConfig = this.configService.get<{ @@ -395,7 +393,7 @@ export class DatasetsService { { text: fields.text, userGroups: fields.userGroups, - isAdmin: isAdmin, + isPublished: fields.isPublished, }, this.osDefaultIndex, osMaxResultWindow, diff --git a/src/opensearch/interfaces/os-common.type.ts b/src/opensearch/interfaces/os-common.type.ts index dd118820b..2420e3d77 100644 --- a/src/opensearch/interfaces/os-common.type.ts +++ b/src/opensearch/interfaces/os-common.type.ts @@ -1,5 +1,5 @@ export interface ISearchFilter { - isAdmin?: boolean; userGroups?: string[]; + isPublished?: boolean; text?: string; } diff --git a/src/opensearch/providers/query-builder.service.ts b/src/opensearch/providers/query-builder.service.ts index becfcde79..7e5455d5a 100644 --- a/src/opensearch/providers/query-builder.service.ts +++ b/src/opensearch/providers/query-builder.service.ts @@ -33,7 +33,8 @@ export class SearchQueryService { minimum_should_match: 1, }, }); - } else if (!fields.isAdmin) { + } + if (fields.isPublished) { filter.push({ term: { isPublished: true, From 63f6f058fa43557f247d318ab9e1e4a2321e2624 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 10:59:27 +0100 Subject: [PATCH 36/46] fix os api test --- .github/workflows/test.yml | 2 +- CI/ESS/docker-compose.api.yaml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2aca9946b..f32ede04c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,5 +136,5 @@ jobs: run: | cp opensearchConfig.example.json opensearchConfig.json cp CI/ESS/docker-compose.api.yaml docker-compose.yaml - docker compose up --build -d + docker compose up --build -d --wait OPENSEARCH_ENABLED=${{ matrix.opensearch_enabled }} npm run test:api diff --git a/CI/ESS/docker-compose.api.yaml b/CI/ESS/docker-compose.api.yaml index b7236fea3..005d3d18f 100644 --- a/CI/ESS/docker-compose.api.yaml +++ b/CI/ESS/docker-compose.api.yaml @@ -25,6 +25,12 @@ services: memlock: soft: -1 hard: -1 + healthcheck: + test: ["CMD-SHELL", "curl -sk -u admin:Scicat_default_password_2026 https://localhost:9200/_cluster/health || exit 1"] + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s volumes: mongodb_data: driver: local From a27ee4e19da10758a600be25224a25c24e248ea6 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 12:06:57 +0100 Subject: [PATCH 37/46] add bulk sync --- .env.example | 1 + README.md | 1 + docs/developer-guide/opensearch-guidelines.md | 17 ++--- src/config/configuration.ts | 4 ++ src/datasets/datasets.service.ts | 65 +++++++++++++++++-- 5 files changed, 73 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index 47f1dfe0f..5548c34a8 100644 --- a/.env.example +++ b/.env.example @@ -93,6 +93,7 @@ OPENSEARCH_ENABLED="https://localhost:9200" OPENSEARCH_ENABLED="admin" OPENSEARCH_ENABLED="Scicat_default_password_2026" OPENSEARCH_DEFAULT_INDEX="dataset" +OPENSEARCH_DATA_SYNC_BATCH_SIZE=50000 FRONTEND_CONFIG_FILE="./src/config/frontend.config.json" FRONTEND_THEME_FILE="./src/config/frontend.theme.json" diff --git a/README.md b/README.md index 681281eba..09409efb3 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,7 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) |`OPENSEARCH_USERNAME`| string | Yes | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | "admin" | |`OPENSEARCH_PASSWORD`| string | | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | | |`OPENSEARCH_REFRESH`| string | | Controls index refresh behavior. `wait_for`waits for the next refresh cycle before returning, which is useful for development and testing.`false`skips waiting and is recommended for production. Defaults to false. | false | +|`OPENSEARCH_DATA_SYNC_BATCH_SIZE`| number | | Number of documents fetched from MongoDB per batch during OpenSearch data sync. | 10000 | |`FRONTEND_CONFIG_FILE`| string | | The file name for frontend configuration, located in the`/src/config`directory by default. | "./src/config/frontend.config.json" | |`FRONTEND_THEME_FILE`| string | | The file name for frontend theme, located in the`/src/config`directory by default. | "./src/config/frontend.theme.json" | |`LOGGERS_CONFIG_FILE`| string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | diff --git a/docs/developer-guide/opensearch-guidelines.md b/docs/developer-guide/opensearch-guidelines.md index 3bf592215..ea34dc6d2 100644 --- a/docs/developer-guide/opensearch-guidelines.md +++ b/docs/developer-guide/opensearch-guidelines.md @@ -60,14 +60,15 @@ To start the application with OpenSearch: OpenSearch behavior is controlled using environment variables. To enable OpenSearch, set `OPENSEARCH_ENABLED=yes` and provide values for all required environment variables: `OPENSEARCH_HOST`, `OPENSEARCH_USERNAME`, and `OPENSEARCH_PASSWORD`. -| Variable | Example | Description | Required | -| -------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -| `OPENSEARCH_ENABLED` | `"yes" \| "no"` | Controls whether OpenSearch is enabled on application startup. If not provided or set to `no`, OpenSearch will not be instantiated. | Yes | -| `OPENSEARCH_DEFAULT_INDEX` | `dataset` | Specifies the default index. If not provided, a default index named `dataset` will be created automatically. | No | -| `OPENSEARCH_HOST` | `https://localhost:9200` | Specifies the OpenSearch server endpoint. | Yes | -| `OPENSEARCH_USERNAME` | `"admin"` | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | Yes | -| `OPENSEARCH_PASSWORD` | `Scicat-password2026` | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | Yes | -| `OPENSEARCH_REFRESH` | `"wait_for" \| "false"` | Controls index refresh behavior. `wait_for` waits for the next refresh cycle before returning, which is useful for development and testing. `false` skips waiting and is recommended for production. Defaults to false. | No | +| Variable | Example | Description | Required | +| --------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| `OPENSEARCH_ENABLED` | `"yes" \| "no"` | Controls whether OpenSearch is enabled on application startup. If not provided or set to `no`, OpenSearch will not be instantiated. | Yes | +| `OPENSEARCH_DEFAULT_INDEX` | `dataset` | Specifies the default index. If not provided, a default index named `dataset` will be created automatically. | No | +| `OPENSEARCH_HOST` | `https://localhost:9200` | Specifies the OpenSearch server endpoint. | Yes | +| `OPENSEARCH_USERNAME` | `"admin"` | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | Yes | +| `OPENSEARCH_PASSWORD` | `Scicat-password2026` | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | Yes | +| `OPENSEARCH_REFRESH` | `"wait_for" \| "false"` | Controls index refresh behavior. `wait_for` waits for the next refresh cycle before returning, which is useful for development and testing. `false` skips waiting and is recommended for production. Defaults to false. | No | +| `OPENSEARCH_DATA_SYNC_BATCH_SIZE` | 10000 | Number of documents fetched from MongoDB per batch during OpenSearch data sync. Defaults to 10000 | No | ## Index Configuration diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 4580d2104..e68f76e32 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -386,6 +386,10 @@ const configuration = () => { host: process.env.OPENSEARCH_HOST, refresh: process.env.OPENSEARCH_REFRESH, defaultIndex: process.env.OPENSEARCH_DEFAULT_INDEX ?? "dataset", + dataSyncBatchSize: parseInt( + process.env.OPENSEARCH_DATA_SYNC_BATCH_SIZE || "10000", + 10, + ), }, metrics: { // Note: `process.env.METRICS_ENABLED` is directly used for conditional module loading in diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 0d20bfccd..8a6319ab1 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -2,6 +2,7 @@ import { BadRequestException, Inject, Injectable, + Logger, NotFoundException, Optional, Scope, @@ -71,11 +72,13 @@ import { } from "./utils/dataset-opensearch.utils"; import type { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; +import { BulkStats } from "@opensearch-project/opensearch/lib/Helpers"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { private readonly osDefaultIndex: string; private readonly isOsEnabled: boolean; + private readonly osSyncBatchSize: number; constructor( private configService: ConfigService, @@ -92,6 +95,8 @@ export class DatasetsService { this.configService.get("opensearch.defaultIndex") || "dataset"; this.isOsEnabled = this.configService.get("opensearch.enabled") === "yes" || false; + this.osSyncBatchSize = + this.configService.get("opensearch.dataSyncBatchSize") || 10000; } private createMetadataKeysInstance( @@ -650,14 +655,60 @@ export class DatasetsService { try { await this.opensearchService.checkIndexExists(index); - const datasets = await this.datasetModel - .find({}, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) - .lean() - .exec(); - return await this.opensearchService.performBulkOperation( - datasets, - index, + let lastId: string | null = null; + const bulkOperationFinalResult: BulkStats = { + total: 0, + failed: 0, + retry: 0, + successful: 0, + noop: 0, + time: 0, + bytes: 0, + aborted: false, + }; + + while (true) { + const query = lastId ? { _id: { $gt: lastId } } : {}; + + const datasets: DatasetClass[] = await this.datasetModel + .find(query, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) + .sort({ _id: 1 }) + .lean() + .limit(this.osSyncBatchSize) + .exec(); + + if (datasets.length === 0) break; + + const bulk = + await this.opensearchService.performBulkOperation( + datasets, + index, + ); + + lastId = datasets[datasets.length - 1]._id; + + // Aggregate bulk stats across batches for internal sync status tracking. + bulkOperationFinalResult.total += bulk.total; + bulkOperationFinalResult.failed += bulk.failed; + bulkOperationFinalResult.retry += bulk.retry; + bulkOperationFinalResult.successful += bulk.successful; + bulkOperationFinalResult.noop += bulk.noop; + bulkOperationFinalResult.time += bulk.time; + bulkOperationFinalResult.bytes += bulk.bytes; + bulkOperationFinalResult.aborted = bulk.aborted; + + Logger.log( + `Synced ${bulkOperationFinalResult.total} datasets to OpenSearch`, + "OpensearchSync", + ); + } + + Logger.log( + `Sync complete — total: ${bulkOperationFinalResult.total}`, + "OpensearchSync", ); + + return bulkOperationFinalResult; } catch (error) { throw new Error(`OpenSearch sync failed: ${error}`); } From 40e3a9dd01ac75c09a130e72265400f1e498e537 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 14:59:57 +0100 Subject: [PATCH 38/46] user cursor for data sync --- src/datasets/datasets.service.ts | 55 +++++++++++++++++++------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 8a6319ab1..b635df410 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -655,7 +655,6 @@ export class DatasetsService { try { await this.opensearchService.checkIndexExists(index); - let lastId: string | null = null; const bulkOperationFinalResult: BulkStats = { total: 0, failed: 0, @@ -667,27 +666,39 @@ export class DatasetsService { aborted: false, }; - while (true) { - const query = lastId ? { _id: { $gt: lastId } } : {}; + const cursor = this.datasetModel + .find({}, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) + .lean() + .cursor({ batchSize: this.osSyncBatchSize }); - const datasets: DatasetClass[] = await this.datasetModel - .find(query, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) - .sort({ _id: 1 }) - .lean() - .limit(this.osSyncBatchSize) - .exec(); + let batch: DatasetClass[] = []; + let isCursorExhausted = false; - if (datasets.length === 0) break; + while (!isCursorExhausted) { + const doc = await cursor.next(); + if (doc) { + batch.push(doc as DatasetClass); + } else { + isCursorExhausted = true; + } + + // Condition: Is the batch full OR are we at the very end with a non-empty tail? + const isBatchReady = batch.length >= this.osSyncBatchSize; + const isFinalBatch = isCursorExhausted && batch.length > 0; + + if (!isBatchReady && !isFinalBatch) { + continue; + } + + // Single source of truth for the bulk operation const bulk = await this.opensearchService.performBulkOperation( - datasets, + batch, index, ); - lastId = datasets[datasets.length - 1]._id; - - // Aggregate bulk stats across batches for internal sync status tracking. + // Aggregate bulk stats bulkOperationFinalResult.total += bulk.total; bulkOperationFinalResult.failed += bulk.failed; bulkOperationFinalResult.retry += bulk.retry; @@ -698,19 +709,19 @@ export class DatasetsService { bulkOperationFinalResult.aborted = bulk.aborted; Logger.log( - `Synced ${bulkOperationFinalResult.total} datasets to OpenSearch`, + `Synced ${bulkOperationFinalResult.total} datasets to OpenSearch (Final Batch: ${isFinalBatch})`, "OpensearchSync", ); - } - Logger.log( - `Sync complete — total: ${bulkOperationFinalResult.total}`, - "OpensearchSync", - ); + batch = []; + } return bulkOperationFinalResult; - } catch (error) { - throw new Error(`OpenSearch sync failed: ${error}`); + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : "Unknown error"; + Logger.error(`Sync failed: ${errorMessage}`, "OpensearchSync"); + throw error; } } } From a0f7c8f08724708a6a1be42ca08ae6c97f1719a9 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 15:07:26 +0100 Subject: [PATCH 39/46] default dataSyncBatchSize to 1000 --- README.md | 2 +- docs/developer-guide/opensearch-guidelines.md | 2 +- src/config/configuration.ts | 2 +- src/datasets/datasets.service.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 09409efb3..cc1db4c0c 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ Valid environment variables for the .env file. See [.env.example](/.env.example) |`OPENSEARCH_USERNAME`| string | Yes | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | "admin" | |`OPENSEARCH_PASSWORD`| string | | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | | |`OPENSEARCH_REFRESH`| string | | Controls index refresh behavior. `wait_for`waits for the next refresh cycle before returning, which is useful for development and testing.`false`skips waiting and is recommended for production. Defaults to false. | false | -|`OPENSEARCH_DATA_SYNC_BATCH_SIZE`| number | | Number of documents fetched from MongoDB per batch during OpenSearch data sync. | 10000 | +|`OPENSEARCH_DATA_SYNC_BATCH_SIZE`| number | | Number of documents fetched from MongoDB per batch during OpenSearch data sync. | 1000 | |`FRONTEND_CONFIG_FILE`| string | | The file name for frontend configuration, located in the`/src/config`directory by default. | "./src/config/frontend.config.json" | |`FRONTEND_THEME_FILE`| string | | The file name for frontend theme, located in the`/src/config`directory by default. | "./src/config/frontend.theme.json" | |`LOGGERS_CONFIG_FILE`| string | | The file name for loggers configuration, located in the project root directory. | "loggers.json" | diff --git a/docs/developer-guide/opensearch-guidelines.md b/docs/developer-guide/opensearch-guidelines.md index ea34dc6d2..8a26aeccb 100644 --- a/docs/developer-guide/opensearch-guidelines.md +++ b/docs/developer-guide/opensearch-guidelines.md @@ -68,7 +68,7 @@ To enable OpenSearch, set `OPENSEARCH_ENABLED=yes` and provide values for all re | `OPENSEARCH_USERNAME` | `"admin"` | Username for OpenSearch authentication. Defaults to `admin` in standard deployments but can be configured to use a custom user with appropriate permissions. | Yes | | `OPENSEARCH_PASSWORD` | `Scicat-password2026` | Password used for OpenSearch authentication. Must match `OPENSEARCH_INITIAL_ADMIN_PASSWORD` used when creating the OpenSearch container. | Yes | | `OPENSEARCH_REFRESH` | `"wait_for" \| "false"` | Controls index refresh behavior. `wait_for` waits for the next refresh cycle before returning, which is useful for development and testing. `false` skips waiting and is recommended for production. Defaults to false. | No | -| `OPENSEARCH_DATA_SYNC_BATCH_SIZE` | 10000 | Number of documents fetched from MongoDB per batch during OpenSearch data sync. Defaults to 10000 | No | +| `OPENSEARCH_DATA_SYNC_BATCH_SIZE` | 1000 | Number of documents fetched from MongoDB per batch during OpenSearch data sync. Defaults to 10000 | No | ## Index Configuration diff --git a/src/config/configuration.ts b/src/config/configuration.ts index e68f76e32..c88fdfa2f 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -387,7 +387,7 @@ const configuration = () => { refresh: process.env.OPENSEARCH_REFRESH, defaultIndex: process.env.OPENSEARCH_DEFAULT_INDEX ?? "dataset", dataSyncBatchSize: parseInt( - process.env.OPENSEARCH_DATA_SYNC_BATCH_SIZE || "10000", + process.env.OPENSEARCH_DATA_SYNC_BATCH_SIZE || "1000", 10, ), }, diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index b635df410..c4a80643a 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -96,7 +96,7 @@ export class DatasetsService { this.isOsEnabled = this.configService.get("opensearch.enabled") === "yes" || false; this.osSyncBatchSize = - this.configService.get("opensearch.dataSyncBatchSize") || 10000; + this.configService.get("opensearch.dataSyncBatchSize") || 1000; } private createMetadataKeysInstance( From c4b095039c5e86708e9be6f723930b2c8e9c4f1f Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 16:07:33 +0100 Subject: [PATCH 40/46] added dataset opensearch dto --- src/datasets/datasets.service.ts | 19 +++++------- .../utils/dataset-opensearch.utils.ts | 31 ------------------- src/opensearch/dto/dataset-opensearch.dto.ts | 9 ++++++ .../utils/dataset-opensearch.utils.ts | 16 ++++++++++ 4 files changed, 32 insertions(+), 43 deletions(-) delete mode 100644 src/datasets/utils/dataset-opensearch.utils.ts create mode 100644 src/opensearch/dto/dataset-opensearch.dto.ts create mode 100644 src/opensearch/utils/dataset-opensearch.utils.ts diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index c4a80643a..7dd5bb56d 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -66,13 +66,12 @@ import { MetadataSourceDoc, } from "src/metadata-keys/metadatakeys.service"; import { OpensearchService } from "src/opensearch/opensearch.service"; -import { - DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY, - sanitizeDatasetForOpensearch, -} from "./utils/dataset-opensearch.utils"; import type { IndexSettings } from "@opensearch-project/opensearch/api/_types/indices._common"; import type { TypeMapping } from "@opensearch-project/opensearch/api/_types/_common.mapping"; import { BulkStats } from "@opensearch-project/opensearch/lib/Helpers"; +import { DatasetOpenSearchDto } from "src/opensearch/dto/dataset-opensearch.dto"; +import { plainToInstance } from "class-transformer"; +import { DATASET_OPENSEARCH_PROJECTION } from "../opensearch/utils/dataset-opensearch.utils"; @Injectable({ scope: Scope.REQUEST }) export class DatasetsService { @@ -204,7 +203,7 @@ export class DatasetsService { if (this.opensearchService && createdDataset) { await this.opensearchService.updateInsertDocument( - sanitizeDatasetForOpensearch(savedDataset.toObject()), + plainToInstance(DatasetOpenSearchDto, savedDataset.toObject()), ); } @@ -493,9 +492,7 @@ export class DatasetsService { if (this.opensearchService) { await this.opensearchService.updateInsertDocument( - sanitizeDatasetForOpensearch( - updatedDataset.toObject(), - ), + plainToInstance(DatasetOpenSearchDto, updatedDataset.toObject()), ); } @@ -543,9 +540,7 @@ export class DatasetsService { if (this.opensearchService) { await this.opensearchService.updateInsertDocument( - sanitizeDatasetForOpensearch( - patchedDataset.toObject(), - ), + plainToInstance(DatasetOpenSearchDto, patchedDataset.toObject()), ); } @@ -667,7 +662,7 @@ export class DatasetsService { }; const cursor = this.datasetModel - .find({}, DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY) + .find({}, DATASET_OPENSEARCH_PROJECTION) .lean() .cursor({ batchSize: this.osSyncBatchSize }); diff --git a/src/datasets/utils/dataset-opensearch.utils.ts b/src/datasets/utils/dataset-opensearch.utils.ts deleted file mode 100644 index 12470cb83..000000000 --- a/src/datasets/utils/dataset-opensearch.utils.ts +++ /dev/null @@ -1,31 +0,0 @@ -// It controlles which fields of the dataset document should be excluded -// when sending it to Opensearch. -const DATASET_OPENSEARCH_EXCLUDED_FIELDS = [ - "scientificMetadata", - "history", - "datasetlifecycle", -] as const; - -// OUTPUT EXAMPLE: -// DATASET_OPENSEARCH_EXCLUDED_FIELDS[0] // "scientificMetadata" -// DATASET_OPENSEARCH_EXCLUDED_FIELDS[1] // "history" ...etc -type ExcludedDatasetField = (typeof DATASET_OPENSEARCH_EXCLUDED_FIELDS)[number]; - -// OUTPUT EXAMPLE: -// { scientificMetadata: 0, history: 0, datasetlifecycle: 0 } -export const DATASET_OPENSEARCH_EXCLUDE_FIELDS_QUERY = Object.fromEntries( - DATASET_OPENSEARCH_EXCLUDED_FIELDS.map((field) => [field, 0]), -) as Record; - -// It removes the fields that are defined in DATASET_OPENSEARCH_EXCLUDED_FIELDS -// from the given document before sending it to Opensearch. -export function sanitizeDatasetForOpensearch( - doc: T, -): Omit { - const result = { ...doc } as Record; - for (const field of DATASET_OPENSEARCH_EXCLUDED_FIELDS) { - delete result[field]; - } - - return result as Omit; -} diff --git a/src/opensearch/dto/dataset-opensearch.dto.ts b/src/opensearch/dto/dataset-opensearch.dto.ts new file mode 100644 index 000000000..eddec908d --- /dev/null +++ b/src/opensearch/dto/dataset-opensearch.dto.ts @@ -0,0 +1,9 @@ +import { PickType } from "@nestjs/swagger"; + +import { UpdateDatasetDto } from "src/datasets/dto/update-dataset.dto"; +import { DATASET_OPENSEARCH_FIELDS } from "src/opensearch/utils/dataset-opensearch.utils"; + +export class DatasetOpenSearchDto extends PickType( + UpdateDatasetDto, + DATASET_OPENSEARCH_FIELDS, +) {} diff --git a/src/opensearch/utils/dataset-opensearch.utils.ts b/src/opensearch/utils/dataset-opensearch.utils.ts new file mode 100644 index 000000000..a3bae5e37 --- /dev/null +++ b/src/opensearch/utils/dataset-opensearch.utils.ts @@ -0,0 +1,16 @@ +// It controlles which fields of the dataset document should be excluded +// when sending it to Opensearch. +export const DATASET_OPENSEARCH_FIELDS = [ + "description", + "datasetName", + "isPublished", + "relationships", + "ownerGroup", + "accessGroups", +] as const; + +// OUTPUT EXAMPLE: +// { description: 1, datasetName: 1, isPublished: 1, relationships: 1, ownerGroup: 1, accessGroups: 1 } +export const DATASET_OPENSEARCH_PROJECTION = Object.fromEntries( + DATASET_OPENSEARCH_FIELDS.map((key) => [key, 1]), +); From 1ae35710750566b42be653d8f14770daed29f5a6 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 16:08:40 +0100 Subject: [PATCH 41/46] minor text change --- src/opensearch/utils/dataset-opensearch.utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/opensearch/utils/dataset-opensearch.utils.ts b/src/opensearch/utils/dataset-opensearch.utils.ts index a3bae5e37..a96036a19 100644 --- a/src/opensearch/utils/dataset-opensearch.utils.ts +++ b/src/opensearch/utils/dataset-opensearch.utils.ts @@ -1,16 +1,15 @@ -// It controlles which fields of the dataset document should be excluded +// It controlles which fields of the dataset document should be included // when sending it to Opensearch. export const DATASET_OPENSEARCH_FIELDS = [ "description", "datasetName", "isPublished", - "relationships", "ownerGroup", "accessGroups", ] as const; // OUTPUT EXAMPLE: -// { description: 1, datasetName: 1, isPublished: 1, relationships: 1, ownerGroup: 1, accessGroups: 1 } +// { description: 1, datasetName: 1, isPublished: 1, ownerGroup: 1, accessGroups: 1 } export const DATASET_OPENSEARCH_PROJECTION = Object.fromEntries( DATASET_OPENSEARCH_FIELDS.map((key) => [key, 1]), ); From 5fa33adfd2db3295dc22efb35b4cf992678f8ce3 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Thu, 19 Mar 2026 16:44:42 +0100 Subject: [PATCH 42/46] add excludeExtraneousValues ot plainToInstance --- src/datasets/datasets.service.ts | 12 +++++++++--- src/opensearch/dto/dataset-opensearch.dto.ts | 18 +++++++++++++----- src/opensearch/opensearch.service.ts | 3 --- .../utils/dataset-opensearch.utils.ts | 1 + 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/datasets/datasets.service.ts b/src/datasets/datasets.service.ts index 7dd5bb56d..7ca344075 100644 --- a/src/datasets/datasets.service.ts +++ b/src/datasets/datasets.service.ts @@ -203,7 +203,9 @@ export class DatasetsService { if (this.opensearchService && createdDataset) { await this.opensearchService.updateInsertDocument( - plainToInstance(DatasetOpenSearchDto, savedDataset.toObject()), + plainToInstance(DatasetOpenSearchDto, savedDataset.toObject(), { + excludeExtraneousValues: true, + }), ); } @@ -492,7 +494,9 @@ export class DatasetsService { if (this.opensearchService) { await this.opensearchService.updateInsertDocument( - plainToInstance(DatasetOpenSearchDto, updatedDataset.toObject()), + plainToInstance(DatasetOpenSearchDto, updatedDataset.toObject(), { + excludeExtraneousValues: true, + }), ); } @@ -540,7 +544,9 @@ export class DatasetsService { if (this.opensearchService) { await this.opensearchService.updateInsertDocument( - plainToInstance(DatasetOpenSearchDto, patchedDataset.toObject()), + plainToInstance(DatasetOpenSearchDto, patchedDataset.toObject(), { + excludeExtraneousValues: true, + }), ); } diff --git a/src/opensearch/dto/dataset-opensearch.dto.ts b/src/opensearch/dto/dataset-opensearch.dto.ts index eddec908d..5c9d2d6ca 100644 --- a/src/opensearch/dto/dataset-opensearch.dto.ts +++ b/src/opensearch/dto/dataset-opensearch.dto.ts @@ -1,9 +1,17 @@ -import { PickType } from "@nestjs/swagger"; +import { OmitType } from "@nestjs/swagger"; +import { Expose } from "class-transformer"; +import { OutputDatasetDto } from "src/datasets/dto/output-dataset.dto"; -import { UpdateDatasetDto } from "src/datasets/dto/update-dataset.dto"; import { DATASET_OPENSEARCH_FIELDS } from "src/opensearch/utils/dataset-opensearch.utils"; -export class DatasetOpenSearchDto extends PickType( - UpdateDatasetDto, +export class DatasetOpenSearchDto extends OmitType( + OutputDatasetDto, DATASET_OPENSEARCH_FIELDS, -) {} +) { + @Expose() pid: string; + @Expose() description?: string; + @Expose() datasetName: string; + @Expose() isPublished?: boolean; + @Expose() ownerGroup: string; + @Expose() accessGroups: string[]; +} diff --git a/src/opensearch/opensearch.service.ts b/src/opensearch/opensearch.service.ts index 8528b480f..d2dd82e9f 100644 --- a/src/opensearch/opensearch.service.ts +++ b/src/opensearch/opensearch.service.ts @@ -308,9 +308,6 @@ export class OpensearchService implements OnModuleInit { } async updateInsertDocument(data: Partial) { - //NOTE: Replace all keys with lower case, also replace spaces and dot with underscore - delete data._id; - try { await this.osClient.index({ index: this.defaultIndex, diff --git a/src/opensearch/utils/dataset-opensearch.utils.ts b/src/opensearch/utils/dataset-opensearch.utils.ts index a96036a19..210cc8e99 100644 --- a/src/opensearch/utils/dataset-opensearch.utils.ts +++ b/src/opensearch/utils/dataset-opensearch.utils.ts @@ -1,6 +1,7 @@ // It controlles which fields of the dataset document should be included // when sending it to Opensearch. export const DATASET_OPENSEARCH_FIELDS = [ + "pid", "description", "datasetName", "isPublished", From 572130e1a565807df576019a5773730b9c3d2b09 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 24 Mar 2026 16:10:17 +0100 Subject: [PATCH 43/46] update package --- package-lock.json | 3515 +++++++++++++++++++++++++++++---------------- 1 file changed, 2266 insertions(+), 1249 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0dada054e..07c0ce4e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,20 +109,49 @@ "wait-on": "^9.0.1" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "node_modules/@angular-devkit/schematics": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", + "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@angular-devkit/core": "19.2.19", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" }, "engines": { - "node": ">=6.0.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", + "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@angular-devkit/core": { + "node_modules/@angular-devkit/schematics-cli/node_modules/@angular-devkit/core": { "version": "19.2.19", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", @@ -150,7 +179,37 @@ } } }, - "node_modules/@angular-devkit/core/node_modules/ajv": { + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", @@ -167,7 +226,41 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { + "node_modules/@angular-devkit/schematics-cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", @@ -177,76 +270,83 @@ "tslib": "^2.1.0" } }, - "node_modules/@angular-devkit/schematics": { + "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", - "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.19", + "ajv": "8.17.1", + "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" }, "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } } }, - "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", - "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", + "node_modules/@angular-devkit/schematics/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.19", - "@angular-devkit/schematics": "19.2.19", - "@inquirer/prompts": "7.3.2", - "ansi-colors": "4.1.3", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", - "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "node_modules/@angular-devkit/schematics/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@inquirer/checkbox": "^4.1.2", - "@inquirer/confirm": "^5.1.6", - "@inquirer/editor": "^4.2.7", - "@inquirer/expand": "^4.0.9", - "@inquirer/input": "^4.1.6", - "@inquirer/number": "^3.0.9", - "@inquirer/password": "^4.0.9", - "@inquirer/rawlist": "^4.0.9", - "@inquirer/search": "^3.0.9", - "@inquirer/select": "^4.0.9" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=18" + "node": ">= 14.16.0" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.18.0" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { @@ -260,13 +360,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -275,9 +375,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -285,22 +385,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -320,19 +420,20 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -342,13 +443,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -379,29 +480,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -431,9 +532,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "devOptional": true, "license": "MIT", "engines": { @@ -451,27 +552,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -536,13 +637,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -729,33 +830,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -763,14 +864,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -783,6 +884,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@casl/ability": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/@casl/ability/-/ability-6.8.0.tgz", @@ -809,6 +920,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -819,6 +931,7 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -831,6 +944,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1035,20 +1149,20 @@ } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", "dependencies": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "node_modules/@emnapi/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", - "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", "dev": true, "license": "MIT", "optional": true, @@ -1058,9 +1172,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", "dev": true, "license": "MIT", "optional": true, @@ -1103,6 +1217,7 @@ "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, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1246,6 +1361,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1261,9 +1377,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -1298,9 +1414,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.3.0.tgz", - "integrity": "sha512-It0Sne6P3szg7JIi6CgKbvTZoMjxBZhcv91ZrqrNuaZQfB5WoqYYbzCUOq89YR+VY8juY9M1vDWmDDa2TzfXCw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.4.0.tgz", + "integrity": "sha512-sDBWI3yLy8EcDzgobvJTWq1MJYzAkQdpjXuPukga9wXonhpMRvd1Izuo2Qgwey2OiEoRIBr35RMU9HJRoOHzpw==", "dev": true, "funding": [ { @@ -1373,41 +1489,31 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1417,10 +1523,11 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1784,6 +1891,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "devOptional": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1796,29 +1904,45 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "devOptional": true + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "devOptional": true, + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "devOptional": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -1831,11 +1955,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "devOptional": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1900,9 +2041,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -1970,6 +2111,7 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2039,22 +2181,6 @@ } } }, - "node_modules/@jest/core/node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/diff-sequences": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", @@ -2241,6 +2367,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@jest/reporters/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -2257,6 +2390,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@jest/schemas": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", @@ -2379,9 +2529,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2389,11 +2539,23 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -2410,15 +2572,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "devOptional": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "devOptional": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2430,6 +2593,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "license": "MIT", "engines": { "node": ">= 10.16.0" }, @@ -2441,6 +2605,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "license": "MIT", "engines": { "node": ">= 10.16.0" }, @@ -2452,6 +2617,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", "engines": { "node": ">=8" } @@ -2463,9 +2629,9 @@ "license": "MIT" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.1.tgz", - "integrity": "sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.6.tgz", + "integrity": "sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -2566,42 +2732,6 @@ } } }, - "node_modules/@nestjs-modules/mailer/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@nestjs-modules/mailer/node_modules/nunjucks": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", - "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", - "license": "BSD-2-Clause", - "optional": true, - "dependencies": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "commander": "^5.1.0" - }, - "bin": { - "nunjucks-precompile": "bin/precompile" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "chokidar": "^3.3.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@nestjs/axios": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.1.tgz", @@ -2658,6 +2788,51 @@ } } }, + "node_modules/@nestjs/cli/node_modules/@angular-devkit/core": { + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@nestjs/cli/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -2669,9 +2844,9 @@ } }, "node_modules/@nestjs/cli/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { @@ -2681,6 +2856,46 @@ "node": "18 || 20 || >=22" } }, + "node_modules/@nestjs/cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nestjs/cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@nestjs/cli/node_modules/glob": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", @@ -2699,20 +2914,33 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nestjs/cli/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "node_modules/@nestjs/cli/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/@nestjs/cli/node_modules/minimatch": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", - "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -2725,40 +2953,137 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@nestjs/cli/node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "node_modules/@nestjs/cli/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">= 14.18.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nestjs/common": { - "version": "11.0.13", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.0.13.tgz", - "integrity": "sha512-cXqXJPQTcJIYqT8GtBYqjYY9sklCBqp/rh9z1R40E60gWnsU598YIQWkojSFRI9G7lT/+uF+jqSrg/CMPBk7QQ==", + "node_modules/@nestjs/cli/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "iterare": "1.2.1", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12 || ^0.2.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/cli/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@nestjs/cli/node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.104.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", + "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.4", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.17", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.17.tgz", + "integrity": "sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==", + "license": "MIT", + "dependencies": { + "file-type": "21.3.2", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, "peerDependenciesMeta": { @@ -2798,15 +3123,16 @@ } }, "node_modules/@nestjs/core": { - "version": "11.0.13", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.0.13.tgz", - "integrity": "sha512-1xjrsYjff4sg4MfvF+/NInOq+7oI1D1vK8Yj9wkrbBH1dM+h2At71tccbFfl/eJUt4ckZlH+XmROnt/T0daYcA==", + "version": "11.1.17", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.17.tgz", + "integrity": "sha512-lD5mAYekTTurF3vDaa8C2OKPnjiz4tsfxIc5XlcSUzOhkwWf6Ay3HKvt6FmvuWQam6uIIHX52Clg+e6tAvf/cg==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "8.2.0", + "path-to-regexp": "8.3.0", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -2841,6 +3167,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/@nestjs/event-emitter/-/event-emitter-3.0.1.tgz", "integrity": "sha512-0Ln/x+7xkU6AJFOcQI9tIhUMXVF7D5itiaQGOyJbXtlAfAIt8gzDdJm+Im7cFzKoWkiW5nCXCPh6GSvdQd/3Dw==", + "license": "MIT", "dependencies": { "eventemitter2": "6.4.9" }, @@ -2878,6 +3205,7 @@ "version": "11.0.5", "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", + "license": "MIT", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" @@ -2904,16 +3232,6 @@ "@nestjs/core": "^11.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/@nestjs/schematics": { "version": "11.0.9", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", @@ -2995,6 +3313,40 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@nestjs/schematics/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nestjs/schematics/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -3058,16 +3410,6 @@ } } }, - "node_modules/@nestjs/swagger/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/@nestjs/terminus": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-11.1.1.tgz", @@ -3182,6 +3524,7 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "dev": true, + "license": "MIT", "engines": { "node": "^14.21.3 || >=16" }, @@ -3193,6 +3536,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "license": "MIT", "dependencies": { "consola": "^3.2.3" }, @@ -3204,20 +3548,10 @@ "npm": ">=5.10.0" } }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "optional": true - }, "node_modules/@opensearch-project/opensearch": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/@opensearch-project/opensearch/-/opensearch-3.5.1.tgz", "integrity": "sha512-6bf+HcuERzAtHZxrm6phjref54ABse39BpkDie/YO3AUFMCBrb3SK5okKSdT5n3+nDRuEEQLhQCl0RQV3s1qpA==", - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", "dependencies": { "aws4": "^1.11.0", @@ -3232,17 +3566,12 @@ "yarn": "^1.22.10" } }, - "node_modules/@opensearch-project/opensearch/node_modules/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==", - "license": "BSD-3-Clause" - }, "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, + "license": "MIT", "dependencies": { "@noble/hashes": "^1.1.5" } @@ -3251,6 +3580,8 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -3273,7 +3604,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true + "hasInstallScript": true, + "license": "Apache-2.0" }, "node_modules/@selderee/plugin-htmlparser2": { "version": "0.11.0", @@ -3290,9 +3622,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -3301,6 +3633,7 @@ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } @@ -3336,6 +3669,16 @@ "node": ">=4" } }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -3365,9 +3708,9 @@ } }, "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -3377,29 +3720,56 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, "node_modules/@tsconfig/node10": { - "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 + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", @@ -3413,9 +3783,10 @@ } }, "node_modules/@types/amqplib": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.7.tgz", - "integrity": "sha512-IVj3avf9AQd2nXCx0PGk/OYq7VmHiyNxWFSb5HhU9ATh+i+gHWvVcljFTcTWQ/dyHJCTrzCixde+r/asL2ErDA==", + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.8.tgz", + "integrity": "sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3476,10 +3847,11 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, + "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -3501,6 +3873,7 @@ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3509,18 +3882,21 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/ejs": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "license": "MIT", "optional": true }, "node_modules/@types/eslint": { @@ -3528,6 +3904,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3538,6 +3915,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -3570,10 +3948,11 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3602,13 +3981,15 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -3618,6 +3999,7 @@ "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -3637,7 +4019,8 @@ "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-merge-patch": { "version": "1.0.0", @@ -3649,13 +4032,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/jsonpath-plus": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@types/jsonpath-plus/-/jsonpath-plus-5.0.5.tgz", "integrity": "sha512-aaqqDf5LcGOtAfEntO5qKZS6ixT0MpNhUXNwbVPdLf7ET9hKsufJq+buZr7eXSnWoLRyGzVj2Yz2hbjVSK3wsA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/jsonschema": { "version": "1.1.1", @@ -3663,6 +4048,7 @@ "integrity": "sha512-JPP6H7/h/uXzAow+NbD/jqA3tA1Fj3u2C0KiFAlQ+rtl2QhozxMEz0aPjKnr4YyAB8/6s0Xm9UPa0fFUB0y1OA==", "deprecated": "This is a stub types definition for jsonschema (https://github.com/tdegrunt/jsonschema). jsonschema provides its own type definitions, so you don't need @types/jsonschema installed!", "dev": true, + "license": "MIT", "dependencies": { "jsonschema": "*" } @@ -3681,6 +4067,7 @@ "version": "2.2.5", "resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.5.tgz", "integrity": "sha512-Lv/nD6QDCmcT+V1vaTRnEKE8UgOilVv5pHcQuzkU1LcRe4mbHHuUo/KHi0LKrpdHhQY8FJzryF38fcVdeUIrzg==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -3703,34 +4090,32 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mjml": { "version": "4.7.4", "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", + "license": "MIT", "optional": true, "dependencies": { "@types/mjml-core": "*" } }, "node_modules/@types/mjml-core": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.1.tgz", - "integrity": "sha512-qu8dUksU8yXX18qMTFINkM4uoz7WQYC5F14lcWeSNmWbulaGG0KG19yeZwpx75b9RJXr8WI/FRHH0LyQTU9JbA==", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.2.tgz", + "integrity": "sha512-Q7SxFXgoX979HP57DEVsRI50TV8x1V4lfCA4Up9AvfINDM5oD/X9ARgfoyX1qS987JCnDLv85JjkqAjt3hZSiQ==", + "license": "MIT", "optional": true }, "node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "2.1.0", @@ -3773,6 +4158,7 @@ "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*" } @@ -3782,6 +4168,7 @@ "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/jsonwebtoken": "*", "@types/passport-strategy": "*" @@ -3792,6 +4179,7 @@ "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.38.tgz", "integrity": "sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*", "@types/passport": "*", @@ -3803,6 +4191,7 @@ "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*", "@types/passport": "*" @@ -3812,19 +4201,22 @@ "version": "2.0.10", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "license": "MIT", "optional": true }, "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "dev": true, + "license": "MIT" }, "node_modules/@types/range-parser": { "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/relateurl": { "version": "0.2.33", @@ -3834,12 +4226,12 @@ "optional": true }, "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -3866,6 +4258,7 @@ "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/cookiejar": "^2.1.5", "@types/methods": "^1.1.4", @@ -3910,21 +4303,25 @@ "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" }, "node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "license": "MIT", + "peer": true, "dependencies": { "@types/webidl-conversions": "*" } }, "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -3933,20 +4330,21 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", - "integrity": "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", + "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/type-utils": "8.57.1", - "@typescript-eslint/utils": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/type-utils": "8.57.2", + "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -3959,7 +4357,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.1", + "@typescript-eslint/parser": "^8.57.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3975,16 +4373,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.1.tgz", - "integrity": "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", + "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3" }, "engines": { @@ -4000,14 +4398,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.1.tgz", - "integrity": "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", + "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.1", - "@typescript-eslint/types": "^8.57.1", + "@typescript-eslint/tsconfig-utils": "^8.57.2", + "@typescript-eslint/types": "^8.57.2", "debug": "^4.4.3" }, "engines": { @@ -4022,14 +4420,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz", - "integrity": "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", + "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1" + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4040,9 +4438,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz", - "integrity": "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", + "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", "dev": true, "license": "MIT", "engines": { @@ -4057,15 +4455,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz", - "integrity": "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", + "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1", - "@typescript-eslint/utils": "8.57.1", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2", + "@typescript-eslint/utils": "8.57.2", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -4082,9 +4480,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.1.tgz", - "integrity": "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", + "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", "dev": true, "license": "MIT", "engines": { @@ -4096,16 +4494,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz", - "integrity": "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", + "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.1", - "@typescript-eslint/tsconfig-utils": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/visitor-keys": "8.57.1", + "@typescript-eslint/project-service": "8.57.2", + "@typescript-eslint/tsconfig-utils": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/visitor-keys": "8.57.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -4163,16 +4561,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.1.tgz", - "integrity": "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", + "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.1", - "@typescript-eslint/types": "8.57.1", - "@typescript-eslint/typescript-estree": "8.57.1" + "@typescript-eslint/scope-manager": "8.57.2", + "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/typescript-estree": "8.57.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4187,13 +4585,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz", - "integrity": "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==", + "version": "8.57.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", + "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.1", + "@typescript-eslint/types": "8.57.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4220,32 +4618,36 @@ "node_modules/@ucast/core": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/@ucast/core/-/core-1.10.2.tgz", - "integrity": "sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==" + "integrity": "sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==", + "license": "Apache-2.0" }, "node_modules/@ucast/js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.0.4.tgz", - "integrity": "sha512-TgG1aIaCMdcaEyckOZKQozn1hazE0w90SVdlpIJ/er8xVumE11gYAtSbw/LBeUnA4fFnFWTcw3t6reqseeH/4Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@ucast/js/-/js-3.1.0.tgz", + "integrity": "sha512-eJ7yQeYtMK85UZjxoxBEbTWx6UMxEXKbjVyp+NlzrT5oMKV5Gpo/9bjTl3r7msaXTVC8iD9NJacqJ8yp7joX+Q==", + "license": "Apache-2.0", "dependencies": { - "@ucast/core": "^1.0.0" + "@ucast/core": "1.10.2" } }, "node_modules/@ucast/mongo": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/@ucast/mongo/-/mongo-2.4.3.tgz", "integrity": "sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==", + "license": "Apache-2.0", "dependencies": { "@ucast/core": "^1.4.1" } }, "node_modules/@ucast/mongo2js": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.4.0.tgz", - "integrity": "sha512-vR9RJ3BHlkI3RfKJIZFdVktxWvBCQRiSTeJSWN9NPxP5YJkpfXvcBWAMLwvyJx4HbB+qib5/AlSDEmQiuQyx2w==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@ucast/mongo2js/-/mongo2js-1.4.1.tgz", + "integrity": "sha512-9aeg5cmqwRQnKCXHN6I17wk83Rcm487bHelaG8T4vfpWneAI469wSI3Srnbu+PuZ5znWRbnwtVq9RgPL+bN6CA==", + "license": "Apache-2.0", "dependencies": { - "@ucast/core": "^1.6.1", - "@ucast/js": "^3.0.0", - "@ucast/mongo": "^2.4.0" + "@ucast/core": "1.10.2", + "@ucast/js": "3.1.0", + "@ucast/mongo": "2.4.3" } }, "node_modules/@ungap/structured-clone": { @@ -4559,6 +4961,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -4568,25 +4971,29 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4597,13 +5004,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4616,6 +5025,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -4625,6 +5035,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -4633,13 +5044,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4656,6 +5069,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -4669,6 +5083,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4681,6 +5096,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4695,6 +5111,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -4704,13 +5121,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@zone-eu/mailsplit": { "version": "5.4.8", @@ -4734,12 +5153,14 @@ "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -4779,15 +5200,17 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^8.11.0" }, @@ -4816,6 +5239,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -4833,6 +5257,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -4844,6 +5269,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", + "license": "MIT", "optional": true, "dependencies": { "esprima": "^1.2.0", @@ -4892,6 +5318,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", "dependencies": { "string-width": "^4.1.0" } @@ -4901,6 +5328,7 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4921,10 +5349,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -4933,6 +5375,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4958,6 +5401,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "devOptional": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4967,10 +5411,11 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -4988,12 +5433,14 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/array-timsort": { "version": "1.0.3", @@ -5006,12 +5453,14 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } @@ -5039,6 +5488,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", "engines": { "node": ">=0.8" } @@ -5056,12 +5506,14 @@ "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/aws4": { "version": "1.13.2", @@ -5070,9 +5522,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -5196,6 +5648,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", + "license": "MIT", "dependencies": { "precond": "0.2" }, @@ -5207,7 +5660,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -5227,16 +5681,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", - "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", + "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", "devOptional": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/bcrypt": { @@ -5256,7 +5714,8 @@ "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -5276,6 +5735,7 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -5289,9 +5749,9 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", - "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -5300,7 +5760,7 @@ "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", - "qs": "^6.14.0", + "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" }, @@ -5323,6 +5783,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "license": "MIT", "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", @@ -5340,32 +5801,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/boxen/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5394,6 +5834,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "devOptional": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -5405,7 +5846,8 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserslist": { "version": "4.28.1", @@ -5426,6 +5868,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5445,6 +5888,7 @@ "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, + "license": "MIT", "dependencies": { "fast-json-stable-stringify": "2.x" }, @@ -5457,17 +5901,19 @@ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } }, "node_modules/bson": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", - "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", + "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", "license": "Apache-2.0", + "peer": true, "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" } }, "node_modules/buffer": { @@ -5489,6 +5935,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -5503,12 +5950,14 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" }, "node_modules/buffer-more-ints": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz", - "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==" + "integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==", + "license": "MIT" }, "node_modules/busboy": { "version": "1.6.0", @@ -5534,6 +5983,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -5546,6 +5996,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -5562,6 +6013,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5570,6 +6022,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -5591,9 +6044,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", "devOptional": true, "funding": [ { @@ -5644,6 +6097,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5669,6 +6123,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "license": "MIT", "optional": true, "dependencies": { "is-regex": "^1.0.3" @@ -5686,6 +6141,7 @@ "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -5694,6 +6150,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz", "integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==", + "license": "MIT", "engines": { "node": ">=16" } @@ -5742,39 +6199,42 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/cheerio/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "license": "MIT", "optional": true, "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "dependencies": { - "readdirp": "^4.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/chrome-trace-event": { @@ -5782,21 +6242,23 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], - "optional": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5811,7 +6273,8 @@ "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" }, "node_modules/class-validator": { "version": "0.15.1", @@ -5828,6 +6291,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "license": "MIT", "engines": { "node": ">=6" }, @@ -5840,6 +6304,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -5852,6 +6317,7 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -5863,6 +6329,7 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -5888,6 +6355,7 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "devOptional": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -5897,23 +6365,12 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "devOptional": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -5931,6 +6388,7 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -5940,6 +6398,7 @@ "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-3.0.0.tgz", "integrity": "sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==", "dev": true, + "license": "MIT", "dependencies": { "is-regexp": "^3.0.0" }, @@ -5969,19 +6428,23 @@ "license": "MIT" }, "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -5992,32 +6455,50 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" } }, "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" } }, "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } }, "node_modules/colord": { "version": "2.9.3", @@ -6026,20 +6507,11 @@ "license": "MIT", "optional": true }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -6052,6 +6524,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -6072,9 +6545,10 @@ } }, "node_modules/complex.js": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz", - "integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz", + "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==", + "license": "MIT", "engines": { "node": "*" }, @@ -6088,6 +6562,7 @@ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -6096,7 +6571,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-stream": { "version": "2.0.0", @@ -6143,6 +6619,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6174,6 +6651,7 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -6182,6 +6660,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "license": "MIT", "optional": true, "dependencies": { "@babel/parser": "^7.6.0", @@ -6189,20 +6668,23 @@ } }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6212,6 +6694,7 @@ "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -6223,12 +6706,14 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6237,6 +6722,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", "engines": { "node": ">=6.6.0" } @@ -6245,7 +6731,8 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/core-util-is": { "version": "1.0.3", @@ -6276,6 +6763,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", @@ -6301,13 +6789,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "devOptional": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6525,6 +7015,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", "engines": { "node": ">= 12" } @@ -6551,6 +7042,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -6559,9 +7051,10 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" }, "node_modules/dedent": { "version": "1.7.2", @@ -6582,6 +7075,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "optional": true, "engines": { "node": ">=4.0.0" @@ -6591,13 +7085,15 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6607,6 +7103,7 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -6618,6 +7115,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -6626,6 +7124,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6634,6 +7133,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -6644,6 +7144,7 @@ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6660,6 +7161,7 @@ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, + "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -6689,6 +7191,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", + "license": "MIT", "optional": true, "dependencies": { "escape-string-applescript": "^1.0.0", @@ -6709,6 +7212,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "optional": true, "dependencies": { "domelementtype": "^2.3.0", @@ -6729,12 +7233,14 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "BSD-2-Clause", "optional": true }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "optional": true, "dependencies": { "domelementtype": "^2.3.0" @@ -6750,6 +7256,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", "optional": true, "dependencies": { "dom-serializer": "^2.0.0", @@ -6819,6 +7326,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -6832,7 +7340,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", @@ -6846,7 +7355,8 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/ejs": { "version": "5.0.1", @@ -6862,9 +7372,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "version": "1.5.322", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.322.tgz", + "integrity": "sha512-vFU34OcrvMcH66T+dYC3G4nURmgfDVewMIu6Q2urXpumAPSMmzvcn04KVVV8Opikq8Vs5nUbO/8laNhNRqSzYw==", "devOptional": true, "license": "ISC" }, @@ -6884,7 +7394,8 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/enabled": { "version": "2.0.0", @@ -6896,6 +7407,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6938,13 +7450,14 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -6954,6 +7467,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "optional": true, "engines": { "node": ">=0.12" @@ -6973,10 +7487,11 @@ } }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "devOptional": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -6985,6 +7500,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6993,6 +7509,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -7008,6 +7525,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -7019,6 +7537,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -7034,6 +7553,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7054,17 +7574,20 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, "node_modules/escape-latex": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", + "license": "MIT" }, "node_modules/escape-string-applescript": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -7075,6 +7598,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7334,6 +7858,7 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -7360,6 +7885,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -7372,6 +7898,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -7381,6 +7908,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -7389,6 +7917,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7396,13 +7925,15 @@ "node_modules/eventemitter2": { "version": "6.4.9", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } @@ -7535,12 +8066,14 @@ "node_modules/express-session/node_modules/cookie-signature": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7548,12 +8081,14 @@ "node_modules/express-session/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/extend-object": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==", + "license": "MIT", "optional": true }, "node_modules/extsprintf": { @@ -7562,12 +8097,14 @@ "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", "engines": [ "node >=0.6.0" - ] + ], + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -7580,23 +8117,26 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { "type": "github", @@ -7606,13 +8146,15 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } @@ -7655,6 +8197,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -7668,6 +8211,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -7675,6 +8219,24 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "21.3.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.2.tgz", + "integrity": "sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -7708,6 +8270,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "devOptional": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -7716,9 +8279,10 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -7728,7 +8292,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/find-up": { @@ -7736,6 +8304,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -7751,6 +8320,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", + "license": "MIT", "optional": true, "dependencies": { "alce": "1.2.0", @@ -7768,6 +8338,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", "optional": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -7782,6 +8353,7 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } @@ -7791,6 +8363,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -7837,6 +8410,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "devOptional": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -7853,6 +8427,7 @@ "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", "chalk": "^4.1.2", @@ -7875,6 +8450,36 @@ "webpack": "^5.11.0" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -7895,6 +8500,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7903,6 +8509,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -7914,6 +8521,7 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" }, @@ -7926,6 +8534,7 @@ "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, + "license": "MIT", "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", @@ -7942,16 +8551,18 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fraction.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.2.tgz", - "integrity": "sha512-uXBDv5knpYmv/2gLzWQ5mBHGBRk9wcKTeWu6GLTUEQfjCxO09uM/mHDrojlL+Q1mVGIIFo149Gba7od1XPgSzQ==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "license": "MIT", "engines": { - "node": ">= 12" + "node": "*" }, "funding": { "type": "github", @@ -7962,6 +8573,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7971,6 +8583,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7981,10 +8594,11 @@ } }, "node_modules/fs-monkey": { - "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 + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -7998,6 +8612,7 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -8010,6 +8625,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8019,6 +8635,7 @@ "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-0.1.1.tgz", "integrity": "sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -8030,6 +8647,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/gelf-pro/-/gelf-pro-1.4.0.tgz", "integrity": "sha512-VXQ1DHQjQr8/Xil83hHgozqeCcVrggGAA6A8hgjojQSatiF6PErR+nnYv/DCBxFBolO6licomD7Abaj0Iec7Qg==", + "license": "MIT", "dependencies": { "lodash": "~4.17.21" } @@ -8039,6 +8657,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -8048,6 +8667,7 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "devOptional": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -8056,6 +8676,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -8089,6 +8710,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -8101,6 +8723,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -8144,6 +8767,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -8179,15 +8803,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/glob/node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/glob/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", @@ -8203,22 +8818,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "17.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", @@ -8236,6 +8835,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8247,12 +8847,14 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -8273,6 +8875,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -8281,6 +8884,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -8289,6 +8893,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8300,6 +8905,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -8314,6 +8920,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -8326,6 +8933,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "devOptional": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -8363,6 +8971,26 @@ "node": ">=14" } }, + "node_modules/html-to-text/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/htmlnano": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-3.2.0.tgz", @@ -8453,9 +9081,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -8463,12 +9091,13 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "optional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, "node_modules/http-errors": { @@ -8521,7 +9150,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -8535,13 +9163,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -8551,6 +9181,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "devOptional": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -8587,6 +9218,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -8606,12 +9238,14 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC", "optional": true }, "node_modules/ip-regex": { @@ -8619,6 +9253,7 @@ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz", "integrity": "sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -8630,6 +9265,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -8638,7 +9274,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -8657,6 +9294,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "optional": true, "dependencies": { "hasown": "^2.0.2" @@ -8672,6 +9310,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", "optional": true, "bin": { "is-docker": "cli.js" @@ -8687,6 +9326,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "license": "MIT", "optional": true, "dependencies": { "acorn": "^7.1.1", @@ -8697,6 +9337,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", "optional": true, "bin": { "acorn": "bin/acorn" @@ -8710,6 +9351,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8718,6 +9360,7 @@ "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==", + "license": "MIT", "engines": { "node": ">=8" } @@ -8737,6 +9380,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "devOptional": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -8749,6 +9393,7 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8758,6 +9403,7 @@ "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-5.0.1.tgz", "integrity": "sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==", "dev": true, + "license": "MIT", "dependencies": { "ip-regex": "^5.0.0", "super-regex": "^0.2.0" @@ -8781,6 +9427,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8800,6 +9447,7 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8808,12 +9456,14 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT", "optional": true }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "optional": true, "dependencies": { "call-bound": "^1.0.2", @@ -8833,6 +9483,7 @@ "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -8844,6 +9495,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -8856,6 +9508,7 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8867,6 +9520,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", "optional": true, "dependencies": { "is-docker": "^2.0.0" @@ -8879,13 +9533,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "devOptional": true + "devOptional": true, + "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } @@ -8955,6 +9611,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", "engines": { "node": ">=6" } @@ -8978,7 +9635,8 @@ "node_modules/javascript-natural-sort": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "license": "MIT" }, "node_modules/jest": { "version": "30.3.0", @@ -9148,22 +9806,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/jest-config/node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-config/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -9186,6 +9828,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/jest-config/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -9202,20 +9851,85 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { @@ -9280,86 +9994,6 @@ "mkdirp": "^3.0.1" } }, - "node_modules/jest-file-snapshot/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-file-snapshot/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-file-snapshot/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-file-snapshot/node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-file-snapshot/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-file-snapshot/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -9396,9 +10030,9 @@ } }, "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -9438,6 +10072,22 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-matcher-utils/node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/jest-message-util": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", @@ -9460,9 +10110,9 @@ } }, "node_modules/jest-message-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -9649,6 +10299,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/jest-runtime/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", @@ -9665,6 +10322,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-snapshot": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", @@ -9698,6 +10372,22 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.3.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/jest-util": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", @@ -9716,26 +10406,10 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -9817,9 +10491,9 @@ } }, "node_modules/joi": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", - "integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==", + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.1.tgz", + "integrity": "sha512-pJkBiPtNo+o0h19LfSvUN46Y5zY+ck99AtHwch9n2HqVLNRgP0ZMyIH8FRMoP+HV8hy/+AG99dXFfwpf83iZfQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9829,7 +10503,7 @@ "@hapi/pinpoint": "^2.0.1", "@hapi/tlds": "^1.1.1", "@hapi/topo": "^6.0.2", - "@standard-schema/spec": "^1.0.0" + "@standard-schema/spec": "^1.1.0" }, "engines": { "node": ">= 20" @@ -9839,6 +10513,7 @@ "version": "4.15.9", "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } @@ -9854,7 +10529,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", @@ -9872,6 +10548,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "license": "MIT", "engines": { "node": ">= 10.16.0" } @@ -9893,7 +10570,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/json-merge-patch": { "version": "1.0.2", @@ -9908,18 +10586,21 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" }, "node_modules/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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json11": { "version": "2.0.2", @@ -9935,6 +10616,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -9946,13 +10628,15 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9982,6 +10666,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", + "license": "MIT", "engines": { "node": "*" } @@ -10012,6 +10697,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "license": "MIT", "optional": true, "dependencies": { "is-promise": "^2.0.0", @@ -10097,6 +10783,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -10123,6 +10810,7 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.3.3.tgz", "integrity": "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg==", + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -10134,6 +10822,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-5.0.5.tgz", "integrity": "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw==", + "license": "MIT", "dependencies": { "@types/ldapjs": "^2.2.2", "bcryptjs": "^2.4.0", @@ -10148,6 +10837,7 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", "engines": { "node": ">=12" } @@ -10157,6 +10847,7 @@ "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-2.3.3.tgz", "integrity": "sha512-75QiiLJV/PQqtpH+HGls44dXweviFwQ6SiIK27EqzKQ5jU/7UFrl2E5nLdQ3IYRBzJ/AVFJI66u0MZ0uofKYwg==", "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", + "license": "MIT", "dependencies": { "abstract-logging": "^2.0.0", "asn1": "^0.2.4", @@ -10196,6 +10887,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -10238,9 +10930,10 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.6", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.6.tgz", - "integrity": "sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==" + "version": "1.12.40", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.40.tgz", + "integrity": "sha512-HKGs7GowShNls3Zh+7DTr6wYpPk5jC78l508yQQY3e8ZgJChM3A9JZghmMJZuK+5bogSfuTafpjksGSR3aMIEg==", + "license": "MIT" }, "node_modules/libqp": { "version": "2.1.1", @@ -10266,7 +10959,8 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/linkify-it": { "version": "5.0.0", @@ -10279,9 +10973,9 @@ } }, "node_modules/liquidjs": { - "version": "10.25.0", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.25.0.tgz", - "integrity": "sha512-XpO7AiGULTG4xcTlwkcTI5JreFG7b6esLCLp+aUSh7YuQErJZEoUXre9u9rbdb0057pfWG4l0VursvLd5Q/eAw==", + "version": "10.25.1", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.25.1.tgz", + "integrity": "sha512-D+jsJvkGigFn8qNUgh8U6XNHhGFBp+p8Dk26ea/Hl+XrjFVSg9OXlN31hGAfS3MYQ3Kr8Xi9sEVBVQa/VTVSmg==", "license": "MIT", "optional": true, "dependencies": { @@ -10303,11 +10997,31 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, "node_modules/loader-runner": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", @@ -10327,6 +11041,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -10346,43 +11061,51 @@ "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" }, "node_modules/lodash.uniq": { "version": "4.5.0", @@ -10396,6 +11119,7 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -10457,14 +11181,15 @@ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mailparser": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.3.tgz", - "integrity": "sha512-AnB0a3zROum6fLaa52L+/K2SoRJVyFDk78Ea6q1D0ofcZLxWEWDtsS1+OrVqKbV7r5dulKL/AwYQccFGAPpuYQ==", + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.5.tgz", + "integrity": "sha512-i5mXdEqDrh7I095uiQiebOrc00AOIJRLlpze1nBb82oZpI4GaCJIn9ypXR2bqb/Ayr7q+nmT6k11yYU5Age/Gg==", "license": "MIT", "optional": true, "dependencies": { @@ -10475,7 +11200,7 @@ "iconv-lite": "0.7.2", "libmime": "5.3.7", "linkify-it": "5.0.0", - "nodemailer": "7.0.13", + "nodemailer": "8.0.3", "punycode.js": "2.3.1", "tlds": "1.261.0" } @@ -10500,13 +11225,15 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -10515,6 +11242,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -10553,6 +11281,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10562,6 +11291,7 @@ "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, + "license": "Unlicense", "dependencies": { "fs-monkey": "^1.0.4" }, @@ -10586,6 +11316,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -10597,13 +11328,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10613,6 +11346,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -10622,10 +11356,11 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -10653,9 +11388,9 @@ } }, "node_modules/migrate-mongo/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "license": "MIT", "engines": { "node": ">=20" @@ -10666,6 +11401,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "devOptional": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -10677,19 +11413,25 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { @@ -10697,6 +11439,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -10724,6 +11467,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10834,31 +11578,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/mjml-cli/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "optional": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/mjml-cli/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -10881,18 +11600,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mjml-cli/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/mjml-cli/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC", - "optional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } + "optional": true }, "node_modules/mjml-cli/node_modules/minimatch": { "version": "9.0.9", @@ -10910,30 +11623,21 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mjml-cli/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mjml-cli/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", + "node_modules/mjml-cli/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "optional": true, "dependencies": { - "picomatch": "^2.2.1" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mjml-column": { @@ -11138,26 +11842,6 @@ "lodash": "^4.17.21" } }, - "node_modules/mjml-parser-xml/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, "node_modules/mjml-preset-core": { "version": "5.0.0-beta.2", "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-5.0.0-beta.2.tgz", @@ -11288,6 +11972,22 @@ "mjml-section": "5.0.0-beta.2" } }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mocha": { "version": "11.7.5", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", @@ -11325,27 +12025,30 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/mocha/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "readdirp": "^4.0.1" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/mocha/node_modules/glob": { @@ -11370,14 +12073,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.7.tgz", - "integrity": "sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -11386,11 +12096,43 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11402,26 +12144,27 @@ } }, "node_modules/mongodb": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", - "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", + "integrity": "sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@mongodb-js/saslprep": "^1.3.0", - "bson": "^6.10.4", - "mongodb-connection-string-url": "^3.0.2" + "bson": "^7.1.1", + "mongodb-connection-string-url": "^7.0.0" }, "engines": { - "node": ">=16.20.1" + "node": ">=20.19.0" }, "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": ">=7.0.0 <7.1.0", "snappy": "^7.3.2", - "socks": "^2.7.1" + "socks": "^2.8.6" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -11448,18 +12191,23 @@ } }, "node_modules/mongodb-connection-string-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", + "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", + "license": "Apache-2.0", + "peer": true, "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^14.1.0 || ^13.0.0" + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" } }, "node_modules/mongoose": { - "version": "8.20.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.20.3.tgz", - "integrity": "sha512-AQk63Ry4YM/lWJRt/D5P7UiRjKT+z+vD0NkNKgeQ35TioBC7kuI6wBzhu6/kyrNXg+WotFidW1icEWLNC1rUfg==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.0.tgz", + "integrity": "sha512-Bul4Ha6J8IqzFrb0B1xpVzkC3S0sk43dmLSnhFOn8eJlZiLwL5WO6cRymmjaADdCMjUcCpj2ce8hZI6O4ZFSug==", "license": "MIT", "dependencies": { "bson": "^6.10.4", @@ -11478,10 +12226,85 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/mongoose/node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/mongoose/node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongoose/node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -11501,7 +12324,8 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/multer": { "version": "2.1.1", @@ -11614,12 +12438,14 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -11627,24 +12453,27 @@ "node_modules/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==", + "license": "MIT" }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "license": "MIT", "optional": true }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", - "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", + "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -11654,6 +12483,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -11664,6 +12494,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "engines": { "node": ">=10.5.0" } @@ -11673,6 +12504,7 @@ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.21" } @@ -11681,6 +12513,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -11709,19 +12542,20 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", "devOptional": true, "license": "MIT" }, "node_modules/nodemailer": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", - "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.3.tgz", + "integrity": "sha512-JQNBqvK+bj3NMhUFR3wmCl3SYcOeMotDiwDBvIoCuQdF0PvlIY0BH+FJ2CG7u4cXKPChplE78oowlH/Otsc4ZQ==", "license": "MIT-0", "engines": { "node": ">=6.0.0" @@ -11732,6 +12566,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11762,10 +12597,47 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11774,6 +12646,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", "engines": { "node": ">= 6" } @@ -11782,6 +12655,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11790,9 +12664,10 @@ } }, "node_modules/oidc-token-hash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", - "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", + "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", + "license": "MIT", "engines": { "node": "^10.13.0 || >=12.0.0" } @@ -11801,6 +12676,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -11821,6 +12697,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -11839,6 +12716,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -11853,6 +12731,7 @@ "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", "optional": true, "dependencies": { "is-docker": "^2.0.0", @@ -11869,6 +12748,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", "dependencies": { "jose": "^4.15.9", "lru-cache": "^6.0.0", @@ -11883,6 +12763,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -11893,13 +12774,15 @@ "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -11917,6 +12800,7 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, + "license": "MIT", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -11935,22 +12819,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-event": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "license": "MIT", "optional": true, "dependencies": { "p-timeout": "^3.1.0" @@ -11966,6 +12839,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -11976,6 +12850,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -11991,6 +12866,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -12005,6 +12881,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", "optional": true, "dependencies": { "p-finally": "^1.0.0" @@ -12027,6 +12904,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "license": "MIT", "optional": true, "dependencies": { "p-timeout": "^3.0.0" @@ -12041,13 +12919,15 @@ "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "devOptional": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -12060,6 +12940,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "devOptional": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -12144,6 +13025,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -12152,6 +13034,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -12169,6 +13052,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", "dependencies": { "jsonwebtoken": "^9.0.0", "passport-strategy": "^1.0.0" @@ -12178,6 +13062,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-3.0.1.tgz", "integrity": "sha512-TRRx3BHi8GC8MfCT9wmghjde/EGeKjll7zqHRRfGRxXbLcaDce2OftbQrFG7/AWaeFhR6zpZHtBQ/IkINdLVjQ==", + "license": "MIT", "dependencies": { "ldapauth-fork": "^5.0.1", "passport-strategy": "^1.0.0" @@ -12210,6 +13095,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -12229,6 +13115,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -12237,36 +13124,42 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT", "optional": true }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "devOptional": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "devOptional": true + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "engines": { - "node": ">=16" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/path-type": { @@ -12274,6 +13167,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -12297,13 +13191,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "devOptional": true + "devOptional": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -12316,6 +13212,7 @@ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -12394,6 +13291,7 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -13028,6 +13926,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -13112,6 +14011,32 @@ "node": ">=14" } }, + "node_modules/preview-email/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/preview-email/node_modules/nodemailer": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", + "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/preview-email/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -13120,6 +14045,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "optional": true, "bin": { "uuid": "dist/bin/uuid" @@ -13129,6 +14055,7 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", "optional": true, "dependencies": { "asap": "~2.0.3" @@ -13138,6 +14065,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -13149,7 +14077,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/pug": { "version": "3.0.4", @@ -13201,12 +14130,14 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "license": "MIT", "optional": true }, "node_modules/pug-filters": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "license": "MIT", "optional": true, "dependencies": { "constantinople": "^4.0.1", @@ -13220,6 +14151,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "license": "MIT", "optional": true, "dependencies": { "character-parser": "^2.2.0", @@ -13231,6 +14163,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "license": "MIT", "optional": true, "dependencies": { "pug-error": "^2.0.0", @@ -13241,6 +14174,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "license": "MIT", "optional": true, "dependencies": { "object-assign": "^4.1.1", @@ -13251,6 +14185,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "license": "MIT", "optional": true, "dependencies": { "pug-error": "^2.0.0", @@ -13268,6 +14203,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "license": "MIT", "optional": true, "dependencies": { "pug-error": "^2.0.0" @@ -13277,12 +14213,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "license": "MIT", "optional": true }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -13315,9 +14253,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -13332,12 +14270,14 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" }, "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13347,6 +14287,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -13355,6 +14296,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13378,6 +14320,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "optional": true, "dependencies": { "deep-extend": "^0.6.0", @@ -13393,6 +14336,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -13420,28 +14364,43 @@ } }, "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">= 14.18.0" + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13450,6 +14409,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13457,15 +14417,17 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", "optional": true, "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -13507,6 +14469,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -13516,6 +14479,7 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -13528,7 +14492,8 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/rimraf": { "version": "6.1.3", @@ -13553,6 +14518,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -13567,12 +14533,14 @@ "node_modules/router/node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" }, "node_modules/run-applescript": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", + "license": "MIT", "optional": true, "dependencies": { "execa": "^0.10.0" @@ -13585,6 +14553,7 @@ "version": "6.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "license": "MIT", "optional": true, "dependencies": { "nice-try": "^1.0.4", @@ -13601,6 +14570,7 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "license": "MIT", "optional": true, "dependencies": { "cross-spawn": "^6.0.0", @@ -13619,6 +14589,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -13628,6 +14599,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -13637,6 +14609,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "license": "MIT", "optional": true, "dependencies": { "path-key": "^2.0.0" @@ -13649,6 +14622,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", "optional": true, "engines": { "node": ">=4" @@ -13658,6 +14632,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -13667,6 +14642,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", "optional": true, "dependencies": { "shebang-regex": "^1.0.0" @@ -13679,6 +14655,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -13688,12 +14665,14 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", "optional": true }, "node_modules/run-applescript/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", "optional": true, "dependencies": { "isexe": "^2.0.0" @@ -13706,6 +14685,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -13727,7 +14707,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-stable-stringify": { "version": "2.5.0", @@ -13741,7 +14722,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/sax": { "version": "1.6.0", @@ -13758,6 +14740,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -13772,10 +14755,11 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -13792,6 +14776,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -13800,12 +14785,20 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/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==", + "license": "BSD-3-Clause" }, "node_modules/seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "license": "MIT" }, "node_modules/selderee": { "version": "0.11.0", @@ -13821,9 +14814,9 @@ } }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -13833,24 +14826,29 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serialize-javascript": { @@ -13858,14 +14856,16 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -13874,18 +14874,24 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "devOptional": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -13898,6 +14904,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -13919,6 +14926,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -13937,6 +14945,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -13952,6 +14961,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -13969,6 +14979,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -13986,13 +14997,15 @@ "node_modules/sift": { "version": "17.1.3", "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", - "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "devOptional": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -14000,21 +15013,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, "node_modules/sinon": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.3.tgz", @@ -14034,9 +15032,9 @@ } }, "node_modules/sinon/node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -14048,6 +15046,7 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -14067,6 +15066,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">= 8" } @@ -14168,18 +15168,14 @@ } }, "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -14194,23 +15190,11 @@ "node": ">=10" } }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14226,6 +15210,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "devOptional": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14235,22 +15220,11 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14258,27 +15232,13 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "devOptional": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "devOptional": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14286,18 +15246,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "devOptional": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -14312,6 +15260,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -14332,6 +15281,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -14362,6 +15312,22 @@ "node": ">=0.8.0" } }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/stylehacks": { "version": "7.0.8", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.8.tgz", @@ -14384,6 +15350,7 @@ "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-0.2.0.tgz", "integrity": "sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==", "dev": true, + "license": "MIT", "dependencies": { "clone-regexp": "^3.0.0", "function-timeout": "^0.1.0", @@ -14436,6 +15403,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -14447,6 +15415,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "optional": true, "engines": { "node": ">= 0.4" @@ -14504,6 +15473,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", "dependencies": { "swagger-ui-dist": ">=5.0.0" }, @@ -14541,9 +15511,9 @@ } }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", "dev": true, "license": "MIT", "engines": { @@ -14555,9 +15525,9 @@ } }, "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", "devOptional": true, "license": "BSD-2-Clause", "dependencies": { @@ -14574,16 +15544,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -14724,7 +15693,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -14753,6 +15722,7 @@ "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", "dev": true, + "license": "MIT", "dependencies": { "convert-hrtime": "^5.0.0" }, @@ -14766,7 +15736,8 @@ "node_modules/tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -14786,9 +15757,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -14812,13 +15783,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "devOptional": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -14830,6 +15803,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -14838,12 +15812,32 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "license": "MIT", "optional": true }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -14856,6 +15850,7 @@ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, + "license": "MIT", "bin": { "tree-kill": "cli.js" } @@ -14893,9 +15888,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -14997,6 +15992,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -15036,10 +16032,11 @@ } }, "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -15049,6 +16046,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, + "license": "MIT", "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", @@ -15063,6 +16061,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", @@ -15078,6 +16077,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15085,13 +16085,15 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -15104,15 +16106,15 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -15125,6 +16127,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -15135,9 +16138,10 @@ } }, "node_modules/typed-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz", - "integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz", + "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -15173,6 +16177,7 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -15185,6 +16190,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", "dependencies": { "@lukeed/csprng": "^1.0.0" }, @@ -15196,6 +16202,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", "dependencies": { "random-bytes": "~1.0.0" }, @@ -15203,11 +16210,24 @@ "node": ">= 0.8" } }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici": { - "version": "6.24.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.0.tgz", - "integrity": "sha512-lVLNosgqo5EkGqh5XUDhGfsMSoO8K0BAN0TyJLvwNRSl4xWGZlCVYsAIpa/OpA3TvmnM01GWcoKmc3ZWo5wKKA==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "license": "MIT", + "optional": true, "engines": { "node": ">=18.17" } @@ -15223,6 +16243,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -15272,9 +16293,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", - "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "devOptional": true, "funding": [ { @@ -15307,6 +16328,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -15315,6 +16337,7 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -15323,12 +16346,14 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -15341,6 +16366,7 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } @@ -15349,7 +16375,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/v8-to-istanbul": { "version": "9.3.0", @@ -15377,9 +16404,9 @@ } }, "node_modules/validator": { - "version": "13.15.23", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", - "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -15389,6 +16416,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15400,6 +16428,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "verror": "1.10.0" } @@ -15407,7 +16436,8 @@ "node_modules/vasync/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" }, "node_modules/vasync/node_modules/verror": { "version": "1.10.0", @@ -15416,6 +16446,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -15426,6 +16457,7 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -15438,7 +16470,8 @@ "node_modules/verror/node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" }, "node_modules/void-elements": { "version": "3.1.0", @@ -15475,14 +16508,15 @@ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } }, "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "dependencies": { @@ -15498,6 +16532,7 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -15519,30 +16554,11 @@ "node": ">=10.0.0" } }, - "node_modules/web-resource-inliner/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -15551,16 +16567,18 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" } }, "node_modules/webpack": { - "version": "5.104.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", - "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -15568,11 +16586,11 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.4", + "enhanced-resolve": "^5.20.0", "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -15584,9 +16602,9 @@ "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -15609,14 +16627,15 @@ "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "dev": true, "license": "MIT", "engines": { @@ -15629,6 +16648,7 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -15646,6 +16666,8 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -15659,6 +16681,8 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -15668,6 +16692,8 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -15677,6 +16703,8 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -15690,6 +16718,7 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -15745,6 +16774,7 @@ "version": "14.2.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" @@ -15758,6 +16788,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "devOptional": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -15772,6 +16803,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", "dependencies": { "string-width": "^4.0.0" }, @@ -15780,13 +16812,13 @@ } }, "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", "license": "MIT", "dependencies": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", @@ -15845,6 +16877,7 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15852,12 +16885,13 @@ "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" }, "node_modules/workerpool": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", - "integrity": "sha512-Xz4Nm9c+LiBHhDR5bDLnNzmj6+5F+cyEAWPMkbs2awq/dYazR/efelZzUAjB/y3kNHL+uzkHvxVVpaOfGCPV7A==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, @@ -15882,6 +16916,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "devOptional": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15894,35 +16929,11 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "5.0.1", @@ -15943,6 +16954,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "devOptional": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -15959,6 +16971,7 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "devOptional": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -15977,6 +16990,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "devOptional": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -15986,6 +17000,7 @@ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -16001,6 +17016,7 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -16010,6 +17026,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, From 3baee8db80fc18b65f14a562bf2c7df9e9cf46be Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 24 Mar 2026 17:02:51 +0100 Subject: [PATCH 44/46] fix for inconsistant mongodb version across different packages --- package-lock.json | 129 +++++++++------------------------------------- package.json | 3 +- 2 files changed, 26 insertions(+), 106 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07c0ce4e0..3034cfa17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4307,11 +4307,10 @@ "license": "MIT" }, "node_modules/@types/whatwg-url": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", - "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/webidl-conversions": "*" } @@ -5907,13 +5906,12 @@ } }, "node_modules/bson": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz", - "integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", - "peer": true, "engines": { - "node": ">=20.19.0" + "node": ">=16.20.1" } }, "node_modules/buffer": { @@ -12144,27 +12142,26 @@ } }, "node_modules/mongodb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.1.1.tgz", - "integrity": "sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@mongodb-js/saslprep": "^1.3.0", - "bson": "^7.1.1", - "mongodb-connection-string-url": "^7.0.0" + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" }, "engines": { - "node": ">=20.19.0" + "node": ">=16.20.1" }, "peerDependencies": { - "@aws-sdk/credential-providers": "^3.806.0", - "@mongodb-js/zstd": "^7.0.0", - "gcp-metadata": "^7.0.1", - "kerberos": "^7.0.0", - "mongodb-client-encryption": ">=7.0.0 <7.1.0", + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.3.2", - "socks": "^2.8.6" + "socks": "^2.7.1" }, "peerDependenciesMeta": { "@aws-sdk/credential-providers": { @@ -12191,17 +12188,13 @@ } }, "node_modules/mongodb-connection-string-url": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz", - "integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", "license": "Apache-2.0", - "peer": true, "dependencies": { - "@types/whatwg-url": "^13.0.0", - "whatwg-url": "^14.1.0" - }, - "engines": { - "node": ">=20.19.0" + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" } }, "node_modules/mongoose": { @@ -12226,80 +12219,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/@types/whatwg-url": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "license": "MIT", - "dependencies": { - "@types/webidl-conversions": "*" - } - }, - "node_modules/mongoose/node_modules/bson": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", - "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", - "license": "Apache-2.0", - "engines": { - "node": ">=16.20.1" - } - }, - "node_modules/mongoose/node_modules/mongodb": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", - "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", - "license": "Apache-2.0", - "dependencies": { - "@mongodb-js/saslprep": "^1.3.0", - "bson": "^6.10.4", - "mongodb-connection-string-url": "^3.0.2" - }, - "engines": { - "node": ">=16.20.1" - }, - "peerDependencies": { - "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", - "gcp-metadata": "^5.2.0", - "kerberos": "^2.0.1", - "mongodb-client-encryption": ">=6.0.0 <7", - "snappy": "^7.3.2", - "socks": "^2.7.1" - }, - "peerDependenciesMeta": { - "@aws-sdk/credential-providers": { - "optional": true - }, - "@mongodb-js/zstd": { - "optional": true - }, - "gcp-metadata": { - "optional": true - }, - "kerberos": { - "optional": true - }, - "mongodb-client-encryption": { - "optional": true - }, - "snappy": { - "optional": true - }, - "socks": { - "optional": true - } - } - }, - "node_modules/mongoose/node_modules/mongodb-connection-string-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", - "license": "Apache-2.0", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^14.1.0 || ^13.0.0" - } - }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", diff --git a/package.json b/package.json index 8dcf35405..68246c718 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,8 @@ }, "socks": { "ip": "^2.0.2" - } + }, + "mongodb": "6.20.0" }, "devDependencies": { "@eslint/eslintrc": "^3.1.0", From 7342efff62641752bcad777f5dd2f62766222771 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Tue, 24 Mar 2026 17:16:22 +0100 Subject: [PATCH 45/46] fix unit test --- src/datasets/datasets.v4.controller.spec.ts | 3 ++- src/policies/policies.service.spec.ts | 2 +- src/published-data/published-data.controller.spec.ts | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/datasets/datasets.v4.controller.spec.ts b/src/datasets/datasets.v4.controller.spec.ts index 2e3f765de..c18e18d03 100644 --- a/src/datasets/datasets.v4.controller.spec.ts +++ b/src/datasets/datasets.v4.controller.spec.ts @@ -28,7 +28,8 @@ describe("DatasetsController", () => { ], }).compile(); - controller = module.get(DatasetsV4Controller); + controller = + await module.resolve(DatasetsV4Controller); }); it("should be defined", () => { diff --git a/src/policies/policies.service.spec.ts b/src/policies/policies.service.spec.ts index 9ca0c1877..0fd9ca384 100644 --- a/src/policies/policies.service.spec.ts +++ b/src/policies/policies.service.spec.ts @@ -56,7 +56,7 @@ describe("PoliciesService", () => { ], }).compile(); - service = module.get(PoliciesService); + service = await module.resolve(PoliciesService); policyModel = module.get>(getModelToken("Policy")); }); diff --git a/src/published-data/published-data.controller.spec.ts b/src/published-data/published-data.controller.spec.ts index b17ec39b3..accf61202 100644 --- a/src/published-data/published-data.controller.spec.ts +++ b/src/published-data/published-data.controller.spec.ts @@ -38,7 +38,9 @@ describe("PublishedDataController", () => { ], }).compile(); - controller = module.get(PublishedDataController); + controller = await module.resolve( + PublishedDataController, + ); }); it("should be defined", () => { From 85fee81e66c5034818665e5c0f38b4c39a1f6059 Mon Sep 17 00:00:00 2001 From: junjiequan Date: Wed, 25 Mar 2026 10:31:18 +0100 Subject: [PATCH 46/46] Use correct PickType for dataset-opensearch dto --- src/opensearch/dto/dataset-opensearch.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opensearch/dto/dataset-opensearch.dto.ts b/src/opensearch/dto/dataset-opensearch.dto.ts index 5c9d2d6ca..d86f668a2 100644 --- a/src/opensearch/dto/dataset-opensearch.dto.ts +++ b/src/opensearch/dto/dataset-opensearch.dto.ts @@ -1,10 +1,10 @@ -import { OmitType } from "@nestjs/swagger"; +import { PickType } from "@nestjs/swagger"; import { Expose } from "class-transformer"; import { OutputDatasetDto } from "src/datasets/dto/output-dataset.dto"; import { DATASET_OPENSEARCH_FIELDS } from "src/opensearch/utils/dataset-opensearch.utils"; -export class DatasetOpenSearchDto extends OmitType( +export class DatasetOpenSearchDto extends PickType( OutputDatasetDto, DATASET_OPENSEARCH_FIELDS, ) {