diff --git a/.vscode/launch.json b/.vscode/launch.json index fcad62b4..a3506b03 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -57,6 +57,23 @@ "/**" ] }, + { + "name": "Google Unit tests", + "cwd": "${workspaceFolder}/storage/google", + "type": "pwa-node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "test:unit" + ], + "outFiles": [ + "${workspaceFolder}/*/*/lib/**/*.js" + ], + "skipFiles": [ + "/**" + ] + }, { "name": "S3 Backend Unit tests", "cwd": "${workspaceFolder}/storage/s3", @@ -128,6 +145,24 @@ "/**" ] }, + { + "name": "Backend Google Integration tests", + "cwd": "${workspaceFolder}/storage/google", + "type": "pwa-node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "test:integration:backend" + ], + "outFiles": [ + "${workspaceFolder}/*/*/lib/**/*.js" + ], + "skipFiles": [ + "/**" + ], + "envFile": "${workspaceFolder}/storage/google/src/test/.env" + }, { "name": "Backend OSS Integration tests", "cwd": "${workspaceFolder}/storage/oss", @@ -197,6 +232,20 @@ "DEBUG": "1" } }, + { + "name": "Frontend Google Integration tests", + "cwd": "${workspaceFolder}/storage/google", + "type": "pwa-node", + "request": "launch", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run", + "test:integration:frontend" + ], + "env": { + "DEBUG": "1" + } + }, { "name": "Frontend OSS Integration tests", "cwd": "${workspaceFolder}/storage/oss", diff --git a/.vscode/settings.json b/.vscode/settings.json index 1c934996..518407db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,8 +23,9 @@ } ], "editor.codeActionsOnSave": { - "source.fixAll.markdownlint": true, // first run the markdown linter - "editor.action.fixAll": true, // Run the default formatter before eslint, it handles whitespace better - "source.fixAll.eslint": true // then run ESLint auto-fix + "source.fixAll.markdownlint": "explicit", + "editor.action.fixAll": "explicit", + "source.fixAll.eslint": "explicit" }, + "cSpell.import": ["${workspaceFolder}/utils/common-config/cspell.json"] } diff --git a/cloud-agnostic/core/package.json b/cloud-agnostic/core/package.json index a22902b9..6b40382a 100644 --- a/cloud-agnostic/core/package.json +++ b/cloud-agnostic/core/package.json @@ -40,9 +40,9 @@ "@types/chai": "^4.3.8", "@types/mocha": "^10.0.1", "chai": "^4.3.10", - "cspell": "^5.18.5", - "cypress": "^13.12.0", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "cypress": "^13.16.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "nyc": "^14.0.0", diff --git a/common/config/azure-pipelines/templates/build-test.yml b/common/config/azure-pipelines/templates/build-test.yml index 21d67aec..2efe3951 100644 --- a/common/config/azure-pipelines/templates/build-test.yml +++ b/common/config/azure-pipelines/templates/build-test.yml @@ -10,6 +10,12 @@ steps: versionSpec: $(nodeVersion) checkLatest: true + - task: DownloadSecureFile@1 + name: DownloadGCPAuthFile + displayName: 'Download GCP Secure Auth File' + inputs: + secureFile: 'object-storage-tests-connection.json' + - script: node common/scripts/install-run-rush.js install --purge displayName: rush install @@ -33,9 +39,11 @@ steps: displayName: rush test:integration:backend env: TEST_AZURE_STORAGE_ACCOUNT_KEY: $(TEST_AZURE_STORAGE_ACCOUNT_KEY) + GOOGLE_APPLICATION_CREDENTIALS: $(DownloadGCPAuthFile.secureFilePath) - script: node common/scripts/install-run-rush.js test:integration:frontend --only tag:integration-tested --verbose displayName: rush test:integration:frontend condition: and(succeeded(), eq(variables.runFrontendIntegrationTests, true)) env: TEST_AZURE_STORAGE_ACCOUNT_KEY: $(TEST_AZURE_STORAGE_ACCOUNT_KEY) + GOOGLE_APPLICATION_CREDENTIALS: $(DownloadGCPAuthFile.secureFilePath) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 23ca4dd4..dca5a599 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -14,9 +14,9 @@ importers: '@types/chai': ^4.3.8 '@types/mocha': ^10.0.1 chai: ^4.3.10 - cspell: ^5.18.5 - cypress: ^13.12.0 - eslint: ^8.42.0 + cspell: ^8.16.0 + cypress: ^13.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 nyc: ^14.0.0 @@ -29,9 +29,9 @@ importers: '@types/chai': 4.3.17 '@types/mocha': 10.0.7 chai: 4.5.0 - cspell: 5.21.2 - cypress: 13.13.2 - eslint: 8.57.0 + cspell: 8.16.0 + cypress: 13.16.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 nyc: 14.1.1 @@ -47,8 +47,8 @@ importers: '@itwin/object-storage-common-config': workspace:* '@itwin/object-storage-core': workspace:* '@types/node': ^18.11.18 - cspell: ^5.18.5 - eslint: ^8.42.0 + cspell: ^8.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 reflect-metadata: ^0.1.13 rimraf: ^2.6.2 @@ -63,8 +63,8 @@ importers: devDependencies: '@itwin/object-storage-common-config': link:../utils/common-config '@types/node': 18.19.44 - cspell: 5.21.2 - eslint: 8.57.0 + cspell: 8.16.0 + eslint: 8.57.1 rimraf: 2.7.1 sort-package-json: 1.57.0 typescript: 5.5.4 @@ -86,10 +86,10 @@ importers: '@types/sinon': ^10.0.11 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - cypress: ^13.12.0 + cspell: ^8.16.0 + cypress: ^13.16.0 dotenv: ^16.0.0 - eslint: ^8.42.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 npm-run-all: ^4.1.5 @@ -100,8 +100,8 @@ importers: sort-package-json: ^1.53.1 typescript: ^5.1.3 wait-on: ^7.2.0 - webpack: ^5.72.0 - webpack-cli: ^4.9.2 + webpack: ^5.96.1 + webpack-cli: ^5.1.4 dependencies: '@azure/core-paging': 1.6.2 '@azure/storage-blob': 12.24.0 @@ -119,10 +119,10 @@ importers: '@types/sinon': 10.0.20 chai: 4.5.0 chai-as-promised: 7.1.2_chai@4.5.0 - cspell: 5.21.2 - cypress: 13.13.2 + cspell: 8.16.0 + cypress: 13.16.0 dotenv: 16.4.5 - eslint: 8.57.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 npm-run-all: 4.1.5 @@ -133,8 +133,8 @@ importers: sort-package-json: 1.57.0 typescript: 5.5.4 wait-on: 7.2.0 - webpack: 5.93.0_webpack-cli@4.10.0 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 ../../storage/core: specifiers: @@ -147,9 +147,9 @@ importers: axios: ^1.7.4 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - cypress: ^13.12.0 - eslint: ^8.42.0 + cspell: ^8.16.0 + cypress: ^13.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 nyc: ^14.0.0 @@ -168,9 +168,9 @@ importers: '@types/node': 18.19.44 chai: 4.5.0 chai-as-promised: 7.1.2_chai@4.5.0 - cspell: 5.21.2 - cypress: 13.13.2 - eslint: 8.57.0 + cspell: 8.16.0 + cypress: 13.16.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 nyc: 14.1.1 @@ -179,6 +179,77 @@ importers: sort-package-json: 1.57.0 typescript: 5.5.4 + ../../storage/google: + specifiers: + '@google-cloud/storage': ^7.12.1 + '@google-cloud/storage-control': ^0.2.0 + '@itwin/cloud-agnostic-core': workspace:* + '@itwin/object-storage-common-config': workspace:* + '@itwin/object-storage-core': workspace:* + '@itwin/object-storage-tests-backend': workspace:* + '@itwin/object-storage-tests-backend-unit': workspace:* + '@itwin/object-storage-tests-frontend': workspace:* + '@types/chai': ^4.3.8 + '@types/chai-as-promised': ^7.1.2 + '@types/mocha': ^10.0.1 + '@types/node': ^18.11.18 + '@types/sinon': ^10.0.11 + axios: ^1.7.4 + chai: ^4.3.10 + chai-as-promised: ^7.1.1 + cspell: ^8.16.0 + cypress: ^13.16.0 + dotenv: ^16.0.0 + eslint: ^8.57.1 + google-auth-library: ^9.14.0 + inversify: ^6.0.1 + mocha: ^10.4.0 + npm-run-all: ^4.1.5 + nyc: ^14.0.0 + reflect-metadata: ^0.1.13 + rimraf: ^2.6.2 + sinon: ^14.0.0 + sort-package-json: ^1.53.1 + typescript: ^5.1.3 + wait-on: ^7.2.0 + webpack: ^5.96.1 + webpack-cli: ^5.1.4 + dependencies: + '@google-cloud/storage': 7.12.1 + '@google-cloud/storage-control': 0.2.0 + '@itwin/cloud-agnostic-core': link:../../cloud-agnostic/core + '@itwin/object-storage-core': link:../core + axios: 1.7.4 + google-auth-library: 9.14.1 + devDependencies: + '@itwin/object-storage-common-config': link:../../utils/common-config + '@itwin/object-storage-tests-backend': link:../../tests/backend-storage + '@itwin/object-storage-tests-backend-unit': link:../../tests/backend-storage-unit + '@itwin/object-storage-tests-frontend': link:../../tests/frontend-storage + '@types/chai': 4.3.17 + '@types/chai-as-promised': 7.1.8 + '@types/mocha': 10.0.7 + '@types/node': 18.19.44 + '@types/sinon': 10.0.20 + chai: 4.5.0 + chai-as-promised: 7.1.2_chai@4.5.0 + cspell: 8.16.0 + cypress: 13.16.0 + dotenv: 16.4.5 + eslint: 8.57.1 + inversify: 6.0.2 + mocha: 10.7.3 + npm-run-all: 4.1.5 + nyc: 14.1.1 + reflect-metadata: 0.1.14 + rimraf: 2.7.1 + sinon: 14.0.2 + sort-package-json: 1.57.0 + typescript: 5.5.4 + wait-on: 7.2.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 + ../../storage/minio: specifiers: '@aws-sdk/client-s3': ^3.347.1 @@ -198,9 +269,9 @@ importers: '@types/sinon': ^10.0.11 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - cypress: ^13.12.0 - eslint: ^8.42.0 + cspell: ^8.16.0 + cypress: ^13.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 minio: ^8.0.1 mocha: ^10.4.0 @@ -212,16 +283,16 @@ importers: sort-package-json: ^1.53.1 typescript: ^5.1.3 wait-on: ^7.2.0 - webpack: ^5.72.0 - webpack-cli: ^4.9.2 + webpack: ^5.96.1 + webpack-cli: ^5.1.4 dependencies: '@aws-sdk/client-s3': 3.629.0 '@itwin/object-storage-core': link:../core '@itwin/object-storage-s3': link:../s3 minio: 8.0.1 devDependencies: - '@cypress/webpack-batteries-included-preprocessor': 3.0.4_f0b57b566764db32ca4ebef893c15e6c - '@cypress/webpack-preprocessor': 5.17.1_webpack@5.93.0 + '@cypress/webpack-batteries-included-preprocessor': 3.0.4_b79580a7bf25015423b3438973cdec4d + '@cypress/webpack-preprocessor': 5.17.1_webpack@5.96.1 '@itwin/cloud-agnostic-core': link:../../cloud-agnostic/core '@itwin/object-storage-common-config': link:../../utils/common-config '@itwin/object-storage-tests-backend': link:../../tests/backend-storage @@ -234,9 +305,9 @@ importers: '@types/sinon': 10.0.20 chai: 4.5.0 chai-as-promised: 7.1.2_chai@4.5.0 - cspell: 5.21.2 - cypress: 13.13.2 - eslint: 8.57.0 + cspell: 8.16.0 + cypress: 13.16.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 npm-run-all: 4.1.5 @@ -247,8 +318,8 @@ importers: sort-package-json: 1.57.0 typescript: 5.5.4 wait-on: 7.2.0 - webpack: 5.93.0_webpack-cli@4.10.0 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 ../../storage/oss: specifiers: @@ -267,9 +338,9 @@ importers: '@types/node': ^18.11.18 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 + cspell: ^8.16.0 dotenv: ^16.0.0 - eslint: ^8.42.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 npm-run-all: ^4.1.5 @@ -279,8 +350,8 @@ importers: sort-package-json: ^1.53.1 typescript: ^5.1.3 wait-on: ^7.2.0 - webpack: ^5.72.0 - webpack-cli: ^4.9.2 + webpack: ^5.96.1 + webpack-cli: ^5.1.4 dependencies: '@alicloud/pop-core': 1.7.13 '@aws-sdk/client-s3': 3.629.0 @@ -298,9 +369,9 @@ importers: '@types/node': 18.19.44 chai: 4.5.0 chai-as-promised: 7.1.2_chai@4.5.0 - cspell: 5.21.2 + cspell: 8.16.0 dotenv: 16.4.5 - eslint: 8.57.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 npm-run-all: 4.1.5 @@ -310,8 +381,8 @@ importers: sort-package-json: 1.57.0 typescript: 5.5.4 wait-on: 7.2.0 - webpack: 5.93.0_webpack-cli@4.10.0 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 ../../storage/s3: specifiers: @@ -334,9 +405,9 @@ importers: '@types/sinon': ^10.0.11 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - cypress: ^13.12.0 - eslint: ^8.42.0 + cspell: ^8.16.0 + cypress: ^13.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 nyc: ^14.0.0 @@ -366,9 +437,9 @@ importers: '@types/sinon': 10.0.20 chai: 4.5.0 chai-as-promised: 7.1.2_chai@4.5.0 - cspell: 5.21.2 - cypress: 13.13.2 - eslint: 8.57.0 + cspell: 8.16.0 + cypress: 13.16.0 + eslint: 8.57.1 inversify: 6.0.2 mocha: 10.7.3 nyc: 14.1.1 @@ -392,8 +463,8 @@ importers: abort-controller: ^3.0.0 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - eslint: ^8.42.0 + cspell: ^8.16.0 + eslint: ^8.57.1 fs-extra: ^10.1.0 inversify: ^6.0.1 mocha: ^10.4.0 @@ -419,8 +490,8 @@ importers: '@types/mocha': 10.0.7 '@types/node': 18.19.44 '@types/yargs': 15.0.19 - cspell: 5.21.2 - eslint: 8.57.0 + cspell: 8.16.0 + eslint: 8.57.1 rimraf: 2.7.1 sort-package-json: 1.57.0 typescript: 5.5.4 @@ -436,8 +507,8 @@ importers: '@types/node': ^18.11.18 chai: ^4.3.10 chai-as-promised: ^7.1.1 - cspell: ^5.18.5 - eslint: ^8.42.0 + cspell: ^8.16.0 + eslint: ^8.57.1 inversify: ^6.0.1 mocha: ^10.4.0 rimraf: ^2.6.2 @@ -456,8 +527,8 @@ importers: '@types/chai-as-promised': 7.1.8 '@types/mocha': 10.0.7 '@types/node': 18.19.44 - cspell: 5.21.2 - eslint: 8.57.0 + cspell: 8.16.0 + eslint: 8.57.1 rimraf: 2.7.1 sort-package-json: 1.57.0 typescript: 5.5.4 @@ -470,10 +541,9 @@ importers: '@types/express': ^4.17.13 '@types/node': ^18.11.18 axios: ^1.7.4 - cross-env: ^7.0.3 - cspell: ^5.18.5 - cypress: ^13.12.0 - eslint: ^8.42.0 + cspell: ^8.16.0 + cypress: ^13.16.0 + eslint: ^8.57.1 express: ^4.18.0 inversify: ^6.0.1 rimraf: ^2.6.2 @@ -482,16 +552,15 @@ importers: dependencies: '@itwin/cloud-agnostic-core': link:../../cloud-agnostic/core '@itwin/object-storage-core': link:../../storage/core - cypress: 13.13.2 + cypress: 13.16.0 inversify: 6.0.2 devDependencies: '@itwin/object-storage-common-config': link:../../utils/common-config '@types/express': 4.17.21 '@types/node': 18.19.44 axios: 1.7.4 - cross-env: 7.0.3 - cspell: 5.21.2 - eslint: 8.57.0 + cspell: 8.16.0 + eslint: 8.57.1 express: 4.19.2 rimraf: 2.7.1 sort-package-json: 1.57.0 @@ -502,8 +571,8 @@ importers: '@rushstack/eslint-patch': ^1.2.0 '@typescript-eslint/eslint-plugin': ^5.59.9 '@typescript-eslint/parser': ^5.59.9 - cspell: ^5.18.5 - eslint: ^8.42.0 + cspell: ^8.16.0 + eslint: ^8.57.1 eslint-config-prettier: ^8.8.0 eslint-import-resolver-node: ^0.3.7 eslint-plugin-deprecation: ^1.4.1 @@ -516,17 +585,17 @@ importers: typescript: ^5.1.3 devDependencies: '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 5.62.0_db7229ad096c0d451e7ee6dacac8a7d5 - '@typescript-eslint/parser': 5.62.0_eslint@8.57.0+typescript@5.5.4 - cspell: 5.21.2 - eslint: 8.57.0 - eslint-config-prettier: 8.10.0_eslint@8.57.0 + '@typescript-eslint/eslint-plugin': 5.62.0_4abfc85da931fcd8785d6be50866f99e + '@typescript-eslint/parser': 5.62.0_eslint@8.57.1+typescript@5.5.4 + cspell: 8.16.0 + eslint: 8.57.1 + eslint-config-prettier: 8.10.0_eslint@8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-plugin-deprecation: 1.5.0_eslint@8.57.0+typescript@5.5.4 - eslint-plugin-import: 2.29.1_eslint@8.57.0 - eslint-plugin-mocha: 10.5.0_eslint@8.57.0 - eslint-plugin-prefer-arrow: 1.2.3_eslint@8.57.0 - eslint-plugin-prettier: 4.2.1_cac2d69d611136ee42e8dba38a7224b1 + eslint-plugin-deprecation: 1.5.0_eslint@8.57.1+typescript@5.5.4 + eslint-plugin-import: 2.29.1_eslint@8.57.1 + eslint-plugin-mocha: 10.5.0_eslint@8.57.1 + eslint-plugin-prefer-arrow: 1.2.3_eslint@8.57.1 + eslint-plugin-prettier: 4.2.1_5f55bbfab3e3b9eaf6bb3d00902ff8bc prettier: 2.8.8 sort-package-json: 1.57.0 typescript: 5.5.4 @@ -2676,224 +2745,356 @@ packages: engines: {node: '>=0.1.90'} optional: true - /@cspell/cspell-bundled-dicts/5.21.2: - resolution: {integrity: sha512-Y5TU6wV/H+RV1VOB32MowiKofBsEZId4x4ReWCyw4KUtJegeljajCfhHwiQaZuvA69E13cJnOMDwi9qozj4kjw==} - engines: {node: '>=12.13.0'} - dependencies: - '@cspell/dict-ada': 2.0.1 - '@cspell/dict-aws': 2.0.0 - '@cspell/dict-bash': 2.0.4 - '@cspell/dict-companies': 2.0.14 - '@cspell/dict-cpp': 3.2.1 - '@cspell/dict-cryptocurrencies': 2.0.0 - '@cspell/dict-csharp': 3.0.1 - '@cspell/dict-css': 2.1.0 - '@cspell/dict-dart': 1.1.1 - '@cspell/dict-django': 2.0.0 - '@cspell/dict-dotnet': 2.0.1 - '@cspell/dict-elixir': 2.0.1 - '@cspell/dict-en_us': 2.3.3 + /@cspell/cspell-bundled-dicts/8.16.0: + resolution: {integrity: sha512-R0Eqq5kTZnmZ0elih5uY3TWjMqqAeMl7ciU7maUs+m1FNjCEdJXtJ9wrQxNgjmXi0tX8cvahZRO3O558tEz/KA==} + engines: {node: '>=18'} + dependencies: + '@cspell/dict-ada': 4.0.5 + '@cspell/dict-al': 1.0.3 + '@cspell/dict-aws': 4.0.7 + '@cspell/dict-bash': 4.1.8 + '@cspell/dict-companies': 3.1.7 + '@cspell/dict-cpp': 6.0.2 + '@cspell/dict-cryptocurrencies': 5.0.3 + '@cspell/dict-csharp': 4.0.5 + '@cspell/dict-css': 4.0.16 + '@cspell/dict-dart': 2.2.4 + '@cspell/dict-django': 4.1.3 + '@cspell/dict-docker': 1.1.11 + '@cspell/dict-dotnet': 5.0.8 + '@cspell/dict-elixir': 4.0.6 + '@cspell/dict-en_us': 4.3.27 + '@cspell/dict-en-common-misspellings': 2.0.7 '@cspell/dict-en-gb': 1.1.33 - '@cspell/dict-filetypes': 2.1.1 - '@cspell/dict-fonts': 2.1.0 - '@cspell/dict-fullstack': 2.0.6 - '@cspell/dict-git': 1.0.1 - '@cspell/dict-golang': 3.0.1 - '@cspell/dict-haskell': 2.0.1 - '@cspell/dict-html': 3.3.2 - '@cspell/dict-html-symbol-entities': 3.0.0 - '@cspell/dict-java': 2.0.0 - '@cspell/dict-latex': 2.0.9 - '@cspell/dict-lorem-ipsum': 2.0.1 - '@cspell/dict-lua': 2.0.0 - '@cspell/dict-node': 2.0.1 - '@cspell/dict-npm': 2.0.5 - '@cspell/dict-php': 2.0.0 - '@cspell/dict-powershell': 2.0.0 - '@cspell/dict-public-licenses': 1.0.6 - '@cspell/dict-python': 3.0.6 - '@cspell/dict-r': 1.0.3 - '@cspell/dict-ruby': 2.0.2 - '@cspell/dict-rust': 2.0.1 - '@cspell/dict-scala': 2.0.0 - '@cspell/dict-software-terms': 2.3.0 - '@cspell/dict-swift': 1.0.3 - '@cspell/dict-typescript': 2.0.2 - '@cspell/dict-vue': 2.0.2 + '@cspell/dict-filetypes': 3.0.8 + '@cspell/dict-flutter': 1.0.3 + '@cspell/dict-fonts': 4.0.3 + '@cspell/dict-fsharp': 1.0.4 + '@cspell/dict-fullstack': 3.2.3 + '@cspell/dict-gaming-terms': 1.0.8 + '@cspell/dict-git': 3.0.3 + '@cspell/dict-golang': 6.0.16 + '@cspell/dict-google': 1.0.4 + '@cspell/dict-haskell': 4.0.4 + '@cspell/dict-html': 4.0.10 + '@cspell/dict-html-symbol-entities': 4.0.3 + '@cspell/dict-java': 5.0.10 + '@cspell/dict-julia': 1.0.4 + '@cspell/dict-k8s': 1.0.9 + '@cspell/dict-latex': 4.0.3 + '@cspell/dict-lorem-ipsum': 4.0.3 + '@cspell/dict-lua': 4.0.6 + '@cspell/dict-makefile': 1.0.3 + '@cspell/dict-markdown': 2.0.7_bcd71202de5f42547f1edf3c878db883 + '@cspell/dict-monkeyc': 1.0.9 + '@cspell/dict-node': 5.0.5 + '@cspell/dict-npm': 5.1.13 + '@cspell/dict-php': 4.0.13 + '@cspell/dict-powershell': 5.0.13 + '@cspell/dict-public-licenses': 2.0.11 + '@cspell/dict-python': 4.2.12 + '@cspell/dict-r': 2.0.4 + '@cspell/dict-ruby': 5.0.7 + '@cspell/dict-rust': 4.0.10 + '@cspell/dict-scala': 5.0.6 + '@cspell/dict-software-terms': 4.1.17 + '@cspell/dict-sql': 2.1.8 + '@cspell/dict-svelte': 1.0.5 + '@cspell/dict-swift': 2.0.4 + '@cspell/dict-terraform': 1.0.6 + '@cspell/dict-typescript': 3.1.11 + '@cspell/dict-vue': 3.0.3 + dev: true + + /@cspell/cspell-json-reporter/8.16.0: + resolution: {integrity: sha512-KLjPK94gA3JNuWy70LeenJ6EL3SFk2ejERKYJ6SVV/cVOKIvVd2qe42yX3/A/DkF2xzuZ2LD4z0sfoqQL1BaqA==} + engines: {node: '>=18'} + dependencies: + '@cspell/cspell-types': 8.16.0 + dev: true + + /@cspell/cspell-pipe/8.16.0: + resolution: {integrity: sha512-WoCgrv/mrtwCY4lhc6vEcqN3AQ7lT6K0NW5ShoSo116U2tRaW0unApIYH4Va8u7T9g3wyspFEceQRR1xD9qb9w==} + engines: {node: '>=18'} + dev: true + + /@cspell/cspell-resolver/8.16.0: + resolution: {integrity: sha512-b+99bph43ptkXlQHgPXSkN/jK6LQHy2zL1Fm9up7+x6Yr64bxAzWzoeqJAPtnrPvFuOrFN0jZasZzKBw8CvrrQ==} + engines: {node: '>=18'} + dependencies: + global-directory: 4.0.1 + dev: true + + /@cspell/cspell-service-bus/8.16.0: + resolution: {integrity: sha512-+fn763JKA4EYCOv+1VShFq015UMEBAFRDr+rlCnesgLE0fv9TSFVLsjOfh9/g6GuGQLCRLUqKztwwuueeErstQ==} + engines: {node: '>=18'} dev: true - /@cspell/cspell-pipe/5.21.2: - resolution: {integrity: sha512-MN1SXeqqurWYNknbUMPHRFyTvURbO53/1Aw3zEoCeVUSiGbD5rrb1N+t0YDbOphWrkkrJAZk82/2ZBJ2USE/vg==} - engines: {node: '>=12.13.0'} + /@cspell/cspell-types/8.16.0: + resolution: {integrity: sha512-bGrIK7p4NVsK+QX/CYWmjax+FkzfSIZaIaoiBESGV5gmwgXDVRMJ3IP6tQVAmTtckOYHCmtT5CZgI8zXWr8dHQ==} + engines: {node: '>=18'} dev: true - /@cspell/cspell-types/5.21.2: - resolution: {integrity: sha512-g2h4qNR6C53IcSM3KR0DZ9gsqp+2FyKD371htJOmSJGmWb4s45QY0hsPr12A2J8/bT+E3uMtHn9KxJeQ7t0SzA==} - engines: {node: '>=12.13.0'} + /@cspell/dict-ada/4.0.5: + resolution: {integrity: sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag==} dev: true - /@cspell/dict-ada/2.0.1: - resolution: {integrity: sha512-vopTJ1oHrrFYV5GU55Sr+AzItR78Uj5YbCaspYABmYKlq4NRrcUAUsr4bWgymDcspMIHO7e7IFcj48OKs1fndA==} + /@cspell/dict-al/1.0.3: + resolution: {integrity: sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw==} dev: true - /@cspell/dict-aws/2.0.0: - resolution: {integrity: sha512-NKz7pDZ7pwj/b33i3f4WLpC1rOOUMmENwYgftxU+giU2YBeKM2wZbMTSEIzsrel56r0UlQYmdIVlP/B4nnVaoQ==} + /@cspell/dict-aws/4.0.7: + resolution: {integrity: sha512-PoaPpa2NXtSkhGIMIKhsJUXB6UbtTt6Ao3x9JdU9kn7fRZkwD4RjHDGqulucIOz7KeEX/dNRafap6oK9xHe4RA==} dev: true - /@cspell/dict-bash/2.0.4: - resolution: {integrity: sha512-uK/ehmp5LYrmRH2Gv3nbvdPswpkybJUn34WYKLpeuYHQktmi+pOI1A9uPdBPnSbMDffSvwQlQohIyKawz+X8Ag==} + /@cspell/dict-bash/4.1.8: + resolution: {integrity: sha512-I2CM2pTNthQwW069lKcrVxchJGMVQBzru2ygsHCwgidXRnJL/NTjAPOFTxN58Jc1bf7THWghfEDyKX/oyfc0yg==} dev: true - /@cspell/dict-companies/2.0.14: - resolution: {integrity: sha512-Sq1X29Z05OZ/UNqTwVhf3/WaqvJQy4/S6gS8qYI5AQRX45gVe8CPhNBLmZOTC6z8m716bfQCxa5rRT9YNSdTZg==} + /@cspell/dict-companies/3.1.7: + resolution: {integrity: sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A==} dev: true - /@cspell/dict-cpp/3.2.1: - resolution: {integrity: sha512-XcmzrKIghqFfrYLLaHtWKOp9rupiuGdc5ODONk+emsq0W5CIc3Abn27IQHwUzxzF+Cm5IfKAIJ5Kpe6hkzm0HQ==} + /@cspell/dict-cpp/6.0.2: + resolution: {integrity: sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg==} dev: true - /@cspell/dict-cryptocurrencies/2.0.0: - resolution: {integrity: sha512-nREysmmfOp7L2YCRAUufQahwD5/Punzb5AZ6eyg4zUamdRWHgBFphb5/9h2flt1vgdUfhc6hZcML21Ci7iXjaA==} + /@cspell/dict-cryptocurrencies/5.0.3: + resolution: {integrity: sha512-bl5q+Mk+T3xOZ12+FG37dB30GDxStza49Rmoax95n37MTLksk9wBo1ICOlPJ6PnDUSyeuv4SIVKgRKMKkJJglA==} dev: true - /@cspell/dict-csharp/3.0.1: - resolution: {integrity: sha512-xkfQu03F388w4sdVQSSjrVMkxAxpTYB2yW7nw0XYtTjl3L/jBgvTr/j1BTjdFbQhdNf10Lg0Ak1kXOjmHodVqA==} + /@cspell/dict-csharp/4.0.5: + resolution: {integrity: sha512-c/sFnNgtRwRJxtC3JHKkyOm+U3/sUrltFeNwml9VsxKBHVmvlg4tk4ar58PdpW9/zTlGUkWi2i85//DN1EsUCA==} dev: true - /@cspell/dict-css/2.1.0: - resolution: {integrity: sha512-glASAELcGhh4Ru0rTQ4G9mTQxSyPwsZOON/5BYflB6Kks8YC8nUvKrtMCoo5W7CPKPfSEa8zUNctFQ1+IUYDHA==} + /@cspell/dict-css/4.0.16: + resolution: {integrity: sha512-70qu7L9z/JR6QLyJPk38fNTKitlIHnfunx0wjpWQUQ8/jGADIhMCrz6hInBjqPNdtGpYm8d1dNFyF8taEkOgrQ==} dev: true - /@cspell/dict-dart/1.1.1: - resolution: {integrity: sha512-XBOCpezXrgFN18kGEwqMpTUGZdw4BjCoJrNOo6qBdcdZySCrEHLwELraLOkcSba2kM4stmTp0t59FkwtP8TKOA==} + /@cspell/dict-dart/2.2.4: + resolution: {integrity: sha512-of/cVuUIZZK/+iqefGln8G3bVpfyN6ZtH+LyLkHMoR5tEj+2vtilGNk9ngwyR8L4lEqbKuzSkOxgfVjsXf5PsQ==} dev: true - /@cspell/dict-django/2.0.0: - resolution: {integrity: sha512-GkJdJv6cmzrKcmq2/oxTXjKF5uv71r4eTqnFmgPbNBW1t+G4VYpzOf0QrVQrhx2RC4DdW5XfcTf+iS0FxHOTmw==} + /@cspell/dict-data-science/2.0.5: + resolution: {integrity: sha512-nNSILXmhSJox9/QoXICPQgm8q5PbiSQP4afpbkBqPi/u/b3K9MbNH5HvOOa6230gxcGdbZ9Argl2hY/U8siBlg==} dev: true - /@cspell/dict-dotnet/2.0.1: - resolution: {integrity: sha512-b1n4crJRW0WZVf9Gp/52j/tDtjYiZ3N81fIyfqPlBrjsh/5AivfA697DYwQ2mr8ngNX7RsqRtYNQjealA1rEnQ==} + /@cspell/dict-django/4.1.3: + resolution: {integrity: sha512-yBspeL3roJlO0a1vKKNaWABURuHdHZ9b1L8d3AukX0AsBy9snSggc8xCavPmSzNfeMDXbH+1lgQiYBd3IW03fg==} dev: true - /@cspell/dict-elixir/2.0.1: - resolution: {integrity: sha512-eTTTxZt1FqGkM780yFDxsGHvTbWqvlK8YISSccK8FyrB6ULW+uflQlNS5AnWg3uWKC48b7pQott+odYCsPJ+Ow==} + /@cspell/dict-docker/1.1.11: + resolution: {integrity: sha512-s0Yhb16/R+UT1y727ekbR/itWQF3Qz275DR1ahOa66wYtPjHUXmhM3B/LT3aPaX+hD6AWmK23v57SuyfYHUjsw==} + dev: true + + /@cspell/dict-dotnet/5.0.8: + resolution: {integrity: sha512-MD8CmMgMEdJAIPl2Py3iqrx3B708MbCIXAuOeZ0Mzzb8YmLmiisY7QEYSZPg08D7xuwARycP0Ki+bb0GAkFSqg==} + dev: true + + /@cspell/dict-elixir/4.0.6: + resolution: {integrity: sha512-TfqSTxMHZ2jhiqnXlVKM0bUADtCvwKQv2XZL/DI0rx3doG8mEMS8SGPOmiyyGkHpR/pGOq18AFH3BEm4lViHIw==} + dev: true + + /@cspell/dict-en-common-misspellings/2.0.7: + resolution: {integrity: sha512-qNFo3G4wyabcwnM+hDrMYKN9vNVg/k9QkhqSlSst6pULjdvPyPs1mqz1689xO/v9t8e6sR4IKc3CgUXDMTYOpA==} dev: true /@cspell/dict-en-gb/1.1.33: resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} dev: true - /@cspell/dict-en_us/2.3.3: - resolution: {integrity: sha512-csyKeaNktfpvMkmE2GOPTwsrQm3wWhLKVaDRaGU0qTcIjDiCvqv/iYgrVrKRkoddA3kdNTZ8YNCcix7lb6VkOg==} + /@cspell/dict-en_us/4.3.27: + resolution: {integrity: sha512-7JYHahRWpi0VykWFTSM03KL/0fs6YtYfpOaTAg4N/d0wB2GfwVG/FJ/SBCjD4LBc6Rx9dzdo95Hs4BB8GPQbOA==} + dev: true + + /@cspell/dict-filetypes/3.0.8: + resolution: {integrity: sha512-D3N8sm/iptzfVwsib/jvpX+K/++rM8SRpLDFUaM4jxm8EyGmSIYRbKZvdIv5BkAWmMlTWoRqlLn7Yb1b11jKJg==} + dev: true + + /@cspell/dict-flutter/1.0.3: + resolution: {integrity: sha512-52C9aUEU22ptpgYh6gQyIdA4MP6NPwzbEqndfgPh3Sra191/kgs7CVqXiO1qbtZa9gnYHUoVApkoxRE7mrXHfg==} + dev: true + + /@cspell/dict-fonts/4.0.3: + resolution: {integrity: sha512-sPd17kV5qgYXLteuHFPn5mbp/oCHKgitNfsZLFC3W2fWEgZlhg4hK+UGig3KzrYhhvQ8wBnmZrAQm0TFKCKzsA==} + dev: true + + /@cspell/dict-fsharp/1.0.4: + resolution: {integrity: sha512-G5wk0o1qyHUNi9nVgdE1h5wl5ylq7pcBjX8vhjHcO4XBq20D5eMoXjwqMo/+szKAqzJ+WV3BgAL50akLKrT9Rw==} + dev: true + + /@cspell/dict-fullstack/3.2.3: + resolution: {integrity: sha512-62PbndIyQPH11mAv0PyiyT0vbwD0AXEocPpHlCHzfb5v9SspzCCbzQ/LIBiFmyRa+q5LMW35CnSVu6OXdT+LKg==} + dev: true + + /@cspell/dict-gaming-terms/1.0.8: + resolution: {integrity: sha512-7OL0zTl93WFWhhtpXFrtm9uZXItC3ncAs8d0iQDMMFVNU1rBr6raBNxJskxE5wx2Ant12fgI66ZGVagXfN+yfA==} + dev: true + + /@cspell/dict-git/3.0.3: + resolution: {integrity: sha512-LSxB+psZ0qoj83GkyjeEH/ZViyVsGEF/A6BAo8Nqc0w0HjD2qX/QR4sfA6JHUgQ3Yi/ccxdK7xNIo67L2ScW5A==} + dev: true + + /@cspell/dict-golang/6.0.16: + resolution: {integrity: sha512-hZOBlgcguv2Hdc93n2zjdAQm1j3grsN9T9WhPnQ1wh2vUDoCLEujg+6gWhjcLb8ECOcwZTWgNyQLWeOxEsAj/w==} + dev: true + + /@cspell/dict-google/1.0.4: + resolution: {integrity: sha512-JThUT9eiguCja1mHHLwYESgxkhk17Gv7P3b1S7ZJzXw86QyVHPrbpVoMpozHk0C9o+Ym764B7gZGKmw9uMGduQ==} + dev: true + + /@cspell/dict-haskell/4.0.4: + resolution: {integrity: sha512-EwQsedEEnND/vY6tqRfg9y7tsnZdxNqOxLXSXTsFA6JRhUlr8Qs88iUUAfsUzWc4nNmmzQH2UbtT25ooG9x4nA==} + dev: true + + /@cspell/dict-html-symbol-entities/4.0.3: + resolution: {integrity: sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==} + dev: true + + /@cspell/dict-html/4.0.10: + resolution: {integrity: sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g==} + dev: true + + /@cspell/dict-java/5.0.10: + resolution: {integrity: sha512-pVNcOnmoGiNL8GSVq4WbX/Vs2FGS0Nej+1aEeGuUY9CU14X8yAVCG+oih5ZoLt1jaR8YfR8byUF8wdp4qG4XIw==} dev: true - /@cspell/dict-filetypes/2.1.1: - resolution: {integrity: sha512-Oo0/mUbFHzsaATqRLdkV1RMoYns3aGzeKFIpVJg415GYtJ8EABXtEArYTXeMwlboyGTPvEk+PR2hBSTSfQTqmg==} + /@cspell/dict-julia/1.0.4: + resolution: {integrity: sha512-bFVgNX35MD3kZRbXbJVzdnN7OuEqmQXGpdOi9jzB40TSgBTlJWA4nxeAKV4CPCZxNRUGnLH0p05T/AD7Aom9/w==} dev: true - /@cspell/dict-fonts/2.1.0: - resolution: {integrity: sha512-hk7xsbfWEUhc136Xj7I2TD7ouKAfWwzCVAQaHBxcVXAsVxu7bDOGj4FvE2jBzlkSUY8A9Ww8qS0GOFvowJshVg==} + /@cspell/dict-k8s/1.0.9: + resolution: {integrity: sha512-Q7GELSQIzo+BERl2ya/nBEnZeQC+zJP19SN1pI6gqDYraM51uYJacbbcWLYYO2Y+5joDjNt/sd/lJtLaQwoSlA==} dev: true - /@cspell/dict-fullstack/2.0.6: - resolution: {integrity: sha512-R2E2xvbHvvRwwurxfpBJDRIJjXBMfEPF5WNV3LTOEMRqkZtoYCeJK9aqc8LHlmJMtAbnN1cx//BCDIyTJ0rO0A==} + /@cspell/dict-latex/4.0.3: + resolution: {integrity: sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==} dev: true - /@cspell/dict-git/1.0.1: - resolution: {integrity: sha512-Rk+eTof/9inF11lvxmkCRK+gODatA3qai8kSASv6OG/JfPvpj7fTHErx/rdgPw/LOTDUafnoTjTYmj7B2MOQXg==} + /@cspell/dict-lorem-ipsum/4.0.3: + resolution: {integrity: sha512-WFpDi/PDYHXft6p0eCXuYnn7mzMEQLVeqpO+wHSUd+kz5ADusZ4cpslAA4wUZJstF1/1kMCQCZM6HLZic9bT8A==} dev: true - /@cspell/dict-golang/3.0.1: - resolution: {integrity: sha512-0KNfXTbxHW2l8iVjxeOf+KFv9Qrw3z5cyKnkuYJWlBTSB5KcUBfeKCb4fsds26VdANqiy6U91b4gDx5kNEmBjQ==} + /@cspell/dict-lua/4.0.6: + resolution: {integrity: sha512-Jwvh1jmAd9b+SP9e1GkS2ACbqKKRo9E1f9GdjF/ijmooZuHU0hPyqvnhZzUAxO1egbnNjxS/J2T6iUtjAUK2KQ==} dev: true - /@cspell/dict-haskell/2.0.1: - resolution: {integrity: sha512-ooA23qIG7InOOxlLm67CNH5O2J85QsPHEAzEU9KEqVfYG5ovFs5tx6n9pHekDVk3MpQULpqfNUYDR0KigPLg5g==} + /@cspell/dict-makefile/1.0.3: + resolution: {integrity: sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw==} dev: true - /@cspell/dict-html-symbol-entities/3.0.0: - resolution: {integrity: sha512-04K7cPTcbYXmHICfiob4gZA1yaj4hpfM+Nl5WIJ1EAZsSGHdqmGEF28GuCjyQ8ZeKiJAsPt/vXuLBbjxkHqZyQ==} + /@cspell/dict-markdown/2.0.7_bcd71202de5f42547f1edf3c878db883: + resolution: {integrity: sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg==} + peerDependencies: + '@cspell/dict-css': ^4.0.16 + '@cspell/dict-html': ^4.0.10 + '@cspell/dict-html-symbol-entities': ^4.0.3 + '@cspell/dict-typescript': ^3.1.11 + dependencies: + '@cspell/dict-css': 4.0.16 + '@cspell/dict-html': 4.0.10 + '@cspell/dict-html-symbol-entities': 4.0.3 + '@cspell/dict-typescript': 3.1.11 + dev: true + + /@cspell/dict-monkeyc/1.0.9: + resolution: {integrity: sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA==} + dev: true + + /@cspell/dict-node/5.0.5: + resolution: {integrity: sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA==} + dev: true + + /@cspell/dict-npm/5.1.13: + resolution: {integrity: sha512-7S1Pwq16M4sqvv/op7iHErc6Diz+DXsBYRMS0dDj6HUS44VXMvgejXa3RMd5jwBmcHzkInFm3DW1eb2exBs0cg==} dev: true - /@cspell/dict-html/3.3.2: - resolution: {integrity: sha512-cM5pQSEiqjrdk6cRFLrlLdWNT/J8399f/A6DjwjfYhHrGy0e/Rsjv76HZT0GlE1OqMoq9eG9jdQsfoYYgWTIpQ==} + /@cspell/dict-php/4.0.13: + resolution: {integrity: sha512-P6sREMZkhElzz/HhXAjahnICYIqB/HSGp1EhZh+Y6IhvC15AzgtDP8B8VYCIsQof6rPF1SQrFwunxOv8H1e2eg==} dev: true - /@cspell/dict-java/2.0.0: - resolution: {integrity: sha512-9f5LDATlAiXRGqxLxgqbOLlQxuMW2zcN7tBgxwtN+4u90vM03ZUOR/gKIuDV/y0ZuAiWBIjA73cjk8DJ13Q1eA==} + /@cspell/dict-powershell/5.0.13: + resolution: {integrity: sha512-0qdj0XZIPmb77nRTynKidRJKTU0Fl+10jyLbAhFTuBWKMypVY06EaYFnwhsgsws/7nNX8MTEQuewbl9bWFAbsg==} dev: true - /@cspell/dict-latex/2.0.9: - resolution: {integrity: sha512-d1kTK6dJb5z6UcfASQWjqQlsjZvnoVOvMWxYtLpGksYf6gM4IgqoPVNMLYYK6xBS4T/uAnLIj975A6YuAeyZpg==} + /@cspell/dict-public-licenses/2.0.11: + resolution: {integrity: sha512-rR5KjRUSnVKdfs5G+gJ4oIvQvm8+NJ6cHWY2N+GE69/FSGWDOPHxulCzeGnQU/c6WWZMSimG9o49i9r//lUQyA==} dev: true - /@cspell/dict-lorem-ipsum/2.0.1: - resolution: {integrity: sha512-s7Ft8UiloUJwgz4z8uLeFvCkeTcZ43HQl7mSAlZd76eW+keLSsdeGmLDx2zaciqo+MftPGyzygVCwaJjTGxiew==} + /@cspell/dict-python/4.2.12: + resolution: {integrity: sha512-U25eOFu+RE0aEcF2AsxZmq3Lic7y9zspJ9SzjrC0mfJz+yr3YmSCw4E0blMD3mZoNcf7H/vMshuKIY5AY36U+Q==} + dependencies: + '@cspell/dict-data-science': 2.0.5 dev: true - /@cspell/dict-lua/2.0.0: - resolution: {integrity: sha512-7WUEBEspSKtsq104WdIys1+DLqAxpJPzw74Py1TuE3fI5GvlzeSZkRFP2ya54GB2lCO4C3mq4M8EnitpibVDfw==} + /@cspell/dict-r/2.0.4: + resolution: {integrity: sha512-cBpRsE/U0d9BRhiNRMLMH1PpWgw+N+1A2jumgt1if9nBGmQw4MUpg2u9I0xlFVhstTIdzXiLXMxP45cABuiUeQ==} dev: true - /@cspell/dict-node/2.0.1: - resolution: {integrity: sha512-ztBWzhvI+YaMehICSJ65cohhjQqoztxf9vrS3YckOiVGBFvUMaFVNdX9klQkvrLcS/O4+2PzoGeIEkmf99amLA==} + /@cspell/dict-ruby/5.0.7: + resolution: {integrity: sha512-4/d0hcoPzi5Alk0FmcyqlzFW9lQnZh9j07MJzPcyVO62nYJJAGKaPZL2o4qHeCS/od/ctJC5AHRdoUm0ktsw6Q==} dev: true - /@cspell/dict-npm/2.0.5: - resolution: {integrity: sha512-KuPL5fKaqyG9ACrrinNt84FhVdh23VRtxDLO8MtGUdStca9tjfjPdmP2YF/5VkEKmpKYkfFKVcBUk9RgVkx5bw==} + /@cspell/dict-rust/4.0.10: + resolution: {integrity: sha512-6o5C8566VGTTctgcwfF3Iy7314W0oMlFFSQOadQ0OEdJ9Z9ERX/PDimrzP3LGuOrvhtEFoK8pj+BLnunNwRNrw==} dev: true - /@cspell/dict-php/2.0.0: - resolution: {integrity: sha512-29WgU77eTO985LvMHwPi1pcpfopfCWfTdffDyqya0JIfOSaFUrlYKzGPkE4mRxcz2G3hXsaM0SRvBNdIRwEdUg==} + /@cspell/dict-scala/5.0.6: + resolution: {integrity: sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww==} dev: true - /@cspell/dict-powershell/2.0.0: - resolution: {integrity: sha512-6uvEhLiGmG3u9TFkM1TYcky6aL9Yk7Sk3KJwoTYBaQJY2KqrprgyQtW6yxIw9oU52VRHlq3KKvSAA9Q26+SIkQ==} + /@cspell/dict-software-terms/4.1.17: + resolution: {integrity: sha512-QORIk1R5DV8oOQ+oAlUWE7UomaJwUucqu2srrc2+PmkoI6R1fJwwg2uHCPBWlIb4PGDNEdXLv9BAD13H+0wytQ==} dev: true - /@cspell/dict-public-licenses/1.0.6: - resolution: {integrity: sha512-Z9IUFPkkOpOsEdgPUfQOJNQ+qU6+iBAZWS/CR5sUqTX+s5VkPNVwQyVC2kdmgmE2U5qwzAPewG6nVKr2MVogwg==} + /@cspell/dict-sql/2.1.8: + resolution: {integrity: sha512-dJRE4JV1qmXTbbGm6WIcg1knmR6K5RXnQxF4XHs5HA3LAjc/zf77F95i5LC+guOGppVF6Hdl66S2UyxT+SAF3A==} dev: true - /@cspell/dict-python/3.0.6: - resolution: {integrity: sha512-tzxJ4sd9ZGhAUKg/WJJpQGDNtoHvM8Wn+iS2+PnQj2/LTHBW4mnaCogsGsBtYu8C4b2+BEQs+tc5808AeEfLug==} + /@cspell/dict-svelte/1.0.5: + resolution: {integrity: sha512-sseHlcXOqWE4Ner9sg8KsjxwSJ2yssoJNqFHR9liWVbDV+m7kBiUtn2EB690TihzVsEmDr/0Yxrbb5Bniz70mA==} dev: true - /@cspell/dict-r/1.0.3: - resolution: {integrity: sha512-u2qeXd4cx/TvTVcmkvA+sK6f4K1uMAMO6QPMSr1pSvqGElPRP1mIBXmuiSuBzLO3LbsJuUEHw5Cp3/bxIB6rNA==} + /@cspell/dict-swift/2.0.4: + resolution: {integrity: sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw==} dev: true - /@cspell/dict-ruby/2.0.2: - resolution: {integrity: sha512-vVnUpSmGDbPjs7MHq741DsLHhQcoA4CnUCM9wsTorQ9AQRDAkDTbK/LcY8nM19MoXCb3eF8PFku5Jq+gqH0u7w==} + /@cspell/dict-terraform/1.0.6: + resolution: {integrity: sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg==} dev: true - /@cspell/dict-rust/2.0.1: - resolution: {integrity: sha512-ATDpIh0VWpQdUIZa8zqqJY4wQz3q00BTXlQCodeOmObYSb23+L6KWWzJ8mKLgpbc1lqTkogWrqxiCxlrCmqNmg==} + /@cspell/dict-typescript/3.1.11: + resolution: {integrity: sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ==} dev: true - /@cspell/dict-scala/2.0.0: - resolution: {integrity: sha512-MUwA2YKpqaQOSR4V1/CVGRNk8Ii5kf6I8Ch+4/BhRZRQXuwWbi21rDRYWPqdQWps7VNzAbbMA+PQDWsD5YY38g==} + /@cspell/dict-vue/3.0.3: + resolution: {integrity: sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA==} dev: true - /@cspell/dict-software-terms/2.3.0: - resolution: {integrity: sha512-rl+quUw68IxjWgeX/QDMgQsImZ1DaKzFyYMSGrCNcNPp4b4SMLwHCKoJ97/uOnUnw0jaBxueXoqp2iyN/QiOVw==} + /@cspell/dynamic-import/8.16.0: + resolution: {integrity: sha512-FH+B5y71qfunagXiLSJhXP9h/Vwb1Z8Cc/hLmliGekw/Y8BuYknL86tMg9grXBYNmM0kifIv6ZesQl8Km/p/rA==} + engines: {node: '>=18.0'} + dependencies: + import-meta-resolve: 4.1.0 dev: true - /@cspell/dict-swift/1.0.3: - resolution: {integrity: sha512-yOBLSaRD0AnkkkndJ8PuB82Evp6lA2xItf2AWsnPfCCgxp5Ojk6uUBC/WQBSkzkCAOGbXyHsu9D97tsOx2c6cw==} + /@cspell/filetypes/8.16.0: + resolution: {integrity: sha512-u2Ub0uSwXFPJFvXhAO/0FZBj3sMr4CeYCiQwTUsdFRkRMFpbTc7Vf+a+aC2vIj6WcaWrYXrJy3NZF/yjqF6SGw==} + engines: {node: '>=18'} dev: true - /@cspell/dict-typescript/2.0.2: - resolution: {integrity: sha512-OIoSJsCw9WHX4eDikoF5/0QbptMPZjElOcMYdYCyV03nqV5n4ot72ysTexW95yW4+fQU6uDPNQvnrUnhXXEkTA==} + /@cspell/strong-weak-map/8.16.0: + resolution: {integrity: sha512-R6N12wEIQpBk2uyni/FU1SFSIjP0uql7ynXVcF1ob8/JJeRoikssydi9Xq5J6ghMw+X50u35mFvg9BgWKz0d+g==} + engines: {node: '>=18'} dev: true - /@cspell/dict-vue/2.0.2: - resolution: {integrity: sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g==} + /@cspell/url/8.16.0: + resolution: {integrity: sha512-zW+6hAieD/FjysfjY4mVv7iHWWasBP3ldj6L+xy2p4Kuax1nug7uuJqMHlAVude/OywNwENG0rYaP/P9Pg4O+w==} + engines: {node: '>=18.0'} dev: true - /@cypress/request/3.0.1: - resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==} + /@cypress/request/3.0.6: + resolution: {integrity: sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==} engines: {node: '>= 6'} dependencies: aws-sign2: 0.7.0 @@ -2902,20 +3103,20 @@ packages: combined-stream: 1.0.8 extend: 3.0.2 forever-agent: 0.6.1 - form-data: 2.3.3 - http-signature: 1.3.6 + form-data: 4.0.0 + http-signature: 1.4.0 is-typedarray: 1.0.0 isstream: 0.1.2 json-stringify-safe: 5.0.1 mime-types: 2.1.35 performance-now: 2.1.0 - qs: 6.10.4 + qs: 6.13.0 safe-buffer: 5.2.1 - tough-cookie: 4.1.4 + tough-cookie: 5.0.0 tunnel-agent: 0.6.0 uuid: 8.3.2 - /@cypress/webpack-batteries-included-preprocessor/3.0.4_f0b57b566764db32ca4ebef893c15e6c: + /@cypress/webpack-batteries-included-preprocessor/3.0.4_b79580a7bf25015423b3438973cdec4d: resolution: {integrity: sha512-bqmBqYMDys21txkxlXwxQcOnheMHRVmPzNqQqFvvxVgkoDXybNgRBrOpWE64efXX8icWIYOKJIuxzmi5Sjj6QA==} peerDependencies: '@cypress/webpack-preprocessor': ^5.4.4 @@ -2927,13 +3128,13 @@ packages: '@babel/preset-env': 7.25.3_@babel+core@7.25.2 '@babel/preset-react': 7.24.7_@babel+core@7.25.2 '@babel/runtime': 7.25.0 - '@cypress/webpack-preprocessor': 5.17.1_webpack@5.93.0 + '@cypress/webpack-preprocessor': 5.17.1_webpack@5.96.1 assert: 2.1.0 - babel-loader: 9.1.3_fb5444ab5673dfbe6bc1a45ea6b1bf7e + babel-loader: 9.1.3_5a631cb2df108081ef411545931b56d4 babel-plugin-add-module-exports: 1.0.4 browserify-zlib: 0.2.0 buffer: 6.0.3 - coffee-loader: 4.0.0_83eb9aab725b551aa4d62a0ffc077ce4 + coffee-loader: 4.0.0_2236fbf543816b23597cb8761490b47a coffeescript: 2.6.0 constants-browserify: 1.0.0 crypto-browserify: 3.12.0 @@ -2952,14 +3153,14 @@ packages: stream-http: 3.2.0 string_decoder: 1.3.0 timers-browserify: 2.0.12 - ts-loader: 9.4.4_typescript@5.5.4+webpack@5.93.0 + ts-loader: 9.4.4_typescript@5.5.4+webpack@5.96.1 tsconfig-aliased-for-wbip: /tsconfig/7.0.0 tsconfig-paths-webpack-plugin: 3.5.2 tty-browserify: 0.0.1 url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.93.0_webpack-cli@4.10.0 + webpack: 5.96.1_webpack-cli@5.1.4 transitivePeerDependencies: - '@swc/core' - esbuild @@ -2983,11 +3184,11 @@ packages: '@babel/runtime': 7.25.0 '@cypress/webpack-preprocessor': 5.17.1 assert: 2.1.0 - babel-loader: 9.1.3_fb5444ab5673dfbe6bc1a45ea6b1bf7e + babel-loader: 9.1.3_5a631cb2df108081ef411545931b56d4 babel-plugin-add-module-exports: 1.0.4 browserify-zlib: 0.2.0 buffer: 6.0.3 - coffee-loader: 4.0.0_83eb9aab725b551aa4d62a0ffc077ce4 + coffee-loader: 4.0.0_2236fbf543816b23597cb8761490b47a coffeescript: 2.6.0 constants-browserify: 1.0.0 crypto-browserify: 3.12.0 @@ -3006,14 +3207,14 @@ packages: stream-http: 3.2.0 string_decoder: 1.3.0 timers-browserify: 2.0.12 - ts-loader: 9.4.4_typescript@5.5.4+webpack@5.93.0 + ts-loader: 9.4.4_typescript@5.5.4+webpack@5.96.1 tsconfig-aliased-for-wbip: /tsconfig/7.0.0 tsconfig-paths-webpack-plugin: 3.5.2 tty-browserify: 0.0.1 url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.93.0 + webpack: 5.96.1 transitivePeerDependencies: - '@swc/core' - esbuild @@ -3038,7 +3239,7 @@ packages: - supports-color dev: true - /@cypress/webpack-preprocessor/5.17.1_webpack@5.93.0: + /@cypress/webpack-preprocessor/5.17.1_webpack@5.96.1: resolution: {integrity: sha512-FE/e8ikPc8z4EVopJCaior3RGy0jd2q9Xcp5NtiwNG4XnLfEnUFTZlAGwXe75sEh4fNMPrBJW1KIz77PX5vGAw==} peerDependencies: '@babel/core': ^7.0.1 @@ -3049,7 +3250,7 @@ packages: bluebird: 3.7.1 debug: 4.3.6 lodash: 4.17.21 - webpack: 5.93.0_webpack-cli@4.10.0 + webpack: 5.96.1_webpack-cli@5.1.4 transitivePeerDependencies: - supports-color dev: true @@ -3065,13 +3266,13 @@ packages: engines: {node: '>=10.0.0'} dev: true - /@eslint-community/eslint-utils/4.4.0_eslint@8.57.0: + /@eslint-community/eslint-utils/4.4.0_eslint@8.57.1: resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.57.0 + eslint: 8.57.1 eslint-visitor-keys: 3.4.3 dev: true @@ -3080,6 +3281,11 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true + /@eslint-community/regexpp/4.12.1: + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + /@eslint/eslintrc/2.1.4: resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3097,11 +3303,82 @@ packages: - supports-color dev: true - /@eslint/js/8.57.0: - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + /@eslint/js/8.57.1: + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@google-cloud/paginator/5.0.2: + resolution: {integrity: sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==} + engines: {node: '>=14.0.0'} + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + dev: false + + /@google-cloud/projectify/4.0.0: + resolution: {integrity: sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==} + engines: {node: '>=14.0.0'} + dev: false + + /@google-cloud/promisify/4.0.0: + resolution: {integrity: sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==} + engines: {node: '>=14'} + dev: false + + /@google-cloud/storage-control/0.2.0: + resolution: {integrity: sha512-YjtzJhSoDmMPD0p5rz9zC3MI5xrhG8oggFuP0p76AM5EhQsZl3KpMgtcMN9TkqUpaqP2rUBxe6xDCr/vec9wIA==} + engines: {node: '>=14.0.0'} + dependencies: + google-gax: 4.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@google-cloud/storage/7.12.1: + resolution: {integrity: sha512-Z3ZzOnF3YKLuvpkvF+TjQ6lztxcAyTILp+FjKonmVpEwPa9vFvxpZjubLR4sB6bf19i/8HL2AXRjA0YFgHFRmQ==} + engines: {node: '>=14'} + dependencies: + '@google-cloud/paginator': 5.0.2 + '@google-cloud/projectify': 4.0.0 + '@google-cloud/promisify': 4.0.0 + abort-controller: 3.0.0 + async-retry: 1.3.3 + duplexify: 4.1.3 + fast-xml-parser: 4.4.1 + gaxios: 6.7.1 + google-auth-library: 9.14.1 + html-entities: 2.5.2 + mime: 3.0.0 + p-limit: 3.1.0 + retry-request: 7.0.2 + teeny-request: 9.0.0 + uuid: 8.3.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@grpc/grpc-js/1.11.1: + resolution: {integrity: sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==} + engines: {node: '>=12.10.0'} + dependencies: + '@grpc/proto-loader': 0.7.13 + '@js-sdsl/ordered-map': 4.4.2 + dev: false + + /@grpc/proto-loader/0.7.13: + resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} + engines: {node: '>=6'} + hasBin: true + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.4.0 + yargs: 17.7.2 + dev: false + /@hapi/hoek/9.3.0: resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} dev: true @@ -3112,8 +3389,8 @@ packages: '@hapi/hoek': 9.3.0 dev: true - /@humanwhocodes/config-array/0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + /@humanwhocodes/config-array/0.13.0: + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} deprecated: Use @eslint/config-array instead dependencies: @@ -3171,6 +3448,10 @@ packages: '@jridgewell/sourcemap-codec': 1.5.0 dev: true + /@js-sdsl/ordered-map/4.4.2: + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + dev: false + /@nodelib/fs.scandir/2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3192,6 +3473,49 @@ packages: fastq: 1.17.1 dev: true + /@protobufjs/aspromise/1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64/1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen/2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter/1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch/1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float/1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire/1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path/1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool/1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8/1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + /@rushstack/eslint-patch/1.10.4: resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} dev: true @@ -3745,6 +4069,11 @@ packages: tslib: 2.6.3 dev: false + /@tootallnate/once/2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: false + /@types/body-parser/1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -3752,6 +4081,10 @@ packages: '@types/node': 18.19.44 dev: true + /@types/caseless/0.12.5: + resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} + dev: false + /@types/chai-as-promised/7.1.8: resolution: {integrity: sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==} dependencies: @@ -3772,18 +4105,18 @@ packages: resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} dependencies: '@types/eslint': 9.6.0 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 dev: true /@types/eslint/9.6.0: resolution: {integrity: sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==} dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 dev: true - /@types/estree/1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + /@types/estree/1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} dev: true /@types/express-serve-static-core/4.19.5: @@ -3829,6 +4162,10 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/long/4.0.2: + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + dev: false + /@types/mime/1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true @@ -3852,10 +4189,6 @@ packages: undici-types: 5.26.5 dev: false - /@types/parse-json/4.0.2: - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - dev: true - /@types/qs/6.9.15: resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} dev: true @@ -3864,6 +4197,15 @@ packages: resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} dev: true + /@types/request/2.48.12: + resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} + dependencies: + '@types/caseless': 0.12.5 + '@types/node': 18.19.44 + '@types/tough-cookie': 4.0.5 + form-data: 2.5.1 + dev: false + /@types/semver/7.5.8: resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true @@ -3907,6 +4249,10 @@ packages: resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} dev: true + /@types/tough-cookie/4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: false + /@types/yargs-parser/21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true @@ -3923,7 +4269,7 @@ packages: '@types/node': 18.19.44 optional: true - /@typescript-eslint/eslint-plugin/5.62.0_db7229ad096c0d451e7ee6dacac8a7d5: + /@typescript-eslint/eslint-plugin/5.62.0_4abfc85da931fcd8785d6be50866f99e: resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3935,12 +4281,12 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 5.62.0_eslint@8.57.0+typescript@5.5.4 + '@typescript-eslint/parser': 5.62.0_eslint@8.57.1+typescript@5.5.4 '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0_eslint@8.57.0+typescript@5.5.4 - '@typescript-eslint/utils': 5.62.0_eslint@8.57.0+typescript@5.5.4 + '@typescript-eslint/type-utils': 5.62.0_eslint@8.57.1+typescript@5.5.4 + '@typescript-eslint/utils': 5.62.0_eslint@8.57.1+typescript@5.5.4 debug: 4.3.6 - eslint: 8.57.0 + eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare-lite: 1.4.0 @@ -3951,7 +4297,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser/5.62.0_eslint@8.57.0+typescript@5.5.4: + /@typescript-eslint/parser/5.62.0_eslint@8.57.1+typescript@5.5.4: resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3965,7 +4311,7 @@ packages: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0_typescript@5.5.4 debug: 4.3.6 - eslint: 8.57.0 + eslint: 8.57.1 typescript: 5.5.4 transitivePeerDependencies: - supports-color @@ -3979,7 +4325,7 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils/5.62.0_eslint@8.57.0+typescript@5.5.4: + /@typescript-eslint/type-utils/5.62.0_eslint@8.57.1+typescript@5.5.4: resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -3990,9 +4336,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.62.0_typescript@5.5.4 - '@typescript-eslint/utils': 5.62.0_eslint@8.57.0+typescript@5.5.4 + '@typescript-eslint/utils': 5.62.0_eslint@8.57.1+typescript@5.5.4 debug: 4.3.6 - eslint: 8.57.0 + eslint: 8.57.1 tsutils: 3.21.0_typescript@5.5.4 typescript: 5.5.4 transitivePeerDependencies: @@ -4025,19 +4371,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.62.0_eslint@8.57.0+typescript@5.5.4: + /@typescript-eslint/utils/5.62.0_eslint@8.57.1+typescript@5.5.4: resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.57.0 + '@eslint-community/eslint-utils': 4.4.0_eslint@8.57.1 '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0_typescript@5.5.4 - eslint: 8.57.0 + eslint: 8.57.1 eslint-scope: 5.1.1 semver: 7.6.3 transitivePeerDependencies: @@ -4163,35 +4509,41 @@ packages: '@xtuc/long': 4.2.2 dev: true - /@webpack-cli/configtest/1.2.0_ae1dbec78783f5d68ce755fc6bffc7e5: - resolution: {integrity: sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==} + /@webpack-cli/configtest/2.1.1_webpack-cli@5.1.4+webpack@5.96.1: + resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} + engines: {node: '>=14.15.0'} peerDependencies: - webpack: 4.x.x || 5.x.x - webpack-cli: 4.x.x + webpack: 5.x.x + webpack-cli: 5.x.x dependencies: - webpack: 5.93.0_webpack-cli@4.10.0 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 dev: true - /@webpack-cli/info/1.5.0_webpack-cli@4.10.0: - resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==} + /@webpack-cli/info/2.0.2_webpack-cli@5.1.4+webpack@5.96.1: + resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} + engines: {node: '>=14.15.0'} peerDependencies: - webpack-cli: 4.x.x + webpack: 5.x.x + webpack-cli: 5.x.x dependencies: - envinfo: 7.13.0 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 dev: true - /@webpack-cli/serve/1.7.0_webpack-cli@4.10.0: - resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==} + /@webpack-cli/serve/2.0.5_webpack-cli@5.1.4+webpack@5.96.1: + resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} + engines: {node: '>=14.15.0'} peerDependencies: - webpack-cli: 4.x.x + webpack: 5.x.x + webpack-cli: 5.x.x webpack-dev-server: '*' peerDependenciesMeta: webpack-dev-server: optional: true dependencies: - webpack-cli: 4.10.0_webpack@5.93.0 + webpack: 5.96.1_webpack-cli@5.1.4 + webpack-cli: 5.1.4_webpack@5.96.1 dev: true /@xtuc/ieee754/1.2.0: @@ -4222,28 +4574,29 @@ packages: negotiator: 0.6.3 dev: true - /acorn-import-attributes/1.9.5_acorn@8.12.1: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.12.1 - dev: true - - /acorn-jsx/5.3.2_acorn@8.12.1: + /acorn-jsx/5.3.2_acorn@8.14.0: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.12.1 + acorn: 8.14.0 dev: true - /acorn/8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + /acorn/8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true dev: true + /agent-base/6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + dev: false + /agent-base/7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -4445,6 +4798,11 @@ packages: is-shared-array-buffer: 1.0.3 dev: true + /arrify/2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + dev: false + /asn1.js/4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} dependencies: @@ -4479,6 +4837,12 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} + /async-retry/1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + dependencies: + retry: 0.13.1 + dev: false + /async/3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} @@ -4510,7 +4874,7 @@ packages: transitivePeerDependencies: - debug - /babel-loader/9.1.3_fb5444ab5673dfbe6bc1a45ea6b1bf7e: + /babel-loader/9.1.3_5a631cb2df108081ef411545931b56d4: resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -4520,7 +4884,7 @@ packages: '@babel/core': 7.25.2 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.93.0 + webpack: 5.96.1 dev: true /babel-plugin-add-module-exports/1.0.4: @@ -4725,6 +5089,17 @@ packages: update-browserslist-db: 1.1.0_browserslist@4.23.3 dev: true + /browserslist/4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001683 + electron-to-chromium: 1.5.64 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1_browserslist@4.24.2 + dev: true + /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -4733,6 +5108,10 @@ packages: engines: {node: '>=8.0.0'} dev: false + /buffer-equal-constant-time/1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true @@ -4812,6 +5191,10 @@ packages: resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} dev: true + /caniuse-lite/1.0.30001683: + resolution: {integrity: sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==} + dev: true + /caseless/0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -4835,6 +5218,13 @@ packages: pathval: 1.1.1 type-detect: 4.1.0 + /chalk-template/1.1.0: + resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} + engines: {node: '>=14.16'} + dependencies: + chalk: 5.3.0 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4851,6 +5241,11 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk/5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /check-error/1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: @@ -4879,8 +5274,8 @@ packages: engines: {node: '>=6.0'} dev: true - /ci-info/3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + /ci-info/4.1.0: + resolution: {integrity: sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==} engines: {node: '>=8'} /cipher-base/1.0.4: @@ -4938,6 +5333,15 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + /cliui/8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + /clone-deep/4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} engines: {node: '>=6'} @@ -4947,7 +5351,7 @@ packages: shallow-clone: 3.0.1 dev: true - /coffee-loader/4.0.0_83eb9aab725b551aa4d62a0ffc077ce4: + /coffee-loader/4.0.0_2236fbf543816b23597cb8761490b47a: resolution: {integrity: sha512-RvgC8c0JwRew5lq3x2J+P4z9Cvan/v91muEvV90VJXcTuJbJQN20taZxfj6/XC4yysA8PInPGpxdB1J9LphLuQ==} engines: {node: '>= 14.15.0'} peerDependencies: @@ -4955,7 +5359,7 @@ packages: webpack: ^5.0.0 dependencies: coffeescript: 2.6.0 - webpack: 5.93.0 + webpack: 5.96.1 dev: true /coffeescript/2.6.0: @@ -4992,6 +5396,16 @@ packages: dependencies: delayed-stream: 1.0.0 + /commander/10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + + /commander/12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + dev: true + /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true @@ -5000,16 +5414,6 @@ packages: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} - /commander/7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - dev: true - - /commander/9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - dev: true - /comment-json/4.2.5: resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} engines: {node: '>= 6'} @@ -5037,18 +5441,6 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /configstore/5.0.1: - resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} - engines: {node: '>=8'} - dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.11 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - dev: true - /constants-browserify/1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} dev: true @@ -5095,17 +5487,6 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /cosmiconfig/7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - dev: true - /cp-file/6.2.0: resolution: {integrity: sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==} engines: {node: '>=6'} @@ -5145,14 +5526,6 @@ packages: sha.js: 2.4.11 dev: true - /cross-env/7.0.3: - resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} - engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} - hasBin: true - dependencies: - cross-spawn: 7.0.3 - dev: true - /cross-spawn/4.0.2: resolution: {integrity: sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==} dependencies: @@ -5171,8 +5544,8 @@ packages: which: 1.3.1 dev: true - /cross-spawn/7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + /cross-spawn/7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} dependencies: path-key: 3.1.1 @@ -5195,95 +5568,132 @@ packages: randomfill: 1.0.4 dev: true - /crypto-random-string/2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} + /cspell-config-lib/8.16.0: + resolution: {integrity: sha512-PGT6ohLtIYXYLIm+R5hTcTrF0dzj8e7WAUJSJe5WlV/7lrwVdwgWaliLcXtSSPmfxgczr6sndX9TMJ2IEmPrmg==} + engines: {node: '>=18'} + dependencies: + '@cspell/cspell-types': 8.16.0 + comment-json: 4.2.5 + yaml: 2.6.1 + dev: true + + /cspell-dictionary/8.16.0: + resolution: {integrity: sha512-Y3sN6ttLBKbu0dOLcduY641n5QP1srUvZkW4bOTnG455DbIZfilrP1El/2Hl0RS6hC8LN9PM4bsIm/2xgdbApA==} + engines: {node: '>=18'} + dependencies: + '@cspell/cspell-pipe': 8.16.0 + '@cspell/cspell-types': 8.16.0 + cspell-trie-lib: 8.16.0 + fast-equals: 5.0.1 dev: true - /cspell-gitignore/5.21.2: - resolution: {integrity: sha512-MdNmRRbglmCi20LU7ORZM1gyPSe1gL+4A8Pn+Jm+W5ropSbotzCqiO8BcyhRMNb3lAdMGGrj7gmYtiQ5C/fXIQ==} - engines: {node: '>=12.13.0'} + /cspell-gitignore/8.16.0: + resolution: {integrity: sha512-ODKe0ooyzYSBJkwgIVZSRIvzoZfT4tEbFt4fFDT88wPyyfX7xp7MAQhXy5KD1ocXH0WvYbdv37qzn2UbckrahA==} + engines: {node: '>=18'} hasBin: true dependencies: - cspell-glob: 5.21.2 - find-up: 5.0.0 + '@cspell/url': 8.16.0 + cspell-glob: 8.16.0 + cspell-io: 8.16.0 + find-up-simple: 1.0.0 dev: true - /cspell-glob/5.21.2: - resolution: {integrity: sha512-AabqzG31UWy4CSz1xJIK4qzXcarxuRFP9OD2EX8iDtEo0tQJLGoTHE+UpNDBPWTHearE0BZPhpMDF/radtZAgw==} - engines: {node: '>=12.13.0'} + /cspell-glob/8.16.0: + resolution: {integrity: sha512-xJSXRHwfENCNFmjpVSEucXY8E3BrpSCA+TukmOYtLyaMKtn6EAwoCpEU7Oj2tZOjdivprPmQ74k4Dqb1RHjIVQ==} + engines: {node: '>=18'} dependencies: - micromatch: 4.0.7 + '@cspell/url': 8.16.0 + micromatch: 4.0.8 dev: true - /cspell-io/5.21.2: - resolution: {integrity: sha512-3J4cLuN59R7ARiRZ8ke5QwlC5uPfzHLVELOtEAmsTIjuUMvr7BpbrdCuTsUvLkAqYE9NA5eqolqQm3GLXnECNw==} - engines: {node: '>=12.13.0'} + /cspell-grammar/8.16.0: + resolution: {integrity: sha512-vvbJEkBqXocGH/H975RtkfMzVpNxNGMd0JCDd+NjbpeRyZceuChFw5Tie7kHteFY29SwZovub+Am3F4H1kmf9A==} + engines: {node: '>=18'} + hasBin: true + dependencies: + '@cspell/cspell-pipe': 8.16.0 + '@cspell/cspell-types': 8.16.0 dev: true - /cspell-lib/5.21.2: - resolution: {integrity: sha512-emAFXtDfs84FoMlhOxZYxYVvbCoCN0LxN0obIRvCsvFCLUPj9y7vHv/Tu/01ZyAPeo2r6gkqhanJpQyoIDA1yg==} - engines: {node: '>=12.13.0'} + /cspell-io/8.16.0: + resolution: {integrity: sha512-WIK5uhPMjGsTAzm2/fGRbIdr7zWsMVG1fn8wNJYUiYELuyvzvLelfI1VG6szaFCGYqd6Uvgb/fS0uNbwGqCLAQ==} + engines: {node: '>=18'} dependencies: - '@cspell/cspell-bundled-dicts': 5.21.2 - '@cspell/cspell-pipe': 5.21.2 - '@cspell/cspell-types': 5.21.2 + '@cspell/cspell-service-bus': 8.16.0 + '@cspell/url': 8.16.0 + dev: true + + /cspell-lib/8.16.0: + resolution: {integrity: sha512-fU8CfECyuhT12COIi4ViQu2bTkdqaa+05YSd2ZV8k8NA7lapPaMFnlooxdfcwwgZJfHeMhRVMzvQF1OhWmwGfA==} + engines: {node: '>=18'} + dependencies: + '@cspell/cspell-bundled-dicts': 8.16.0 + '@cspell/cspell-pipe': 8.16.0 + '@cspell/cspell-resolver': 8.16.0 + '@cspell/cspell-types': 8.16.0 + '@cspell/dynamic-import': 8.16.0 + '@cspell/filetypes': 8.16.0 + '@cspell/strong-weak-map': 8.16.0 + '@cspell/url': 8.16.0 clear-module: 4.1.2 comment-json: 4.2.5 - configstore: 5.0.1 - cosmiconfig: 7.1.0 - cspell-glob: 5.21.2 - cspell-io: 5.21.2 - cspell-trie-lib: 5.21.2 - fast-equals: 3.0.3 - find-up: 5.0.0 - fs-extra: 10.1.0 - gensequence: 3.1.1 + cspell-config-lib: 8.16.0 + cspell-dictionary: 8.16.0 + cspell-glob: 8.16.0 + cspell-grammar: 8.16.0 + cspell-io: 8.16.0 + cspell-trie-lib: 8.16.0 + env-paths: 3.0.0 + fast-equals: 5.0.1 + gensequence: 7.0.0 import-fresh: 3.3.0 resolve-from: 5.0.0 - resolve-global: 1.0.0 vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.0.8 + xdg-basedir: 5.1.0 dev: true - /cspell-trie-lib/5.21.2: - resolution: {integrity: sha512-iux2F+85jDlBEJZgikfPT5SUZMwuFjNqEJiO1SO+xfQG+2MFV9CaHTsoRJIGNy3udMm1mw0GMY5UIVAodwlnhg==} - engines: {node: '>=12.13.0'} + /cspell-trie-lib/8.16.0: + resolution: {integrity: sha512-Io1qqI0r4U9ewAWBLClFBBlxLeAoIi15PUGJi4Za1xrlgQJwRE8PMNIJNHKmPEIp78Iute3o/JyC2OfWlxl4Sw==} + engines: {node: '>=18'} dependencies: - '@cspell/cspell-pipe': 5.21.2 - fs-extra: 10.1.0 - gensequence: 3.1.1 + '@cspell/cspell-pipe': 8.16.0 + '@cspell/cspell-types': 8.16.0 + gensequence: 7.0.0 dev: true - /cspell/5.21.2: - resolution: {integrity: sha512-yG14BUumeIcsuSEcM//+9XpbUR6a6FlAxfaVI4e5t6ZZE5tPgDE0PNIVr/jAiLPVm9qUfnq+oNdZE8wmVUbMzw==} - engines: {node: '>=12.13.0'} + /cspell/8.16.0: + resolution: {integrity: sha512-U6Up/4nODE+Ca+zqwZXTgBioGuF2JQHLEUIuoRJkJzAZkIBYDqrMXM+zdSL9E39+xb9jAtr9kPAYJf1Eybgi9g==} + engines: {node: '>=18'} hasBin: true dependencies: - '@cspell/cspell-pipe': 5.21.2 - chalk: 4.1.2 - commander: 9.5.0 - cspell-gitignore: 5.21.2 - cspell-glob: 5.21.2 - cspell-lib: 5.21.2 + '@cspell/cspell-json-reporter': 8.16.0 + '@cspell/cspell-pipe': 8.16.0 + '@cspell/cspell-types': 8.16.0 + '@cspell/dynamic-import': 8.16.0 + '@cspell/url': 8.16.0 + chalk: 5.3.0 + chalk-template: 1.1.0 + commander: 12.1.0 + cspell-dictionary: 8.16.0 + cspell-gitignore: 8.16.0 + cspell-glob: 8.16.0 + cspell-io: 8.16.0 + cspell-lib: 8.16.0 fast-json-stable-stringify: 2.1.0 - file-entry-cache: 6.0.1 - fs-extra: 10.1.0 - get-stdin: 8.0.0 - glob: 8.1.0 - imurmurhash: 0.1.4 + file-entry-cache: 9.1.0 + get-stdin: 9.0.0 semver: 7.6.3 - strip-ansi: 6.0.1 - vscode-uri: 3.0.8 + tinyglobby: 0.2.10 dev: true - /cypress/13.13.2: - resolution: {integrity: sha512-PvJQU33933NvS1StfzEb8/mu2kMy4dABwCF+yd5Bi7Qly1HOVf+Bufrygee/tlmty/6j5lX+KIi8j9Q3JUMbhA==} + /cypress/13.16.0: + resolution: {integrity: sha512-g6XcwqnvzXrqiBQR/5gN+QsyRmKRhls1y5E42fyOvsmU7JuY+wM6uHJWj4ZPttjabzbnRvxcik2WemR8+xT6FA==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} hasBin: true requiresBuild: true dependencies: - '@cypress/request': 3.0.1 + '@cypress/request': 3.0.6 '@cypress/xvfb': 1.2.4 '@types/sinonjs__fake-timers': 8.1.1 '@types/sizzle': 2.3.8 @@ -5294,6 +5704,7 @@ packages: cachedir: 2.4.0 chalk: 4.1.2 check-more-types: 2.24.0 + ci-info: 4.1.0 cli-cursor: 3.1.0 cli-table3: 0.6.5 commander: 6.2.1 @@ -5308,7 +5719,6 @@ packages: figures: 3.2.0 fs-extra: 9.1.0 getos: 3.2.1 - is-ci: 3.0.1 is-installed-globally: 0.4.0 lazy-ass: 1.6.0 listr2: 3.14.0_enquirer@2.4.1 @@ -5323,6 +5733,7 @@ packages: semver: 7.6.3 supports-color: 8.1.1 tmp: 0.2.3 + tree-kill: 1.2.2 untildify: 4.0.0 yauzl: 2.10.0 @@ -5513,28 +5924,40 @@ packages: engines: {node: '>=10'} dev: true - /dot-prop/5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - dependencies: - is-obj: 2.0.0 - dev: true - /dotenv/16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} dev: true + /duplexify/4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + dependencies: + end-of-stream: 1.4.4 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + dev: false + /ecc-jsbn/0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} dependencies: jsbn: 0.1.1 safer-buffer: 2.1.2 + /ecdsa-sig-formatter/1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /ee-first/1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true + /electron-to-chromium/1.5.64: + resolution: {integrity: sha512-IXEuxU+5ClW2IGEYFC2T7szbyVgehupCWQe5GNh+H065CD6U6IFN0s4KeAMFGNmQolRU4IV7zGBWSYMmZ8uuqQ==} + dev: true + /electron-to-chromium/1.5.7: resolution: {integrity: sha512-6FTNWIWMxMy/ZY6799nBlPtF1DFDQ6VQJ7yyDP27SJNt5lwtQ5ufqVvHylb3fdQefvRcgA3fKcFMJi9OLwBRNw==} dev: true @@ -5583,6 +6006,11 @@ packages: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + /env-paths/3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /envinfo/7.13.0: resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} engines: {node: '>=4'} @@ -5700,6 +6128,11 @@ packages: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + /escalade/3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + dev: true + /escape-html/1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} dev: true @@ -5712,13 +6145,13 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - /eslint-config-prettier/8.10.0_eslint@8.57.0: + /eslint-config-prettier/8.10.0_eslint@8.57.1: resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 dev: true /eslint-import-resolver-node/0.3.9: @@ -5729,7 +6162,7 @@ packages: resolve: 1.22.8 dev: true - /eslint-module-utils/2.8.1_eslint@8.57.0: + /eslint-module-utils/2.8.1_eslint@8.57.1: resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: @@ -5739,17 +6172,17 @@ packages: optional: true dependencies: debug: 3.2.7 - eslint: 8.57.0 + eslint: 8.57.1 dev: true - /eslint-plugin-deprecation/1.5.0_eslint@8.57.0+typescript@5.5.4: + /eslint-plugin-deprecation/1.5.0_eslint@8.57.1+typescript@5.5.4: resolution: {integrity: sha512-mRcssI/tLROueBQ6yf4LnnGTijbMsTCPIpbRbPj5R5wGYVCpk1zDmAS0SEkgcUDXOPc22qMNFR24Qw7vSPrlTA==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: ^3.7.5 || ^4.0.0 || ^5.0.0 dependencies: - '@typescript-eslint/utils': 5.62.0_eslint@8.57.0+typescript@5.5.4 - eslint: 8.57.0 + '@typescript-eslint/utils': 5.62.0_eslint@8.57.1+typescript@5.5.4 + eslint: 8.57.1 tslib: 2.6.3 tsutils: 3.21.0_typescript@5.5.4 typescript: 5.5.4 @@ -5757,7 +6190,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.29.1_eslint@8.57.0: + /eslint-plugin-import/2.29.1_eslint@8.57.1: resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -5769,9 +6202,9 @@ packages: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1_eslint@8.57.0 + eslint-module-utils: 2.8.1_eslint@8.57.1 hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -5783,27 +6216,27 @@ packages: tsconfig-paths: 3.15.0 dev: true - /eslint-plugin-mocha/10.5.0_eslint@8.57.0: + /eslint-plugin-mocha/10.5.0_eslint@8.57.1: resolution: {integrity: sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==} engines: {node: '>=14.0.0'} peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.57.0 - eslint-utils: 3.0.0_eslint@8.57.0 + eslint: 8.57.1 + eslint-utils: 3.0.0_eslint@8.57.1 globals: 13.24.0 rambda: 7.5.0 dev: true - /eslint-plugin-prefer-arrow/1.2.3_eslint@8.57.0: + /eslint-plugin-prefer-arrow/1.2.3_eslint@8.57.1: resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} peerDependencies: eslint: '>=2.0.0' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 dev: true - /eslint-plugin-prettier/4.2.1_cac2d69d611136ee42e8dba38a7224b1: + /eslint-plugin-prettier/4.2.1_5f55bbfab3e3b9eaf6bb3d00902ff8bc: resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -5814,8 +6247,8 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.57.0 - eslint-config-prettier: 8.10.0_eslint@8.57.0 + eslint: 8.57.1 + eslint-config-prettier: 8.10.0_eslint@8.57.1 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 dev: true @@ -5836,13 +6269,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.57.0: + /eslint-utils/3.0.0_eslint@8.57.1: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.57.0 + eslint: 8.57.1 eslint-visitor-keys: 2.1.0 dev: true @@ -5856,22 +6289,23 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + /eslint/8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0_eslint@8.57.0 - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/eslint-utils': 4.4.0_eslint@8.57.1 + '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 debug: 4.3.6 doctrine: 3.0.0 escape-string-regexp: 4.0.0 @@ -5907,8 +6341,8 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2_acorn@8.12.1 + acorn: 8.14.0 + acorn-jsx: 5.3.2_acorn@8.14.0 eslint-visitor-keys: 3.4.3 dev: true @@ -5979,7 +6413,7 @@ packages: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 5.2.0 human-signals: 1.1.1 is-stream: 2.0.1 @@ -6060,8 +6494,9 @@ packages: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} dev: true - /fast-equals/3.0.3: - resolution: {integrity: sha512-NCe8qxnZFARSHGztGMZOO/PC1qa5MIFB5Hp66WdzbCRAz8U8US3bx1UTgLS49efBQPcUtO9gf5oVEY8o7y/7Kg==} + /fast-equals/5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} dev: true /fast-glob/3.3.2: @@ -6110,6 +6545,17 @@ packages: dependencies: pend: 1.2.0 + /fdir/6.4.2_picomatch@4.0.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 + dev: true + /figures/3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -6123,6 +6569,13 @@ packages: flat-cache: 3.2.0 dev: true + /file-entry-cache/9.1.0: + resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} + engines: {node: '>=18'} + dependencies: + flat-cache: 5.0.0 + dev: true + /fill-range/7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -6164,6 +6617,11 @@ packages: pkg-dir: 7.0.0 dev: true + /find-up-simple/1.0.0: + resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} + engines: {node: '>=18'} + dev: true + /find-up/3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -6203,6 +6661,14 @@ packages: rimraf: 3.0.2 dev: true + /flat-cache/5.0.0: + resolution: {integrity: sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==} + engines: {node: '>=18'} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + dev: true + /flat/5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true @@ -6235,13 +6701,14 @@ packages: /forever-agent/0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - /form-data/2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + /form-data/2.5.1: + resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} engines: {node: '>= 0.12'} dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 + dev: false /form-data/4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} @@ -6268,6 +6735,7 @@ packages: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 + dev: false /fs-extra/9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} @@ -6304,9 +6772,34 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gensequence/3.1.1: - resolution: {integrity: sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g==} - engines: {node: '>=10.0.0'} + /gaxios/6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.5 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /gcp-metadata/6.1.0: + resolution: {integrity: sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==} + engines: {node: '>=14'} + dependencies: + gaxios: 6.7.1 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /gensequence/7.0.0: + resolution: {integrity: sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==} + engines: {node: '>=18'} dev: true /gensync/1.0.0-beta.2: @@ -6331,9 +6824,9 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-stdin/8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} + /get-stdin/9.0.0: + resolution: {integrity: sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==} + engines: {node: '>=12'} dev: true /get-stream/5.2.0: @@ -6405,11 +6898,11 @@ packages: minimatch: 5.1.6 once: 1.4.0 - /global-dirs/0.1.1: - resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} - engines: {node: '>=4'} + /global-directory/4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} dependencies: - ini: 1.3.8 + ini: 4.1.1 dev: true /global-dirs/3.0.1: @@ -6464,6 +6957,42 @@ packages: slash: 3.0.0 dev: true + /google-auth-library/9.14.1: + resolution: {integrity: sha512-Rj+PMjoNFGFTmtItH7gHfbHpGVSb3vmnGK3nwNBqxQF9NoBpttSZI/rc0WiM63ma2uGDQtYEkMHkK9U6937NiA==} + engines: {node: '>=14'} + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1 + gcp-metadata: 6.1.0 + gtoken: 7.1.0 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /google-gax/4.4.0: + resolution: {integrity: sha512-4fkXSbNy85ikO7mkD5lChLL5UfLnRBvg6z3s3THUJKI6OSbISbufMDE4S/ZH+J3mB9A2FdMXBT/hh7wTvpGAow==} + engines: {node: '>=14'} + dependencies: + '@grpc/grpc-js': 1.11.1 + '@grpc/proto-loader': 0.7.13 + '@types/long': 4.0.2 + abort-controller: 3.0.0 + duplexify: 4.1.3 + google-auth-library: 9.14.1 + node-fetch: 2.7.0 + object-hash: 3.0.0 + proto3-json-serializer: 2.0.2 + protobufjs: 7.4.0 + retry-request: 7.0.2 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /gopd/1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -6476,6 +7005,17 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /gtoken/7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} + dependencies: + gaxios: 6.7.1 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /has-bigints/1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -6566,6 +7106,10 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /html-entities/2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} + dev: false + /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -6581,6 +7125,17 @@ packages: toidentifier: 1.0.1 dev: true + /http-proxy-agent/5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + dev: false + /http-proxy-agent/7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -6591,8 +7146,8 @@ packages: - supports-color dev: false - /http-signature/1.3.6: - resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==} + /http-signature/1.4.0: + resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} engines: {node: '>=0.10'} dependencies: assert-plus: 1.0.0 @@ -6603,6 +7158,16 @@ packages: resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} dev: true + /https-proxy-agent/5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + dev: false + /https-proxy-agent/7.0.5: resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} @@ -6658,6 +7223,10 @@ packages: resolve-cwd: 3.0.0 dev: true + /import-meta-resolve/4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + dev: true + /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -6677,14 +7246,15 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ini/1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true - /ini/2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} + /ini/4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /internal-slot/1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -6694,9 +7264,9 @@ packages: side-channel: 1.0.6 dev: true - /interpret/2.2.0: - resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} - engines: {node: '>= 0.10'} + /interpret/3.1.1: + resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} + engines: {node: '>=10.13.0'} dev: true /inversify/6.0.2: @@ -6755,12 +7325,6 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - /is-ci/3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - dependencies: - ci-info: 3.9.0 - /is-core-module/2.15.0: resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} engines: {node: '>= 0.4'} @@ -6838,11 +7402,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - /is-obj/2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true - /is-path-inside/3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} @@ -7110,6 +7669,21 @@ packages: resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} dev: true + /jwa/2.0.0: + resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws/4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + dependencies: + jwa: 2.0.0 + safe-buffer: 5.2.1 + dev: false + /keyv/4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -7137,10 +7711,6 @@ packages: type-check: 0.4.0 dev: true - /lines-and-columns/1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: true - /listr2/3.14.0_enquirer@2.4.1: resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==} engines: {node: '>=10.0.0'} @@ -7203,6 +7773,10 @@ packages: p-locate: 6.0.0 dev: true + /lodash.camelcase/4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + dev: false + /lodash.debounce/4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true @@ -7241,6 +7815,10 @@ packages: slice-ansi: 4.0.0 wrap-ansi: 6.2.0 + /long/5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + /loupe/2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: @@ -7267,13 +7845,6 @@ packages: semver: 5.7.2 dev: true - /make-dir/3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.1 - dev: true - /md5.js/1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} dependencies: @@ -7323,6 +7894,14 @@ packages: picomatch: 2.3.1 dev: true + /micromatch/4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + dev: true + /miller-rabin/4.0.1: resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} hasBin: true @@ -7347,6 +7926,12 @@ packages: hasBin: true dev: true + /mime/3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: false + /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -7472,6 +8057,18 @@ packages: path-to-regexp: 6.2.2 dev: true + /node-fetch/2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + dependencies: + whatwg-url: 5.0.0 + dev: false + /node-releases/2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} dev: true @@ -7545,6 +8142,11 @@ packages: - supports-color dev: true + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: false + /object-inspect/1.13.2: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} @@ -7748,16 +8350,6 @@ packages: json-parse-better-errors: 1.0.2 dev: true - /parse-json/5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.24.7 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - dev: true - /parseurl/1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -7843,10 +8435,19 @@ packages: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} dev: true + /picocolors/1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true + /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /picomatch/4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + dev: true + /pidtree/0.3.1: resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} engines: {node: '>=0.10'} @@ -7922,6 +8523,32 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} + /proto3-json-serializer/2.0.2: + resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} + engines: {node: '>=14.0.0'} + dependencies: + protobufjs: 7.4.0 + dev: false + + /protobufjs/7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.19.44 + long: 5.2.3 + dev: false + /proxy-addr/2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -7940,9 +8567,6 @@ packages: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true - /psl/1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - /public-encrypt/4.0.3: resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} dependencies: @@ -7967,12 +8591,7 @@ packages: /punycode/2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - - /qs/6.10.4: - resolution: {integrity: sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==} - engines: {node: '>=0.6'} - dependencies: - side-channel: 1.0.6 + dev: true /qs/6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} @@ -7986,7 +8605,6 @@ packages: engines: {node: '>=0.6'} dependencies: side-channel: 1.0.6 - dev: true /query-string/7.1.3: resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} @@ -8003,9 +8621,6 @@ packages: engines: {node: '>=0.4.x'} dev: true - /querystringify/2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true @@ -8084,9 +8699,9 @@ packages: dependencies: picomatch: 2.3.1 - /rechoir/0.7.1: - resolution: {integrity: sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==} - engines: {node: '>= 0.10'} + /rechoir/0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} dependencies: resolve: 1.22.8 dev: true @@ -8174,9 +8789,6 @@ packages: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true - /requires-port/1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - /resolve-cwd/3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -8194,13 +8806,6 @@ packages: engines: {node: '>=8'} dev: true - /resolve-global/1.0.0: - resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} - engines: {node: '>=8'} - dependencies: - global-dirs: 0.1.1 - dev: true - /resolve/1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -8217,6 +8822,23 @@ packages: onetime: 5.1.2 signal-exit: 3.0.7 + /retry-request/7.0.2: + resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} + engines: {node: '>=14'} + dependencies: + '@types/request': 2.48.12 + extend: 3.0.2 + teeny-request: 9.0.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /retry/0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + dev: false + /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -8579,6 +9201,12 @@ packages: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} dev: false + /stream-events/1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + dependencies: + stubs: 3.0.0 + dev: false + /stream-http/3.2.0: resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} dependencies: @@ -8594,6 +9222,10 @@ packages: stream-chain: 2.2.5 dev: false + /stream-shift/1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + dev: false + /strict-uri-encode/2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} @@ -8699,6 +9331,10 @@ packages: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false + /stubs/3.0.0: + resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + dev: false + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -8735,7 +9371,21 @@ packages: engines: {node: '>=6'} dev: true - /terser-webpack-plugin/5.3.10_webpack@5.93.0: + /teeny-request/9.0.0: + resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} + engines: {node: '>=14'} + dependencies: + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + stream-events: 1.0.5 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /terser-webpack-plugin/5.3.10_webpack@5.96.1: resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -8756,7 +9406,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 - webpack: 5.93.0_webpack-cli@4.10.0 + webpack: 5.96.1_webpack-cli@5.1.4 dev: true /terser/5.31.6: @@ -8765,7 +9415,7 @@ packages: hasBin: true dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.12.1 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 dev: true @@ -8803,6 +9453,23 @@ packages: setimmediate: 1.0.5 dev: true + /tinyglobby/0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + dependencies: + fdir: 6.4.2_picomatch@4.0.2 + picomatch: 4.0.2 + dev: true + + /tldts-core/6.1.63: + resolution: {integrity: sha512-H1XCt54xY+QPbwhTgmxLkepX0MVHu3USfMmejiCOdkMbRcP22Pn2FVF127r/GWXVDmXTRezyF3Ckvhn4Fs6j7Q==} + + /tldts/6.1.63: + resolution: {integrity: sha512-YWwhsjyn9sB/1rOkSRYxvkN/wl5LFM1QDv6F2pVR+pb/jFne4EOBxHfkKVWvDIBEAw9iGOwwubHtQTm0WRT5sQ==} + hasBin: true + dependencies: + tldts-core: 6.1.63 + /tmp/0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} @@ -8823,16 +9490,21 @@ packages: engines: {node: '>=0.6'} dev: true - /tough-cookie/4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} + /tough-cookie/5.0.0: + resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==} + engines: {node: '>=16'} dependencies: - psl: 1.9.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 + tldts: 6.1.63 + + /tr46/0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + dev: false - /ts-loader/9.4.4_typescript@5.5.4+webpack@5.93.0: + /tree-kill/1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + /ts-loader/9.4.4_typescript@5.5.4+webpack@5.96.1: resolution: {integrity: sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==} engines: {node: '>=12.0.0'} peerDependencies: @@ -8844,7 +9516,7 @@ packages: micromatch: 4.0.7 semver: 7.6.3 typescript: 5.5.4 - webpack: 5.93.0 + webpack: 5.96.1 dev: true /tsconfig-paths-webpack-plugin/3.5.2: @@ -8979,12 +9651,6 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /typedarray-to-buffer/3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - dependencies: - is-typedarray: 1.0.0 - dev: true - /typescript/5.5.4: resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} @@ -9026,17 +9692,6 @@ packages: engines: {node: '>=4'} dev: true - /unique-string/2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 - dev: true - - /universalify/0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - /universalify/2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -9061,18 +9716,23 @@ packages: picocolors: 1.0.1 dev: true + /update-browserslist-db/1.1.1_browserslist@4.24.2: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 + dev: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 dev: true - /url-parse/1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - /url/0.11.4: resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} engines: {node: '>= 0.4'} @@ -9175,38 +9835,40 @@ packages: '@zxing/text-encoding': 0.9.0 dev: false - /webpack-cli/4.10.0_webpack@5.93.0: - resolution: {integrity: sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==} - engines: {node: '>=10.13.0'} + /webidl-conversions/3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + dev: false + + /webpack-cli/5.1.4_webpack@5.96.1: + resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} + engines: {node: '>=14.15.0'} hasBin: true peerDependencies: '@webpack-cli/generators': '*' - '@webpack-cli/migrate': '*' - webpack: 4.x.x || 5.x.x + webpack: 5.x.x webpack-bundle-analyzer: '*' webpack-dev-server: '*' peerDependenciesMeta: '@webpack-cli/generators': optional: true - '@webpack-cli/migrate': - optional: true webpack-bundle-analyzer: optional: true webpack-dev-server: optional: true dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 1.2.0_ae1dbec78783f5d68ce755fc6bffc7e5 - '@webpack-cli/info': 1.5.0_webpack-cli@4.10.0 - '@webpack-cli/serve': 1.7.0_webpack-cli@4.10.0 + '@webpack-cli/configtest': 2.1.1_webpack-cli@5.1.4+webpack@5.96.1 + '@webpack-cli/info': 2.0.2_webpack-cli@5.1.4+webpack@5.96.1 + '@webpack-cli/serve': 2.0.5_webpack-cli@5.1.4+webpack@5.96.1 colorette: 2.0.20 - commander: 7.2.0 - cross-spawn: 7.0.3 + commander: 10.0.1 + cross-spawn: 7.0.6 + envinfo: 7.13.0 fastest-levenshtein: 1.0.16 import-local: 3.2.0 - interpret: 2.2.0 - rechoir: 0.7.1 - webpack: 5.93.0_webpack-cli@4.10.0 + interpret: 3.1.1 + rechoir: 0.8.0 + webpack: 5.96.1_webpack-cli@5.1.4 webpack-merge: 5.10.0 dev: true @@ -9224,8 +9886,8 @@ packages: engines: {node: '>=10.13.0'} dev: true - /webpack/5.93.0: - resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} + /webpack/5.96.1: + resolution: {integrity: sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -9235,13 +9897,12 @@ packages: optional: true dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.1 - acorn-import-attributes: 1.9.5_acorn@8.12.1 - browserslist: 4.23.3 + acorn: 8.14.0 + browserslist: 4.24.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.4 @@ -9255,7 +9916,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10_webpack@5.93.0 + terser-webpack-plugin: 5.3.10_webpack@5.96.1 watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: @@ -9264,8 +9925,8 @@ packages: - uglify-js dev: true - /webpack/5.93.0_webpack-cli@4.10.0: - resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} + /webpack/5.96.1_webpack-cli@5.1.4: + resolution: {integrity: sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -9275,13 +9936,12 @@ packages: optional: true dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.1 - acorn-import-attributes: 1.9.5_acorn@8.12.1 - browserslist: 4.23.3 + acorn: 8.14.0 + browserslist: 4.24.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1 es-module-lexer: 1.5.4 @@ -9295,9 +9955,9 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10_webpack@5.93.0 + terser-webpack-plugin: 5.3.10_webpack@5.96.1 watchpack: 2.4.2 - webpack-cli: 4.10.0_webpack@5.93.0 + webpack-cli: 5.1.4_webpack@5.96.1 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -9305,6 +9965,13 @@ packages: - uglify-js dev: true + /whatwg-url/5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + dev: false + /which-boxed-primitive/1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -9391,18 +10058,9 @@ packages: signal-exit: 3.0.7 dev: true - /write-file-atomic/3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 - dev: true - - /xdg-basedir/4.0.0: - resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} - engines: {node: '>=8'} + /xdg-basedir/5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} dev: true /xml2js/0.5.0: @@ -9439,9 +10097,10 @@ packages: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true - /yaml/1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} + /yaml/2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} + engines: {node: '>= 14'} + hasBin: true dev: true /yargs-parser/13.1.2: @@ -9455,6 +10114,11 @@ packages: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} + /yargs-parser/21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + /yargs-unparser/2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} @@ -9491,6 +10155,19 @@ packages: y18n: 5.0.8 yargs-parser: 20.2.9 + /yargs/17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: false + /yauzl/2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} dependencies: diff --git a/rush.json b/rush.json index 475981f2..35488d0f 100644 --- a/rush.json +++ b/rush.json @@ -32,6 +32,12 @@ "versionPolicyName": "lockStepVersionObjectStorage", "tags": [ "integration-tested" ] }, + { + "packageName": "@itwin/object-storage-google", + "projectFolder": "storage/google", + "versionPolicyName": "lockStepVersionObjectStorage", + "tags": [ "integration-tested" ] + }, { "packageName": "@itwin/object-storage-minio", "projectFolder": "storage/minio", diff --git a/samples/package.json b/samples/package.json index bdd10c17..1df645b6 100644 --- a/samples/package.json +++ b/samples/package.json @@ -41,8 +41,8 @@ "devDependencies": { "@itwin/object-storage-common-config": "workspace:*", "@types/node": "^18.11.18", - "cspell": "^5.18.5", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "eslint": "^8.57.1", "rimraf": "^2.6.2", "sort-package-json": "^1.53.1", "typescript": "^5.1.3" diff --git a/storage/azure/package.json b/storage/azure/package.json index 3cf893f7..1c1abb57 100644 --- a/storage/azure/package.json +++ b/storage/azure/package.json @@ -62,10 +62,10 @@ "@types/sinon": "^10.0.11", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "cspell": "^5.18.5", - "cypress": "^13.12.0", + "cspell": "^8.16.0", + "cypress": "^13.16.0", "dotenv": "^16.0.0", - "eslint": "^8.42.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "npm-run-all": "^4.1.5", @@ -76,8 +76,8 @@ "sort-package-json": "^1.53.1", "typescript": "^5.1.3", "wait-on": "^7.2.0", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2" + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4" }, "peerDependencies": { "inversify": "^6.0.1", diff --git a/storage/azure/src/common/internal/Helpers.ts b/storage/azure/src/common/internal/Helpers.ts index d2162875..f8a4bc37 100644 --- a/storage/azure/src/common/internal/Helpers.ts +++ b/storage/azure/src/common/internal/Helpers.ts @@ -13,11 +13,8 @@ import { import { assertTransferConfig, buildObjectKey, - defaultExpiresInSeconds, } from "@itwin/object-storage-core/lib/common/internal"; -import { ExpiryOptions } from "@itwin/object-storage-core"; - import { AzureTransferConfig, AzureTransferConfigInput } from "../Interfaces"; export function assertAzureTransferConfig( @@ -45,18 +42,3 @@ export function buildBlobName(reference: ObjectReference): string { const { relativeDirectory, objectName } = reference; return (relativeDirectory ? `${relativeDirectory}/` : "") + objectName; } - -export function getExpiryDate(options?: ExpiryOptions): Date { - if (options?.expiresInSeconds && options?.expiresOn) { - throw new Error( - "Only one of 'expiresInSeconds' and 'expiresOn' can be specified." - ); - } - if (options?.expiresInSeconds) { - return new Date(Date.now() + options.expiresInSeconds * 1000); - } - if (options?.expiresOn) { - return options.expiresOn; - } - return new Date(Date.now() + defaultExpiresInSeconds * 1000); // expires in one hour by default -} diff --git a/storage/azure/src/server/AzureServerStorage.ts b/storage/azure/src/server/AzureServerStorage.ts index 8146736a..eb357e5b 100644 --- a/storage/azure/src/server/AzureServerStorage.ts +++ b/storage/azure/src/server/AzureServerStorage.ts @@ -13,6 +13,7 @@ import { assertRelativeDirectory } from "@itwin/object-storage-core/lib/common/i import { assertFileNotEmpty, assertLocalFile, + getExpiryDate, streamToTransferType, } from "@itwin/object-storage-core/lib/server/internal"; @@ -33,7 +34,7 @@ import { } from "@itwin/object-storage-core"; import { AzureTransferConfig, Types } from "../common"; -import { buildBlobName, getExpiryDate } from "../common/internal"; +import { buildBlobName } from "../common/internal"; import { buildBlobSASParameters, @@ -339,7 +340,7 @@ export class AzureServerStorage extends ServerStorage { await copyPoller.pollUntilDone(); } - public releaseResources(): void {} + public override async releaseResources(): Promise {} private async handleNotFound(operation: () => Promise): Promise { try { diff --git a/storage/core/package.json b/storage/core/package.json index 7a7e0ee2..9625154e 100644 --- a/storage/core/package.json +++ b/storage/core/package.json @@ -49,9 +49,9 @@ "@types/node": "^18.11.18", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "cspell": "^5.18.5", - "cypress": "^13.12.0", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "cypress": "^13.16.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "nyc": "^14.0.0", diff --git a/storage/core/src/frontend/internal/Helpers.ts b/storage/core/src/frontend/internal/Helpers.ts index bc70fd1a..02b22b06 100644 --- a/storage/core/src/frontend/internal/Helpers.ts +++ b/storage/core/src/frontend/internal/Helpers.ts @@ -10,8 +10,6 @@ import { FrontendUrlDownloadInput, } from "../FrontendInterfaces"; -export const uploadFileSizeLimit = 5_000_000_000; // 5GB - export async function streamToBufferFrontend( stream: ReadableStream ): Promise { @@ -39,19 +37,24 @@ export async function streamToTransferTypeFrontend( } export async function downloadFromUrlFrontend( - input: FrontendUrlDownloadInput + input: FrontendUrlDownloadInput, + headers?: Record ): Promise { const { transferType, url } = input; switch (transferType) { case "buffer": const bufferResponse = await axios.get(url, { responseType: "arraybuffer", + headers, }); return bufferResponse.data as ArrayBuffer; case "stream": // https://github.com/axios/axios/issues/479 - const blobResponse = await axios.get(url, { responseType: "blob" }); + const blobResponse = await axios.get(url, { + responseType: "blob", + headers, + }); return (blobResponse.data as Blob).stream(); default: throw new Error( @@ -65,9 +68,13 @@ export async function downloadFromUrlFrontend( export async function uploadToUrlFrontend( url: string, data: FrontendTransferData, + method: "POST" | "PUT", headers?: Record ): Promise { - await axios.put(url, data, { + await axios.request({ + url, + method, + data, headers, }); } diff --git a/storage/core/src/server/ServerStorage.ts b/storage/core/src/server/ServerStorage.ts index 8e54cf82..c8751f88 100644 --- a/storage/core/src/server/ServerStorage.ts +++ b/storage/core/src/server/ServerStorage.ts @@ -312,7 +312,7 @@ export abstract class ServerStorage * method after an instance of this class is not used anymore to free the * resources and avoid hanging processes or similar issues. */ - public abstract releaseResources(): void; + public abstract releaseResources(): Promise; } export interface PresignedUrlProvider { diff --git a/storage/core/src/server/internal/Helpers.ts b/storage/core/src/server/internal/Helpers.ts index 0b9d69c8..97960de6 100644 --- a/storage/core/src/server/internal/Helpers.ts +++ b/storage/core/src/server/internal/Helpers.ts @@ -10,8 +10,10 @@ import { Readable } from "stream"; import axios from "axios"; import { ConfigTransferInput, UrlTransferInput } from "../../common"; +import { defaultExpiresInSeconds } from "../../common/internal"; import { ConfigDownloadInput, + ExpiryOptions, TransferData, TransferType, UrlDownloadInput, @@ -54,6 +56,10 @@ export async function streamToBuffer(stream: Readable): Promise { }); } +export function bufferToStream(buffer: Buffer): Readable { + return Readable.from(buffer); +} + export async function streamToLocalFile( stream: Readable, destinationPath: string @@ -71,6 +77,24 @@ export async function streamToLocalFile( }); } +export function bufferToTransferType( + buffer: Buffer, + transferType: "buffer" | "stream" +): TransferData { + switch (transferType) { + case "stream": + return bufferToStream(buffer); + case "buffer": + return buffer; + default: + throw new Error( + `Type '${ + transferType === undefined ? "undefined" : transferType + }' is not supported` + ); + } +} + export async function streamToTransferType( stream: Readable, transferType: TransferType, @@ -191,3 +215,18 @@ async function convertAbortErrorName(promise: Promise): Promise { throw error; } } + +export function getExpiryDate(options?: ExpiryOptions): Date { + if (options?.expiresInSeconds && options?.expiresOn) { + throw new Error( + "Only one of 'expiresInSeconds' and 'expiresOn' can be specified." + ); + } + if (options?.expiresInSeconds) { + return new Date(Date.now() + options.expiresInSeconds * 1000); + } + if (options?.expiresOn) { + return options.expiresOn; + } + return new Date(Date.now() + defaultExpiresInSeconds * 1000); // expires in one hour by default +} diff --git a/storage/google/.npmignore b/storage/google/.npmignore new file mode 100644 index 00000000..61f519e9 --- /dev/null +++ b/storage/google/.npmignore @@ -0,0 +1,6 @@ +# start off ignoring everything, and then add back only the files we want +* +!lib/**/*.d.ts +!lib/**/*.js +!lib/**/*.js.map +test diff --git a/storage/google/LICENSE.md b/storage/google/LICENSE.md new file mode 100644 index 00000000..9e4243e6 --- /dev/null +++ b/storage/google/LICENSE.md @@ -0,0 +1,9 @@ +# MIT License + +Copyright © 2022-2023 Bentley Systems, Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/storage/google/README.md b/storage/google/README.md new file mode 100644 index 00000000..2f5d1858 --- /dev/null +++ b/storage/google/README.md @@ -0,0 +1,7 @@ +# @itwin/object-storage-google + +Copyright © Bentley Systems, Incorporated. All rights reserved. See [LICENSE.md](./LICENSE.md) for license terms and full copyright notice. + +## About this package + +This package contains implementations for object storage interfaces exposed by `@itwin/object-storage-core` which allow to consume Google Cloud Storage service. Communication with the Google Cloud APIs is implemented using using [Google Cloud Storage](https://www.npmjs.com/package/@google-cloud/storage) and [Google Cloud Storage Control](https://www.npmjs.com/package/@google-cloud/storage-control) libraries. diff --git a/storage/google/package.json b/storage/google/package.json new file mode 100644 index 00000000..20e26971 --- /dev/null +++ b/storage/google/package.json @@ -0,0 +1,96 @@ +{ + "name": "@itwin/object-storage-google", + "version": "2.2.5", + "description": "Object storage implementation using Google Cloud Storage", + "keywords": [ + "Bentley", + "iTwin", + "iTwin platform", + "Object storage", + "Google" + ], + "homepage": "https://github.com/iTwin/object-storage/tree/main/storage/google", + "repository": { + "type": "git", + "url": "https://github.com/iTwin/object-storage" + }, + "license": "MIT", + "author": { + "name": "Bentley Systems, Inc.", + "url": "https://www.bentley.com" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build": "tsc 1>&2 && npx webpack", + "clean": "rimraf lib && rimraf dist", + "lint": "eslint --resolve-plugins-relative-to node_modules/@itwin/object-storage-common-config ./src/**/*.ts 1>&2", + "lint-fix": "eslint --fix --resolve-plugins-relative-to node_modules/@itwin/object-storage-common-config ./src/**/*.ts 1>&2 && sort-package-json", + "spell-check": "cspell \"**\" --config ./node_modules/@itwin/object-storage-common-config/cspell.json", + "test:unit": "npm run test:unit:backend && npm run test:unit:frontend", + "test:unit:frontend": "cypress run --browser chrome --config-file node_modules/@itwin/object-storage-common-config/unit.cypress.config.ts", + "test:unit:frontend:debug": "cypress open --browser chrome --config-file node_modules/@itwin/object-storage-common-config/unit.cypress.config.ts", + "test:unit:backend": "npm run test:unit:backend:common && npm run test:unit:backend:local", + "test:unit:backend:local": "mocha lib/test/unit/backend/**/*.test.js --color --exit", + "test:unit:backend:common": "node lib/test/unit/backend/RunCommonUnitTests.js", + "test:integration:backend": "npm run test:integration:backend:common && npm run test:integration:backend:local", + "test:integration:backend:local": "mocha lib/test/integration/backend/**/*.test.js --color --exit -t 30000", + "test:integration:backend:common": "node lib/test/integration/backend/RunIntegrationTests.js", + "test:integration:frontend": "npm-run-all -p -r test:integration:frontend:startServer test:integration:frontend:runTests ", + "test:integration:frontend:runTests": "wait-on tcp:1224 -t 30000 && node lib/test/integration/frontend/RunIntegrationTests.js", + "test:integration:frontend:startServer": "node lib/test/integration/frontend/StartServer.js" + }, + "prettier": "@itwin/object-storage-common-config/prettier.json", + "eslintConfig": { + "extends": "./node_modules/@itwin/object-storage-common-config/.eslintrc.js" + }, + "dependencies": { + "@google-cloud/storage": "^7.12.1", + "@google-cloud/storage-control": "^0.2.0", + "@itwin/cloud-agnostic-core": "workspace:*", + "@itwin/object-storage-core": "workspace:*", + "axios": "^1.7.4", + "google-auth-library": "^9.14.0" + }, + "devDependencies": { + "@itwin/object-storage-common-config": "workspace:*", + "@itwin/object-storage-tests-backend": "workspace:*", + "@itwin/object-storage-tests-backend-unit": "workspace:*", + "@itwin/object-storage-tests-frontend": "workspace:*", + "@types/chai": "^4.3.8", + "@types/chai-as-promised": "^7.1.2", + "@types/mocha": "^10.0.1", + "@types/node": "^18.11.18", + "@types/sinon": "^10.0.11", + "chai": "^4.3.10", + "chai-as-promised": "^7.1.1", + "cspell": "^8.16.0", + "cypress": "^13.16.0", + "dotenv": "^16.0.0", + "eslint": "^8.57.1", + "inversify": "^6.0.1", + "mocha": "^10.4.0", + "npm-run-all": "^4.1.5", + "nyc": "^14.0.0", + "reflect-metadata": "^0.1.13", + "rimraf": "^2.6.2", + "sinon": "^14.0.0", + "sort-package-json": "^1.53.1", + "typescript": "^5.1.3", + "wait-on": "^7.2.0", + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4" + }, + "peerDependencies": { + "inversify": "^6.0.1", + "reflect-metadata": "^0.1.13" + }, + "peerDependenciesMeta": { + "inversify": { + "optional": true + }, + "reflect-metadata": { + "optional": true + } + } +} diff --git a/storage/google/src/client/GoogleClientStorage.ts b/storage/google/src/client/GoogleClientStorage.ts new file mode 100644 index 00000000..e2147df7 --- /dev/null +++ b/storage/google/src/client/GoogleClientStorage.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { Readable } from "stream"; + +import { injectable } from "inversify"; + +import { + assertRelativeDirectory, + instanceOfUrlTransferInput, +} from "@itwin/object-storage-core/lib/common/internal"; +import { + assertFileNotEmpty, + assertLocalFile, + bufferToTransferType, + downloadFromUrl, + uploadToUrl, +} from "@itwin/object-storage-core/lib/server/internal"; + +import { + ClientStorage, + TransferData, + UrlDownloadInput, + UrlUploadInput, +} from "@itwin/object-storage-core"; + +import { + GoogleConfigDownloadInput, + GoogleConfigUploadInput, + GoogleUploadInMultiplePartsInput, +} from "../server"; + +import { ClientStorageWrapperFactory } from "./wrappers"; + +@injectable() +export class GoogleClientStorage extends ClientStorage { + public constructor(private _storageFactory: ClientStorageWrapperFactory) { + super(); + } + + public download( + input: (UrlDownloadInput | GoogleConfigDownloadInput) & { + transferType: "buffer"; + } + ): Promise; + + public download( + input: (UrlDownloadInput | GoogleConfigDownloadInput) & { + transferType: "stream"; + } + ): Promise; + + public download( + input: (UrlDownloadInput | GoogleConfigDownloadInput) & { + transferType: "local"; + localPath: string; + } + ): Promise; + + public override async download( + input: UrlDownloadInput | GoogleConfigDownloadInput + ): Promise { + if (instanceOfUrlTransferInput(input)) return await downloadFromUrl(input); + assertRelativeDirectory(input.reference.relativeDirectory); + if (input.transferType === "local") { + assertLocalFile(input.localPath); + } + + const storage = this._storageFactory.createFromToken(input.transferConfig); + const downloadBuffer = await storage.downloadFile( + input.reference, + input.localPath + ); + + if (input.transferType === "local") return input.localPath!; + return bufferToTransferType(downloadBuffer, input.transferType); + } + + public override async upload( + input: UrlUploadInput | GoogleConfigUploadInput + ): Promise { + const isUrlTransfer = instanceOfUrlTransferInput(input); + if (!isUrlTransfer) + assertRelativeDirectory(input.reference.relativeDirectory); + if (typeof input.data === "string") await assertFileNotEmpty(input.data); + + if (isUrlTransfer) + return uploadToUrl(input.url, input.data, input.metadata); + + const storage = this._storageFactory.createFromToken(input.transferConfig); + await storage.uploadFile(input.reference, input.data, input.metadata); + } + + public override async uploadInMultipleParts( + input: GoogleUploadInMultiplePartsInput + ): Promise { + assertRelativeDirectory(input.reference.relativeDirectory); + if (typeof input.data === "string") await assertFileNotEmpty(input.data); + + const storage = this._storageFactory.createFromToken(input.transferConfig); + await storage.uploadFile( + input.reference, + input.data, + input.options?.metadata, + undefined, + input.options?.partSize + ); + } +} diff --git a/storage/google/src/client/GoogleClientStorageBindings.ts b/storage/google/src/client/GoogleClientStorageBindings.ts new file mode 100644 index 00000000..6ff0410c --- /dev/null +++ b/storage/google/src/client/GoogleClientStorageBindings.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { Container } from "inversify"; + +import { + ClientStorage, + ClientStorageDependency, +} from "@itwin/object-storage-core"; + +import { GoogleClientStorage } from "./GoogleClientStorage"; +import { ClientStorageWrapperFactory } from "./wrappers"; + +export class GoogleClientStorageBindings extends ClientStorageDependency { + public readonly dependencyName: string = "google"; + + public override register(container: Container): void { + container.bind(ClientStorageWrapperFactory).toSelf().inSingletonScope(); + container.bind(ClientStorage).to(GoogleClientStorage).inSingletonScope(); + } +} diff --git a/storage/google/src/client/index.ts b/storage/google/src/client/index.ts new file mode 100644 index 00000000..9f883b81 --- /dev/null +++ b/storage/google/src/client/index.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +export * from "../common"; +export * from "./GoogleClientStorage"; +export * from "./GoogleClientStorageBindings"; diff --git a/storage/google/src/client/wrappers/ClientStorageWrapperFactory.ts b/storage/google/src/client/wrappers/ClientStorageWrapperFactory.ts new file mode 100644 index 00000000..cc56b033 --- /dev/null +++ b/storage/google/src/client/wrappers/ClientStorageWrapperFactory.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { StorageWrapperFactory } from "../../server"; + +export class ClientStorageWrapperFactory extends StorageWrapperFactory {} diff --git a/storage/google/src/client/wrappers/index.ts b/storage/google/src/client/wrappers/index.ts new file mode 100644 index 00000000..3f1e10ab --- /dev/null +++ b/storage/google/src/client/wrappers/index.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +export * from "./ClientStorageWrapperFactory"; diff --git a/storage/google/src/common/Helpers.ts b/storage/google/src/common/Helpers.ts new file mode 100644 index 00000000..a62de536 --- /dev/null +++ b/storage/google/src/common/Helpers.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { + assertPrimitiveType, + FalsyValueError, +} from "@itwin/cloud-agnostic-core/lib/internal"; +import { TransferConfig } from "@itwin/object-storage-core/lib/common"; +import { assertTransferConfig } from "@itwin/object-storage-core/lib/common/internal"; + +import { GoogleTransferConfig } from "./Interfaces"; + +export function assertGoogleTransferConfig( + transferConfig: TransferConfig | GoogleTransferConfig +): asserts transferConfig is GoogleTransferConfig { + assertTransferConfig(transferConfig); + if (!("authentication" in transferConfig)) + throw new FalsyValueError("transferConfig.authentication"); + + assertPrimitiveType( + transferConfig.authentication, + "transferConfig.authentication", + "string" + ); + assertPrimitiveType( + transferConfig.baseUrl, + "transferConfig.baseUrl", + "string" + ); + assertPrimitiveType( + transferConfig.expiration, + "transferConfig.expiration", + "object" + ); +} diff --git a/storage/google/src/common/Interfaces.ts b/storage/google/src/common/Interfaces.ts new file mode 100644 index 00000000..16f4cf82 --- /dev/null +++ b/storage/google/src/common/Interfaces.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { + ObjectReference, + TransferConfig, +} from "@itwin/object-storage-core/lib/common"; + +export interface GoogleTransferConfig extends TransferConfig { + authentication: string; + bucketName: string; +} + +export interface GoogleTransferConfigInput { + transferConfig: GoogleTransferConfig; + reference: ObjectReference; +} diff --git a/storage/google/src/common/Types.ts b/storage/google/src/common/Types.ts new file mode 100644 index 00000000..7d34d847 --- /dev/null +++ b/storage/google/src/common/Types.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +/* eslint-disable @typescript-eslint/naming-convention */ + +const types = { + GoogleServer: { + config: Symbol.for("Types.GoogleServer.Config"), + }, +}; + +export { types as Types }; diff --git a/storage/google/src/common/index.ts b/storage/google/src/common/index.ts new file mode 100644 index 00000000..2a9a0d0a --- /dev/null +++ b/storage/google/src/common/index.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +export * from "./Types"; +export * from "./Interfaces"; +export * from "./Helpers"; diff --git a/storage/google/src/frontend/FrontendInterfaces.ts b/storage/google/src/frontend/FrontendInterfaces.ts new file mode 100644 index 00000000..8cb013b7 --- /dev/null +++ b/storage/google/src/frontend/FrontendInterfaces.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { + FrontendConfigDownloadInput, + FrontendConfigUploadInput, + FrontendUploadInMultiplePartsInput, +} from "@itwin/object-storage-core/lib/frontend"; + +import { GoogleTransferConfig } from "../common"; + +export interface FrontendGoogleConfigDownloadInput + extends FrontendConfigDownloadInput { + transferConfig: GoogleTransferConfig; +} + +export interface FrontendGoogleConfigUploadInput + extends FrontendConfigUploadInput { + transferConfig: GoogleTransferConfig; +} + +export interface FrontendGoogleUploadInMultiplePartsInput + extends FrontendUploadInMultiplePartsInput { + transferConfig: GoogleTransferConfig; +} diff --git a/storage/google/src/frontend/GoogleFrontendStorage.ts b/storage/google/src/frontend/GoogleFrontendStorage.ts new file mode 100644 index 00000000..36c21ff6 --- /dev/null +++ b/storage/google/src/frontend/GoogleFrontendStorage.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { injectable } from "inversify"; + +import { + assertRelativeDirectory, + buildObjectKey, + instanceOfUrlTransferInput, +} from "@itwin/object-storage-core/lib/common/internal"; +import { + FrontendStorage, + FrontendTransferData, + FrontendUrlDownloadInput, + FrontendUrlUploadInput, + ObjectReference, +} from "@itwin/object-storage-core/lib/frontend"; +import { + downloadFromUrlFrontend, + uploadToUrlFrontend, + streamToTransferTypeFrontend, +} from "@itwin/object-storage-core/lib/frontend/internal"; + +import { + FrontendGoogleConfigDownloadInput, + FrontendGoogleConfigUploadInput, + FrontendGoogleUploadInMultiplePartsInput, +} from "./FrontendInterfaces"; + +@injectable() +export class GoogleFrontendStorage extends FrontendStorage { + public constructor() { + super(); + } + + public download( + input: (FrontendUrlDownloadInput | FrontendGoogleConfigDownloadInput) & { + transferType: "buffer"; + } + ): Promise; + + public download( + input: (FrontendUrlDownloadInput | FrontendGoogleConfigDownloadInput) & { + transferType: "stream"; + } + ): Promise; + + public async download( + input: FrontendUrlDownloadInput | FrontendGoogleConfigDownloadInput + ): Promise { + if (instanceOfUrlTransferInput(input)) + return downloadFromUrlFrontend(input); + + assertRelativeDirectory(input.reference.relativeDirectory); + + const updatedInput: FrontendUrlDownloadInput = { + url: `https://storage.googleapis.com/storage/v1/b/${ + input.transferConfig.bucketName + }/o/${encodeURIComponent(this.objectName(input.reference))}?alt=media`, + transferType: input.transferType, + }; + + return downloadFromUrlFrontend(updatedInput, { + Authorization: input.transferConfig.authentication, + }); + } + + private objectName(reference: ObjectReference): string { + return buildObjectKey(reference); + } + + public async upload( + input: FrontendUrlUploadInput | FrontendGoogleConfigUploadInput + ): Promise { + const { data } = input; + + if (instanceOfUrlTransferInput(input)) + return uploadToUrlFrontend(input.url, data, "PUT"); + + assertRelativeDirectory(input.reference.relativeDirectory); + const url = `https://storage.googleapis.com/upload/storage/v1/b/${ + input.transferConfig.bucketName + }/o?uploadType=media&name=${this.objectName(input.reference)}`; + return uploadToUrlFrontend(url, input.data, "POST", { + Authorization: input.transferConfig.authentication, + "Content-Type": "application/octet-stream", + }); + } + + // eslint-disable-next-line @typescript-eslint/require-await + public async uploadInMultipleParts( + input: FrontendGoogleUploadInMultiplePartsInput + ): Promise { + assertRelativeDirectory(input.reference.relativeDirectory); + + const url = `https://storage.googleapis.com/upload/storage/v1/b/${ + input.transferConfig.bucketName + }/o?uploadType=media&name=${this.objectName(input.reference)}`; + const data = await streamToTransferTypeFrontend(input.data, "buffer"); + + return uploadToUrlFrontend(url, data, "POST", { + Authorization: input.transferConfig.authentication, + "Content-Type": "application/octet-stream", + }); + } +} diff --git a/storage/google/src/frontend/GoogleFrontendStorageBindings.ts b/storage/google/src/frontend/GoogleFrontendStorageBindings.ts new file mode 100644 index 00000000..7d2195b6 --- /dev/null +++ b/storage/google/src/frontend/GoogleFrontendStorageBindings.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { Container } from "inversify"; + +import { + FrontendStorage, + FrontendStorageDependency, +} from "@itwin/object-storage-core/lib/frontend"; + +import { GoogleFrontendStorage } from "./GoogleFrontendStorage"; + +export class GoogleFrontendStorageBindings extends FrontendStorageDependency { + public readonly dependencyName: string = "google"; + + public override register(container: Container): void { + container + .bind(FrontendStorage) + .to(GoogleFrontendStorage) + .inSingletonScope(); + } +} diff --git a/storage/google/src/frontend/index.ts b/storage/google/src/frontend/index.ts new file mode 100644 index 00000000..fca66873 --- /dev/null +++ b/storage/google/src/frontend/index.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +export * from "../common"; +export * from "./FrontendInterfaces"; +export * from "./GoogleFrontendStorage"; +export * from "./GoogleFrontendStorageBindings"; diff --git a/storage/google/src/server/GoogleServerStorage.ts b/storage/google/src/server/GoogleServerStorage.ts new file mode 100644 index 00000000..35229768 --- /dev/null +++ b/storage/google/src/server/GoogleServerStorage.ts @@ -0,0 +1,269 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { Readable } from "stream"; + +import { inject, injectable } from "inversify"; + +import { + assertRelativeDirectory, + buildObjectDirectoryString, +} from "@itwin/object-storage-core/lib/common/internal"; +import { + assertFileNotEmpty, + assertLocalFile, + bufferToTransferType, + getExpiryDate, +} from "@itwin/object-storage-core/lib/server/internal"; + +import { + BaseDirectory, + ContentHeaders, + EntityPageListIterator, + ExpiryOptions, + Metadata, + MultipartUploadData, + MultipartUploadOptions, + ObjectDirectory, + ObjectProperties, + ObjectReference, + ServerStorage, + TransferConfig, + TransferData, + TransferType, +} from "@itwin/object-storage-core"; + +import { GoogleTransferConfig, Types } from "../common"; + +import { StorageWrapper } from "./wrappers"; +import { GoogleStorageConfig } from "./wrappers/GoogleStorageConfig"; +import { StorageControlClientWrapper } from "./wrappers/StorageControlClientWrapper"; + +@injectable() +export class GoogleServerStorage extends ServerStorage { + private readonly _bucketName: string; + + constructor( + private readonly _storage: StorageWrapper, + private readonly _storageControl: StorageControlClientWrapper, + @inject(Types.GoogleServer.config) + config: GoogleStorageConfig + ) { + super(); + this._bucketName = config.bucketName; + } + + public get bucketName(): string { + return this._bucketName; + } + + public download( + reference: ObjectReference, + transferType: "buffer" + ): Promise; + + public download( + reference: ObjectReference, + transferType: "stream" + ): Promise; + + public async download( + reference: ObjectReference, + transferType: "local", + localPath?: string + ): Promise; + + public override async download( + reference: ObjectReference, + transferType: TransferType, + localPath?: string + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + if (transferType === "local") { + assertLocalFile(localPath); + await this._storage.downloadFile(reference, localPath); + return localPath; + } + + const downloadBuffer = await this._storage.downloadFile(reference); + + return bufferToTransferType(downloadBuffer, transferType); + } + + public override async upload( + reference: ObjectReference, + data: TransferData, + metadata?: Metadata, + headers?: ContentHeaders + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + if (typeof data === "string") await assertFileNotEmpty(data); + + await this._storage.uploadFile(reference, data, metadata, headers); + } + + public override async uploadInMultipleParts( + reference: ObjectReference, + data: MultipartUploadData, + options?: MultipartUploadOptions, + headers?: ContentHeaders + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + if (typeof data === "string") await assertFileNotEmpty(data); + + await this._storage.uploadFile( + reference, + data, + options?.metadata, + headers, + options?.partSize + ); + } + + public override createBaseDirectory(directory: BaseDirectory): Promise { + return this._storageControl.createManagedFolder(directory.baseDirectory); + } + + public override getListDirectoriesPagedIterator( + _maxPageSize: number + ): EntityPageListIterator { + return new EntityPageListIterator(async () => { + return this._storageControl.getManagedFoldersNextPage({ + maxPageSize: _maxPageSize, + }); + }); + } + + public override getListObjectsPagedIterator( + directory: BaseDirectory, + maxPageSize: number + ): EntityPageListIterator { + return new EntityPageListIterator(async () => { + return this._storage.getFilesNextPage({ + directory: directory, + maxPageSize: maxPageSize, + }); + }); + } + + public override list(directory: BaseDirectory): Promise { + return this.listObjects(directory); + } + + public override async deleteBaseDirectory( + directory: BaseDirectory + ): Promise { + for await (const objectPage of this.getListObjectsPagedIterator( + directory, + 100 + )) { + for (const object of objectPage) { + await this.deleteObject(object); + } + } + + return this._storageControl.deleteManagedFolder(directory.baseDirectory); + } + + public override deleteObject(reference: ObjectReference): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return this._storage.deleteFile(reference); + } + + public override async baseDirectoryExists( + directory: BaseDirectory + ): Promise { + return await this._storageControl.managedFolderExists( + directory.baseDirectory + ); + } + + public override async objectExists( + reference: ObjectReference + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return await this._storage.fileExists(reference); + } + + public override async copyObject( + sourceStorage: ServerStorage, + sourceReference: ObjectReference, + targetReference: ObjectReference + ): Promise { + return await this._storage.copyFile( + (sourceStorage as GoogleServerStorage).bucketName, + sourceReference, + targetReference + ); + } + + public override async updateMetadata( + reference: ObjectReference, + metadata: Metadata + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return await this._storage.updateMetadata(reference, metadata); + } + + public override async getObjectProperties( + reference: ObjectReference + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return await this._storage.getObjectProperties(reference); + } + + public override async getDownloadUrl( + reference: ObjectReference, + expiry?: ExpiryOptions + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return await this._storage.getSignedUrl("read", reference, expiry); + } + + public override async getUploadUrl( + reference: ObjectReference, + expiry?: ExpiryOptions + ): Promise { + assertRelativeDirectory(reference.relativeDirectory); + return await this._storage.getSignedUrl("write", reference, expiry); + } + + public override async getDownloadConfig( + directory: ObjectDirectory, + expiry?: ExpiryOptions + ): Promise { + assertRelativeDirectory(directory.relativeDirectory); + getExpiryDate(expiry); + + const directoryPath = buildObjectDirectoryString(directory); + return await this._storageControl.createAccessToken("read", directoryPath); + } + + public override async getUploadConfig( + directory: ObjectDirectory, + expiry?: ExpiryOptions + ): Promise { + assertRelativeDirectory(directory.relativeDirectory); + getExpiryDate(expiry); + + const directoryPath = buildObjectDirectoryString(directory); + return await this._storageControl.createAccessToken("write", directoryPath); + } + + public override async getDirectoryAccessConfig( + directory: ObjectDirectory, + expiry?: ExpiryOptions + ): Promise { + assertRelativeDirectory(directory.relativeDirectory); + getExpiryDate(expiry); + + const directoryPath = buildObjectDirectoryString(directory); + return await this._storageControl.createAccessToken("user", directoryPath); + } + + public override async releaseResources(): Promise { + await this._storageControl.releaseResources(); + } +} diff --git a/storage/google/src/server/GoogleServerStorageBindings.ts b/storage/google/src/server/GoogleServerStorageBindings.ts new file mode 100644 index 00000000..5da7a766 --- /dev/null +++ b/storage/google/src/server/GoogleServerStorageBindings.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { Container } from "inversify"; + +import { ConfigError } from "@itwin/cloud-agnostic-core/lib/internal"; + +import { DependencyConfig } from "@itwin/cloud-agnostic-core"; +import { + ServerStorage, + ServerStorageDependency, +} from "@itwin/object-storage-core"; + +import { Types } from "../common"; +import { + GoogleStorageConfig, + StorageWrapper, + StorageWrapperFactory, +} from "../server/wrappers"; + +import { GoogleServerStorage } from "./GoogleServerStorage"; +import { StorageControlClientWrapper } from "./wrappers/StorageControlClientWrapper"; + +export type GoogleServerStorageBindingsConfig = GoogleStorageConfig & + DependencyConfig; + +export class GoogleServerStorageBindings extends ServerStorageDependency { + public readonly dependencyName: string = "google"; + + public override register( + container: Container, + config: GoogleServerStorageBindingsConfig + ): void { + if (!config.projectId) + throw new ConfigError("projectId"); + if (!config.bucketName) + throw new ConfigError("bucketName"); + + container + .bind(Types.GoogleServer.config) + .toConstantValue(config); + container.bind(ServerStorage).to(GoogleServerStorage).inSingletonScope(); + + container.bind(StorageControlClientWrapper).toSelf().inSingletonScope(); + container.bind(StorageWrapperFactory).toSelf().inSingletonScope(); + container + .bind(StorageWrapper) + .toDynamicValue((context) => { + const factory = context.container.get(StorageWrapperFactory); + const config = context.container.get( + Types.GoogleServer.config + ); + return factory.createDefaultApplication(config); + }) + .inSingletonScope(); + } +} diff --git a/storage/google/src/server/Interfaces.ts b/storage/google/src/server/Interfaces.ts new file mode 100644 index 00000000..90ec38bb --- /dev/null +++ b/storage/google/src/server/Interfaces.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { + ConfigDownloadInput, + ConfigUploadInput, + UploadInMultiplePartsInput, +} from "@itwin/object-storage-core"; + +import { GoogleTransferConfig } from "../common"; + +export interface GoogleConfigDownloadInput extends ConfigDownloadInput { + transferConfig: GoogleTransferConfig; +} + +export interface GoogleConfigUploadInput extends ConfigUploadInput { + transferConfig: GoogleTransferConfig; +} + +export interface GoogleUploadInMultiplePartsInput + extends UploadInMultiplePartsInput { + transferConfig: GoogleTransferConfig; +} diff --git a/storage/google/src/server/index.ts b/storage/google/src/server/index.ts new file mode 100644 index 00000000..1638a2d4 --- /dev/null +++ b/storage/google/src/server/index.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +export * from "../common"; +export * from "./wrappers"; +export * from "./Interfaces"; +export * from "./GoogleServerStorage"; +export * from "./GoogleServerStorageBindings"; diff --git a/storage/google/src/server/wrappers/GoogleStorageConfig.ts b/storage/google/src/server/wrappers/GoogleStorageConfig.ts new file mode 100644 index 00000000..d198567f --- /dev/null +++ b/storage/google/src/server/wrappers/GoogleStorageConfig.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +export interface GoogleStorageConfig { + projectId: string; + bucketName: string; +} diff --git a/storage/google/src/server/wrappers/Helpers.ts b/storage/google/src/server/wrappers/Helpers.ts new file mode 100644 index 00000000..d8db0a7d --- /dev/null +++ b/storage/google/src/server/wrappers/Helpers.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +export type GoogleStorageConfigType = "read" | "write" | "user"; + +export function roleFromConfigType( + configType: GoogleStorageConfigType +): string { + switch (configType) { + case "read": + return "inRole:roles/storage.objectViewer"; + case "write": + return "inRole:roles/storage.objectCreator"; + case "user": + return "inRole:roles/storage.objectUser"; + } +} diff --git a/storage/google/src/server/wrappers/StorageControlClientWrapper.ts b/storage/google/src/server/wrappers/StorageControlClientWrapper.ts new file mode 100644 index 00000000..8488c2b5 --- /dev/null +++ b/storage/google/src/server/wrappers/StorageControlClientWrapper.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { StorageControlClient } from "@google-cloud/storage-control"; +import { DownscopedClient, GoogleAuth } from "google-auth-library"; +import { inject, injectable } from "inversify"; + +import { + BaseDirectory, + EntityCollectionPage, +} from "@itwin/object-storage-core"; + +import { GoogleTransferConfig, Types } from "../../common"; + +import { GoogleStorageConfig } from "./GoogleStorageConfig"; +import { GoogleStorageConfigType, roleFromConfigType } from "./Helpers"; + +export interface GoogleError extends Error { + code: number; +} + +export function isGoogleError(error: unknown): error is GoogleError { + return error instanceof Error && (error as GoogleError).code !== undefined; +} + +@injectable() +export class StorageControlClientWrapper { + private readonly _client: StorageControlClient; + constructor( + @inject(Types.GoogleServer.config) + private readonly _config: GoogleStorageConfig + ) { + this._client = new StorageControlClient({ + projectId: this._config.projectId, + }); + } + + public async createManagedFolder(folderName: string): Promise { + const parent = this.bucketPath; + await this._client.createManagedFolder({ + parent, + managedFolderId: folderName, + }); + } + + public async deleteManagedFolder(folderName: string): Promise { + const managedFolder = this.managedFolderPath(folderName); + try { + await this._client.deleteManagedFolder({ + name: managedFolder, + }); + } catch (error) { + if (isGoogleError(error) && error.code != 5) { + throw error; + } + } + } + + public async getManagedFoldersNextPage(options: { + maxPageSize: number; + continuationToken?: string; + }): Promise> { + const parent = this.bucketPath; + const [folders, _, response] = await this._client.listManagedFolders( + { + parent, + pageSize: options.maxPageSize, + pageToken: options.continuationToken, + }, + { autoPaginate: false } + ); + const directories: BaseDirectory[] = + folders.map((entry) => { + return { + baseDirectory: entry.name?.split("/").slice(5, -1).join("/"), + } as BaseDirectory; + }) ?? []; + const continuationToken = response?.nextPageToken; + const page: EntityCollectionPage = { + entities: directories, + next: !continuationToken + ? undefined + : () => + this.getManagedFoldersNextPage({ + maxPageSize: options.maxPageSize, + continuationToken: continuationToken, + }), + }; + return page; + } + + public async getManagedFolders(): Promise { + const parent = this._client.bucketPath("_", this._config.bucketName); + const [folders, _1, _2] = await this._client.listManagedFolders({ + parent, + }); + const directories: BaseDirectory[] = + folders.map( + (entry) => ({ baseDirectory: entry.name } as BaseDirectory) + ) ?? []; + return directories; + } + + public async managedFolderExists(folderName: string): Promise { + const managedFolder = this.managedFolderPath(folderName); + try { + await this._client.getManagedFolder({ + name: managedFolder, + }); + return true; + } catch (error) { + if (isGoogleError(error) && error.code == 5) { + return false; + } + throw error; + } + } + + public async createAccessToken( + action: GoogleStorageConfigType, + folderName: string + ): Promise { + const cab = { + accessBoundary: { + accessBoundaryRules: [ + { + availableResource: this.bucketUri, + availablePermissions: [roleFromConfigType(action)], + availabilityCondition: { + expression: + `resource.name.startsWith('${this.bucketPath}/objects/${folderName}') || ` + + `api.getAttribute('storage.googleapis.com/objectListPrefix', '')` + + `.startsWith('${folderName}/')`, + }, + }, + ], + }, + }; + const googleAuth = new GoogleAuth({ + scopes: "https://www.googleapis.com/auth/cloud-platform", + projectId: this._config.projectId, + }); + const client = await googleAuth.getClient(); + const downscopedClient = new DownscopedClient(client, cab); + const { token, expirationTime } = await downscopedClient.getAccessToken(); + return { + baseUrl: this.bucketUri, + authentication: `Bearer ${token!}`, + expiration: new Date(expirationTime!), + bucketName: this._config.bucketName, + }; + } + + private managedFolderPath(folderName: string): string { + return this._client.managedFolderPath( + "_", + this._config.bucketName, + folderName + ); + } + + private get bucketPath(): string { + return this._client.bucketPath("_", this._config.bucketName); + } + + private get bucketUri(): string { + return `//storage.googleapis.com/${this.bucketPath}`; + } + + public async releaseResources(): Promise { + await this._client.close(); + } +} diff --git a/storage/google/src/server/wrappers/StorageWrapper.ts b/storage/google/src/server/wrappers/StorageWrapper.ts new file mode 100644 index 00000000..a972725b --- /dev/null +++ b/storage/google/src/server/wrappers/StorageWrapper.ts @@ -0,0 +1,211 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { Readable } from "stream"; + +import { + ApiError, + Bucket, + File, + SaveOptions, + Storage, + UploadOptions, +} from "@google-cloud/storage"; + +import { buildObjectKey } from "@itwin/object-storage-core/lib/common/internal"; +import { + getExpiryDate, + streamToBuffer, +} from "@itwin/object-storage-core/lib/server/internal"; + +import { + BaseDirectory, + ContentHeaders, + EntityCollectionPage, + ExpiryOptions, + Metadata, + ObjectProperties, + ObjectReference, + TransferData, +} from "@itwin/object-storage-core"; + +import { GoogleStorageConfig } from "./GoogleStorageConfig"; + +export class StorageWrapper { + constructor( + private readonly _storage: Storage, + private readonly _config: Pick + ) {} + + public async downloadFile( + reference: ObjectReference, + destination?: string + ): Promise { + const [buffer] = await this.fileObject(reference).download({ + destination, + }); + return buffer; + } + + public async uploadFile( + reference: ObjectReference, + data?: TransferData, + metadata?: Metadata, + headers?: ContentHeaders, + chunkSize?: number + ): Promise { + const options: SaveOptions = { + metadata: { ...metadata }, + chunkSize: chunkSize, + }; + options.contentType = headers?.contentType; + options.gzip = headers?.contentEncoding === "gzip"; + if (typeof data === "string") { + const uploadOptions: UploadOptions = { ...options }; + uploadOptions.destination = buildObjectKey(reference); + await this.bucketObject().upload(data, uploadOptions); + } else { + const saveData: Buffer | string = + data instanceof Readable ? await streamToBuffer(data) : data ?? ""; + await this.fileObject(reference).save(saveData, options); + } + if (metadata || headers?.cacheControl || headers?.contentEncoding) { + const [updatedMetadata] = await this.fileObject(reference).getMetadata(); + updatedMetadata.metadata = { ...updatedMetadata.metadata, ...metadata }; + if (headers?.cacheControl) + updatedMetadata.cacheControl = headers.cacheControl; + if (headers?.contentEncoding) + updatedMetadata.contentEncoding = headers.contentEncoding; + await this.fileObject(reference).setMetadata(updatedMetadata); + } + } + + public async getFilesNextPage(options: { + directory: BaseDirectory; + maxPageSize: number; + continuationToken?: string; + }): Promise> { + const [files, nextPageToken] = await this.bucketObject().getFiles({ + prefix: options.directory.baseDirectory, + maxResults: options.maxPageSize, + pageToken: options.continuationToken, + }); + return { + entities: files.map((file) => { + const parts = file.name.split("/"); + const reference: ObjectReference = { + objectName: parts.length > 1 ? parts[parts.length - 1] : "", + baseDirectory: parts[0] || options.directory.baseDirectory, + }; + if (parts.length > 2) { + reference.relativeDirectory = parts + .slice(1, parts.length - 1) + .join("/"); + } + return reference; + }), + next: nextPageToken?.pageToken + ? () => + this.getFilesNextPage({ + ...options, + continuationToken: nextPageToken.pageToken, + }) + : undefined, + }; + } + + public async deleteFile(reference: ObjectReference): Promise { + try { + await this.fileObject(reference).delete(); + } catch (error) { + if (error instanceof ApiError && error.code === 404) return; + throw error; + } + } + + public async copyFile( + sourceBucket: string, + sourceReference: ObjectReference, + destinationReference: ObjectReference + ): Promise { + await this._storage + .bucket(sourceBucket) + .file(buildObjectKey(sourceReference)) + .copy(this.fileObject(destinationReference)); + } + + public async getSignedUrl( + action: "read" | "write", + reference: ObjectReference, + expiry?: ExpiryOptions + ): Promise { + const expires = getExpiryDate(expiry); + const [url] = await this.fileObject(reference).getSignedUrl({ + action, + expires, + version: "v4", + }); + return url; + } + + public async updateMetadata( + reference: ObjectReference, + metadata: Metadata + ): Promise { + const [updatedMetadata] = await this.fileObject(reference).getMetadata(); + if (updatedMetadata.metadata == null) updatedMetadata.metadata = {}; + for (const key of Object.keys(updatedMetadata.metadata)) { + if (key in metadata) continue; + updatedMetadata.metadata[key] = null; + } + for (const key of Object.keys(metadata)) { + updatedMetadata.metadata[key] = metadata[key]; + } + + await this.fileObject(reference).setMetadata(updatedMetadata); + } + + public async getObjectProperties( + reference: ObjectReference + ): Promise { + const [fileMetadata] = await this.fileObject(reference).getMetadata(); + const metadata: Metadata = {}; + if (fileMetadata.metadata) { + for (const key of Object.keys(fileMetadata.metadata)) { + metadata[key] = String(fileMetadata.metadata[key]); + } + } + return { + reference, + lastModified: fileMetadata.updated + ? new Date(fileMetadata.updated) + : new Date(), + size: Number(fileMetadata.size), + metadata: metadata, + contentType: fileMetadata.contentType, + contentEncoding: fileMetadata.contentEncoding, + cacheControl: fileMetadata.cacheControl, + }; + } + + public async fileExists(reference: ObjectReference): Promise { + try { + await this.fileObject(reference).get(); + return true; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error) { + if (error instanceof ApiError && error.code === 404) return false; + throw error; + } + } + + private bucketObject(): Bucket { + return this._storage.bucket(this._config.bucketName); + } + + private fileObject(reference: ObjectReference): File { + return this.bucketObject().file(buildObjectKey(reference)); + } +} diff --git a/storage/google/src/server/wrappers/StorageWrapperFactory.ts b/storage/google/src/server/wrappers/StorageWrapperFactory.ts new file mode 100644 index 00000000..84bb16fd --- /dev/null +++ b/storage/google/src/server/wrappers/StorageWrapperFactory.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +import { Storage } from "@google-cloud/storage"; +import { injectable } from "inversify"; + +import { GoogleTransferConfig } from "../../common/Interfaces"; + +import { GoogleStorageConfig } from "./GoogleStorageConfig"; +import { StorageWrapper } from "./StorageWrapper"; + +@injectable() +export class StorageWrapperFactory { + public createDefaultApplication(config: GoogleStorageConfig): StorageWrapper { + return new StorageWrapper( + new Storage({ projectId: config.projectId }), + config + ); + } + + public createFromToken(transferConfig: GoogleTransferConfig): StorageWrapper { + return new StorageWrapper( + new Storage({ + token: transferConfig.authentication, + }), + { bucketName: transferConfig.bucketName } + ); + } +} diff --git a/storage/google/src/server/wrappers/index.ts b/storage/google/src/server/wrappers/index.ts new file mode 100644 index 00000000..1e27fc8e --- /dev/null +++ b/storage/google/src/server/wrappers/index.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +export * from "./GoogleStorageConfig"; +export * from "./Helpers"; +export * from "./StorageWrapper"; +export * from "./StorageWrapperFactory"; diff --git a/storage/google/src/test/integration/IntegrationTests.md b/storage/google/src/test/integration/IntegrationTests.md new file mode 100644 index 00000000..e67e469e --- /dev/null +++ b/storage/google/src/test/integration/IntegrationTests.md @@ -0,0 +1,20 @@ +# Running integration tests locally + +## Setup + +Integration tests require a bucket in Google Cloud that will be used to perform actual file operations. Users can create it using [Google Cloud Console](https://https://console.cloud.google.com//). Tests can work with default application credentials from running `gcloud auth login`. + +To configure backend tests create a `.env` file in `storage/google/src/test/integration` directory and define values for the following variables: + +- `TEST_GOOGLE_BUCKET_NAME` - test bucket name. +- `TEST_GOOGLE_PROJECT_ID` - id of the Google Cloud project. +- `TEST_GOOGLE_SECONDARY_BUCKET_NAME` - name of secondary test bucket. + +## Backend tests + +Backend tests test `ClientStorage` and `ServerStorage` provided by this package. + +To run them: + +- Run the `npm run test:integration:backend` command. +- Launch the "Backend Google Integration tests" configuration using VS Code. diff --git a/storage/google/src/test/integration/ServerStorageConfigProvider.ts b/storage/google/src/test/integration/ServerStorageConfigProvider.ts new file mode 100644 index 00000000..6b833954 --- /dev/null +++ b/storage/google/src/test/integration/ServerStorageConfigProvider.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import * as path from "path"; + +import * as dotenv from "dotenv"; + +import { GoogleStorageConfig } from "../../server/wrappers"; + +export interface SecondaryBucket { + secondaryBucketName: string; +} + +export class ServerStorageConfigProvider { + public get(): GoogleStorageConfig & SecondaryBucket { + const envFilePath = path.resolve( + __dirname, + "..", + "..", + "..", + "src", + "test", + "integration", + ".env" + ); + dotenv.config({ path: envFilePath }); + + const config: GoogleStorageConfig & SecondaryBucket = { + bucketName: process.env.TEST_GOOGLE_BUCKET_NAME!, + projectId: process.env.TEST_GOOGLE_PROJECT_ID!, + secondaryBucketName: process.env.TEST_GOOGLE_SECONDARY_BUCKET_NAME!, + }; + return config; + } +} diff --git a/storage/google/src/test/integration/backend/GoogleServerStorage.test.ts b/storage/google/src/test/integration/backend/GoogleServerStorage.test.ts new file mode 100644 index 00000000..8bfc7a97 --- /dev/null +++ b/storage/google/src/test/integration/backend/GoogleServerStorage.test.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { Storage } from "@google-cloud/storage"; + +import { + buildObjectKey, + buildObjectReference, +} from "@itwin/object-storage-core/lib/common/internal"; + +import { ObjectReference, TransferConfig } from "@itwin/object-storage-core"; +import { + TestRemoteDirectory, + TestRemoteDirectoryManager, + testDeleteObjectWithTransferConfig, + testListObjectsWithTransferConfig, +} from "@itwin/object-storage-tests-backend"; + +import { + assertGoogleTransferConfig, + GoogleServerStorage, + StorageWrapper, +} from "../../../server"; +import { StorageControlClientWrapper } from "../../../server/wrappers/StorageControlClientWrapper"; +import { ServerStorageConfigProvider } from "../ServerStorageConfigProvider"; + +describe(`${GoogleServerStorage.name} internal tests`, () => { + const serverStorageConfig = new ServerStorageConfigProvider().get(); + + const serverStorage = createGoogleServerStorage(); + const testDirectoryManager = new TestRemoteDirectoryManager(serverStorage); + + const deleteFunction = async ( + reference: ObjectReference, + transferConfig: TransferConfig + ) => { + assertGoogleTransferConfig(transferConfig); + const client = new Storage({ + token: transferConfig.authentication, + }); + await client + .bucket(serverStorageConfig.bucketName) + .file(buildObjectKey(reference)) + .delete(); + }; + + const listFunction = async ( + baseDirectory: string, + transferConfig: TransferConfig + ) => { + assertGoogleTransferConfig(transferConfig); + + const client = new Storage({ + token: transferConfig.authentication, + }); + const [files] = await client + .bucket(serverStorageConfig.bucketName) + .getFiles({ + prefix: baseDirectory, + maxResults: 100, + }); + + const references = + files.map((file) => buildObjectReference(file.name)) ?? []; + + const nonEmptyReferences = references.filter((ref) => !!ref.objectName); + return nonEmptyReferences; + }; + + beforeEach(async () => { + await testDirectoryManager.purgeCreatedDirectories(); + }); + + after(async () => { + await testDirectoryManager.purgeCreatedDirectories(); + }); + + it(`should delete object using transfer config`, async () => { + const testDirectory: TestRemoteDirectory = + await testDirectoryManager.createNew(); + + await testDeleteObjectWithTransferConfig( + serverStorage, + testDirectory, + deleteFunction + ); + }); + + it(`should list objects using transfer config`, async () => { + const testDirectory: TestRemoteDirectory = + await testDirectoryManager.createNew(); + + await testListObjectsWithTransferConfig( + serverStorage, + testDirectory, + listFunction + ); + }); + + function createGoogleServerStorage() { + const storage = new StorageWrapper( + new Storage({ projectId: serverStorageConfig.projectId }), + serverStorageConfig + ); + + const storageControl = new StorageControlClientWrapper(serverStorageConfig); + + return new GoogleServerStorage( + storage, + storageControl, + serverStorageConfig + ); + } +}); diff --git a/storage/google/src/test/integration/backend/RunIntegrationTests.ts b/storage/google/src/test/integration/backend/RunIntegrationTests.ts new file mode 100644 index 00000000..c25a24e4 --- /dev/null +++ b/storage/google/src/test/integration/backend/RunIntegrationTests.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { StorageIntegrationTests } from "@itwin/object-storage-tests-backend"; + +import { GoogleClientStorageBindings } from "../../../client/GoogleClientStorageBindings"; +import { GoogleServerStorageBindings } from "../../../server"; +import { ServerStorageConfigProvider } from "../ServerStorageConfigProvider"; + +const serverStorageConfig = new ServerStorageConfigProvider().get(); +const config = { + // eslint-disable-next-line @typescript-eslint/naming-convention + ServerStorage: [ + { + dependencyName: "google", + instanceName: "primary", + ...serverStorageConfig, + }, + { + dependencyName: "google", + instanceName: "secondary", + ...serverStorageConfig, + bucket: serverStorageConfig.secondaryBucketName, + }, + ], + // eslint-disable-next-line @typescript-eslint/naming-convention + ClientStorage: { + dependencyName: "google", + bucket: serverStorageConfig.bucketName, + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + FrontendStorage: { + dependencyName: "google", + bucket: serverStorageConfig.bucketName, + }, +}; + +/** + * There is no way to specify expiration time for the tokens + * without writing our own authentication implementation. + * + * There's also no way to specify an abort signal for storage + * client methods. + * + * Object metadata is managed through separate requests from + * upload and does not work with upload using signed URLs. + */ +const mochaGrepPattern = + "(?!.*should use (expiresInSeconds|expiresOn) if set|.*should cancel file download to path using transfer config|.*should upload a file with metadata .* to URL)^.*$"; + +const tests = new StorageIntegrationTests( + config, + GoogleServerStorageBindings, + GoogleClientStorageBindings, + mochaGrepPattern +); +tests.start().catch((err) => { + process.exitCode = 1; + throw err; +}); diff --git a/storage/google/src/test/integration/frontend/GoogleFrontendTestSetup.ts b/storage/google/src/test/integration/frontend/GoogleFrontendTestSetup.ts new file mode 100644 index 00000000..85ceef2b --- /dev/null +++ b/storage/google/src/test/integration/frontend/GoogleFrontendTestSetup.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { FrontendStorageTestSetup } from "@itwin/object-storage-tests-frontend/lib/FrontendStorageTestSetup"; + +import { GoogleFrontendStorageBindings } from "../../../frontend"; + +const config = { + // eslint-disable-next-line @typescript-eslint/naming-convention + FrontendStorage: { + dependencyName: "google", + }, +}; +const setup = new FrontendStorageTestSetup( + config, + GoogleFrontendStorageBindings, + "http://localhost:1224" +); +setup.setGlobals(); diff --git a/storage/google/src/test/integration/frontend/RunIntegrationTests.ts b/storage/google/src/test/integration/frontend/RunIntegrationTests.ts new file mode 100644 index 00000000..73400d2a --- /dev/null +++ b/storage/google/src/test/integration/frontend/RunIntegrationTests.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import * as path from "path"; + +import { FrontendStorageIntegrationTests } from "@itwin/object-storage-tests-frontend"; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { bundledScriptFileName } = require(path.resolve( + __dirname, + "..", + "..", + "..", + "..", + "webpack.config.js" +)); +const bundledSetupScript = path.resolve( + __dirname, + "..", + "..", + "..", + "..", + "dist", + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + bundledScriptFileName +); + +const tests = new FrontendStorageIntegrationTests(bundledSetupScript, { + SKIP_FILE_WITH_METADATA_UPLOAD_TO_URL: "1", + SKIP_FILE_WITH_METADATA_UPLOAD_WITH_CONFIG: "1", +}); +tests.start().catch((err) => { + process.exitCode = 1; + throw err; +}); diff --git a/storage/google/src/test/integration/frontend/StartServer.ts b/storage/google/src/test/integration/frontend/StartServer.ts new file mode 100644 index 00000000..7a7fc135 --- /dev/null +++ b/storage/google/src/test/integration/frontend/StartServer.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { + DependenciesConfig, + Types as DependencyTypes, +} from "@itwin/cloud-agnostic-core"; +import { ServerStorageProxy } from "@itwin/object-storage-tests-frontend"; + +import { GoogleServerStorageBindings } from "../../../server"; +import { ServerStorageConfigProvider } from "../ServerStorageConfigProvider"; + +function run(): void { + const backendServer = new ServerStorageProxy(); + + backendServer.container + .bind(DependencyTypes.dependenciesConfig) + .toConstantValue({ + // eslint-disable-next-line @typescript-eslint/naming-convention + ServerStorage: { + dependencyName: "google", + ...new ServerStorageConfigProvider().get(), + }, + }); + backendServer.useBindings(GoogleServerStorageBindings); + backendServer.start({ port: 1224 }); +} + +run(); diff --git a/storage/google/src/test/unit/backend/GoogleClientStorageBindings.test.ts b/storage/google/src/test/unit/backend/GoogleClientStorageBindings.test.ts new file mode 100644 index 00000000..9f582fb6 --- /dev/null +++ b/storage/google/src/test/unit/backend/GoogleClientStorageBindings.test.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { Container } from "inversify"; + +import { ClientStorage } from "@itwin/object-storage-core"; +import { + DependencyBindingsTestCase, + testBindings, +} from "@itwin/object-storage-tests-backend-unit"; + +import { + GoogleClientStorage, + GoogleClientStorageBindings, +} from "../../../client"; +import { ClientStorageWrapperFactory } from "../../../client/wrappers"; + +describe(`${GoogleClientStorageBindings.name}`, () => { + const clientBindings = new GoogleClientStorageBindings(); + + describe(`${clientBindings.register.name}()`, () => { + const bindingsTestCases: DependencyBindingsTestCase[] = [ + { + testedClassIdentifier: ClientStorage.name, + testedFunction: (container: Container) => container.get(ClientStorage), + expectedCtor: GoogleClientStorage, + }, + { + testedClassIdentifier: ClientStorageWrapperFactory.name, + testedFunction: (container: Container) => + container.get(ClientStorageWrapperFactory), + expectedCtor: ClientStorageWrapperFactory, + }, + ]; + testBindings(clientBindings, undefined, bindingsTestCases); + }); +}); diff --git a/storage/google/src/test/unit/backend/GoogleServerStorageBindings.test.ts b/storage/google/src/test/unit/backend/GoogleServerStorageBindings.test.ts new file mode 100644 index 00000000..f87855c4 --- /dev/null +++ b/storage/google/src/test/unit/backend/GoogleServerStorageBindings.test.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { Container } from "inversify"; + +import { ServerStorage } from "@itwin/object-storage-core"; +import { + DependencyBindingsTestCase, + InvalidConfigTestCase, + testBindings, + testInvalidServerConfig, +} from "@itwin/object-storage-tests-backend-unit"; + +import { Types } from "../../../common"; +import { + GoogleServerStorageBindings, + GoogleServerStorageBindingsConfig, + GoogleServerStorage, + StorageWrapperFactory, + StorageWrapper, +} from "../../../server"; +import { StorageControlClientWrapper } from "../../../server/wrappers/StorageControlClientWrapper"; + +describe(`${GoogleServerStorageBindings.name}`, () => { + const serverBindings = new GoogleServerStorageBindings(); + + describe(`${serverBindings.register.name}()`, () => { + const invalidConfigTestCases: InvalidConfigTestCase[] = [ + { + config: { + dependencyName: "google", + } as unknown as GoogleServerStorageBindingsConfig, + expectedErrorMessage: "projectId is not defined in configuration", + }, + { + config: { + dependencyName: "google", + projectId: "testProjectId", + } as unknown as GoogleServerStorageBindingsConfig, + expectedErrorMessage: "bucketName is not defined in configuration", + }, + ]; + testInvalidServerConfig(serverBindings, invalidConfigTestCases); + + const config: GoogleServerStorageBindingsConfig = { + dependencyName: "google", + projectId: "testProjectId", + bucketName: "testBucketName", + }; + const bindingsTestCases: DependencyBindingsTestCase[] = []; + [ + { + testedClassIdentifier: ServerStorage.name, + testedFunction: (container: Container) => container.get(ServerStorage), + expectedCtor: GoogleServerStorage, + }, + { + testedClassIdentifier: Types.GoogleServer.config.toString(), + testedFunction: (container: Container) => + container.get( + Types.GoogleServer.config + ), + expectedCtor: Object, + }, + { + testedClassIdentifier: StorageControlClientWrapper.name, + testedFunction: (container: Container) => + container.get(StorageControlClientWrapper), + expectedCtor: StorageControlClientWrapper, + }, + { + testedClassIdentifier: StorageWrapperFactory.name, + testedFunction: (container: Container) => + container.get(StorageWrapperFactory), + expectedCtor: StorageWrapperFactory, + }, + { + testedClassIdentifier: StorageWrapper.name, + testedFunction: (container: Container) => container.get(StorageWrapper), + expectedCtor: StorageWrapper, + }, + ]; + testBindings(serverBindings, config, bindingsTestCases); + }); +}); diff --git a/storage/google/src/test/unit/backend/RunCommonUnitTests.ts b/storage/google/src/test/unit/backend/RunCommonUnitTests.ts new file mode 100644 index 00000000..65b47c24 --- /dev/null +++ b/storage/google/src/test/unit/backend/RunCommonUnitTests.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { Container } from "inversify"; +import { createStubInstance } from "sinon"; + +import { StorageUnitTests } from "@itwin/object-storage-tests-backend-unit"; + +import { GoogleClientStorageBindings } from "../../../client"; +import { + GoogleServerStorageBindings, + GoogleServerStorageBindingsConfig, + StorageWrapper, +} from "../../../server"; +import { StorageControlClientWrapper } from "../../../server/wrappers/StorageControlClientWrapper"; + +const dependencyName = "google"; +const googleTestConfig = { + // eslint-disable-next-line @typescript-eslint/naming-convention + ServerStorage: { + dependencyName, + projectId: "testProjectId", + bucketName: "testBucketName", + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + ClientStorage: { + dependencyName, + }, + // eslint-disable-next-line @typescript-eslint/naming-convention + FrontendStorage: { + dependencyName, + }, +}; + +class TestGoogleServerStorageBindings extends GoogleServerStorageBindings { + public override register( + container: Container, + config: GoogleServerStorageBindingsConfig + ): void { + super.register(container, config); + + const mockStorageControlClient = createStubInstance( + StorageControlClientWrapper + ); + const mockStorageWrapper = createStubInstance(StorageWrapper); + + container + .rebind(StorageControlClientWrapper) + .toConstantValue(mockStorageControlClient); + container.rebind(StorageWrapper).toConstantValue(mockStorageWrapper); + } +} + +class TestGoogleClientStorageBindings extends GoogleClientStorageBindings { + public override register(container: Container): void { + super.register(container); + + const mockStorageWrapper = createStubInstance(StorageWrapper); + + container.rebind(StorageWrapper).toConstantValue(mockStorageWrapper); + } +} + +const tests = new StorageUnitTests( + googleTestConfig, + TestGoogleServerStorageBindings, + TestGoogleClientStorageBindings +); +tests.start().catch((err) => { + process.exitCode = 1; + throw err; +}); diff --git a/storage/google/src/test/unit/frontend/GoogleFrontendStorageBindings.test.ts b/storage/google/src/test/unit/frontend/GoogleFrontendStorageBindings.test.ts new file mode 100644 index 00000000..2234c31a --- /dev/null +++ b/storage/google/src/test/unit/frontend/GoogleFrontendStorageBindings.test.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "reflect-metadata"; + +import { Container } from "inversify"; + +import { FrontendStorage } from "@itwin/object-storage-core/lib/frontend"; +import { + DependencyBindingsTestCase, + testBindings, +} from "@itwin/object-storage-tests-backend-unit/lib/shared/test-templates/BindingsTests"; + +import { + GoogleFrontendStorage, + GoogleFrontendStorageBindings, +} from "../../../frontend"; + +describe(`${GoogleFrontendStorageBindings.name}`, () => { + const frontendBindings = new GoogleFrontendStorageBindings(); + + describe(`${frontendBindings.register.name}()`, () => { + const bindingsTestCases: DependencyBindingsTestCase[] = [ + { + testedClassIdentifier: FrontendStorage.name, + testedFunction: (container: Container) => + container.get(FrontendStorage), + expectedCtor: GoogleFrontendStorage, + }, + ]; + testBindings(frontendBindings, undefined, bindingsTestCases); + }); +}); diff --git a/storage/google/tsconfig.json b/storage/google/tsconfig.json new file mode 100644 index 00000000..146586b6 --- /dev/null +++ b/storage/google/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/@itwin/object-storage-common-config/tsconfig.json", +} diff --git a/storage/google/webpack.config.js b/storage/google/webpack.config.js new file mode 100644 index 00000000..814d5158 --- /dev/null +++ b/storage/google/webpack.config.js @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require("path"); + +const setupScriptFileName = "GoogleFrontendTestSetup.js"; +const bundledScriptFileName = setupScriptFileName; + +const webpackConfig = { + mode: "development", + optimization: { + minimize: false + }, + entry: { + app: path.resolve(__dirname, "lib", "test", "integration", "frontend", setupScriptFileName) + }, + output: { + filename: bundledScriptFileName, + path: path.resolve(__dirname, "dist") + }, +}; + +module.exports = { + default: webpackConfig, + bundledScriptFileName +}; diff --git a/storage/minio/package.json b/storage/minio/package.json index ae78d2a5..069761a9 100644 --- a/storage/minio/package.json +++ b/storage/minio/package.json @@ -69,9 +69,9 @@ "@types/sinon": "^10.0.11", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "cspell": "^5.18.5", - "cypress": "^13.12.0", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "cypress": "^13.16.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "npm-run-all": "^4.1.5", @@ -82,8 +82,8 @@ "sort-package-json": "^1.53.1", "typescript": "^5.1.3", "wait-on": "^7.2.0", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2" + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4" }, "peerDependencies": { "inversify": "^6.0.1", diff --git a/storage/minio/src/frontend/internal/Helpers.ts b/storage/minio/src/frontend/internal/Helpers.ts index d5929edd..166dc548 100644 --- a/storage/minio/src/frontend/internal/Helpers.ts +++ b/storage/minio/src/frontend/internal/Helpers.ts @@ -41,5 +41,5 @@ export async function handleMinioUrlUploadFrontend( const headers = metadata ? metadataToHeaders(metadata, "x-amz-meta-") : undefined; - return uploadToUrlFrontend(url, data, headers); + return uploadToUrlFrontend(url, data, "PUT", headers); } diff --git a/storage/oss/package.json b/storage/oss/package.json index 26dd2e89..2c2675ac 100644 --- a/storage/oss/package.json +++ b/storage/oss/package.json @@ -59,9 +59,9 @@ "@types/node": "^18.11.18", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "cspell": "^5.18.5", + "cspell": "^8.16.0", "dotenv": "^16.0.0", - "eslint": "^8.42.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "npm-run-all": "^4.1.5", @@ -71,8 +71,8 @@ "sort-package-json": "^1.53.1", "typescript": "^5.1.3", "wait-on": "^7.2.0", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2" + "webpack": "^5.96.1", + "webpack-cli": "^5.1.4" }, "peerDependencies": { "inversify": "^6.0.1", diff --git a/storage/s3/package.json b/storage/s3/package.json index 2d6d741e..defcaa0e 100644 --- a/storage/s3/package.json +++ b/storage/s3/package.json @@ -62,9 +62,9 @@ "@types/sinon": "^10.0.11", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", - "cspell": "^5.18.5", - "cypress": "^13.12.0", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "cypress": "^13.16.0", + "eslint": "^8.57.1", "inversify": "^6.0.1", "mocha": "^10.4.0", "nyc": "^14.0.0", diff --git a/storage/s3/src/client/S3ClientStorage.ts b/storage/s3/src/client/S3ClientStorage.ts index f9e1ba2c..b3766439 100644 --- a/storage/s3/src/client/S3ClientStorage.ts +++ b/storage/s3/src/client/S3ClientStorage.ts @@ -11,13 +11,11 @@ import { inject, injectable } from "inversify"; import { assertRelativeDirectory, instanceOfUrlTransferInput, - metadataToHeaders, } from "@itwin/object-storage-core/lib/common/internal"; import { assertFileNotEmpty, downloadFromUrl, streamToTransferType, - uploadToUrl, } from "@itwin/object-storage-core/lib/server/internal"; import { @@ -37,6 +35,8 @@ import { } from "../server"; import { createAndUseClient } from "../server/internal"; +import { handleS3UrlUpload } from "./internal/Helpers"; + @injectable() export class S3ClientStorage extends ClientStorage { constructor( @@ -91,6 +91,8 @@ export class S3ClientStorage extends ClientStorage { public override async upload( input: UrlUploadInput | S3ConfigUploadInput ): Promise { + if (instanceOfUrlTransferInput(input)) return handleS3UrlUpload(input); + let { data } = input; const { metadata } = input; @@ -102,19 +104,11 @@ export class S3ClientStorage extends ClientStorage { data = createReadStream(data); } - if (instanceOfUrlTransferInput(input)) - return uploadToUrl( - input.url, - data, - metadata ? metadataToHeaders(metadata, "x-amz-meta-") : undefined - ); - else { - return createAndUseClient( - () => this._clientWrapperFactory.create(input.transferConfig), - async (clientWrapper: S3ClientWrapper) => - clientWrapper.upload(input.reference, data, metadata) - ); - } + return createAndUseClient( + () => this._clientWrapperFactory.create(input.transferConfig), + async (clientWrapper: S3ClientWrapper) => + clientWrapper.upload(input.reference, data, metadata) + ); } public async uploadInMultipleParts( diff --git a/storage/s3/src/client/internal/Helpers.ts b/storage/s3/src/client/internal/Helpers.ts new file mode 100644 index 00000000..f4b6baf9 --- /dev/null +++ b/storage/s3/src/client/internal/Helpers.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import { createReadStream } from "fs"; +import { Readable } from "stream"; + +import { metadataToHeaders } from "@itwin/object-storage-core/lib/common/internal"; +import { + assertFileNotEmpty, + streamToBuffer, + uploadToUrl, +} from "@itwin/object-storage-core/lib/server/internal"; + +import { UrlUploadInput } from "@itwin/object-storage-core"; + +export async function handleS3UrlUpload(input: UrlUploadInput): Promise { + const { data, metadata, url } = input; + + let dataToUpload: Buffer; + if (data instanceof Buffer) dataToUpload = data; + else if (data instanceof Readable) dataToUpload = await streamToBuffer(data); + else { + await assertFileNotEmpty(data); + dataToUpload = await streamToBuffer(createReadStream(data)); + } + const metadataHeaders = metadata + ? metadataToHeaders(metadata, "x-amz-meta-") + : {}; + const headers = { + ...metadataHeaders, + // eslint-disable-next-line @typescript-eslint/naming-convention + "Content-Length": dataToUpload.byteLength.toString(), + }; + return uploadToUrl(url, dataToUpload, headers); +} diff --git a/storage/s3/src/frontend/S3FrontendStorage.ts b/storage/s3/src/frontend/S3FrontendStorage.ts index 1ec0d8df..ee37ab16 100644 --- a/storage/s3/src/frontend/S3FrontendStorage.ts +++ b/storage/s3/src/frontend/S3FrontendStorage.ts @@ -77,6 +77,7 @@ export class S3FrontendStorage extends FrontendStorage { return uploadToUrlFrontend( input.url, data, + "PUT", metadata ? metadataToHeaders(metadata, "x-amz-meta-") : undefined ); else { diff --git a/storage/s3/src/server/S3ServerStorage.ts b/storage/s3/src/server/S3ServerStorage.ts index ce0a74dc..dbb27fe8 100644 --- a/storage/s3/src/server/S3ServerStorage.ts +++ b/storage/s3/src/server/S3ServerStorage.ts @@ -293,7 +293,8 @@ export class S3ServerStorage extends ServerStorage { return this._s3Client.bucketName; } - public releaseResources(): void { + public releaseResources(): Promise { this._s3Client.releaseResources(); + return Promise.resolve(); } } diff --git a/tests/backend-storage-unit/package.json b/tests/backend-storage-unit/package.json index e6376b5b..e7b66642 100644 --- a/tests/backend-storage-unit/package.json +++ b/tests/backend-storage-unit/package.json @@ -50,8 +50,8 @@ "@types/chai-as-promised": "^7.1.2", "@types/mocha": "^10.0.1", "@types/node": "^18.11.18", - "cspell": "^5.18.5", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "eslint": "^8.57.1", "rimraf": "^2.6.2", "sort-package-json": "^1.53.1", "typescript": "^5.1.3" diff --git a/tests/backend-storage/package.json b/tests/backend-storage/package.json index f6ecd2e5..314051d5 100644 --- a/tests/backend-storage/package.json +++ b/tests/backend-storage/package.json @@ -56,8 +56,8 @@ "@types/mocha": "^10.0.1", "@types/node": "^18.11.18", "@types/yargs": "^15.0.5", - "cspell": "^5.18.5", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "eslint": "^8.57.1", "rimraf": "^2.6.2", "sort-package-json": "^1.53.1", "typescript": "^5.1.3" diff --git a/tests/backend-storage/src/common-tests/ServerStorage.test.ts b/tests/backend-storage/src/common-tests/ServerStorage.test.ts index 707bb6d7..530f285b 100644 --- a/tests/backend-storage/src/common-tests/ServerStorage.test.ts +++ b/tests/backend-storage/src/common-tests/ServerStorage.test.ts @@ -382,7 +382,7 @@ describe(`${ServerStorage.name}: ${serverStorage.constructor.name}`, () => { }); describe(`${serverStorage.getListObjectsPagedIterator.name}()`, () => { - it("Should list objects without exceeding maxPageSize per page.", async () => { + it("should list objects without exceeding maxPageSize per page.", async () => { const testDirectory: TestRemoteDirectory = await testDirectoryManager.createNew(); await createObjectsReferences(testDirectory, 3); @@ -395,7 +395,7 @@ describe(`${ServerStorage.name}: ${serverStorage.constructor.name}`, () => { expect(entityPage.length).to.be.lte(maxPageSize); }); - it("Should list created objects without duplicates", async () => { + it("should list created objects without duplicates", async () => { const testDirectory: TestRemoteDirectory = await testDirectoryManager.createNew(); const createdObjects = 3; @@ -500,7 +500,7 @@ describe(`${ServerStorage.name}: ${serverStorage.constructor.name}`, () => { }); describe(`${serverStorage.getListDirectoriesPagedIterator.name}()`, () => { - it("Should list directories without exceeding maxPageSize per page.", async () => { + it("should list directories without exceeding maxPageSize per page.", async () => { await testDirectoryManager.createNew(); await testDirectoryManager.createNew(); await testDirectoryManager.createNew(); @@ -511,7 +511,7 @@ describe(`${ServerStorage.name}: ${serverStorage.constructor.name}`, () => { expect(entityPage.length).to.be.lte(maxPageSize); }); - it("Should list created directories without duplicates", async () => { + it("should list created directories without duplicates", async () => { const testDirectory1: TestRemoteDirectory = await testDirectoryManager.createNew(); const testDirectory2: TestRemoteDirectory = diff --git a/tests/backend-storage/src/common-tests/StorageIntegrationTests.ts b/tests/backend-storage/src/common-tests/StorageIntegrationTests.ts index 007c6435..056b8097 100644 --- a/tests/backend-storage/src/common-tests/StorageIntegrationTests.ts +++ b/tests/backend-storage/src/common-tests/StorageIntegrationTests.ts @@ -73,10 +73,10 @@ export class StorageIntegrationTests extends Bindable { .filter((file) => file.toLowerCase().endsWith("test.js")) .forEach((file) => mocha.addFile(join(testDirectory, file))); - return new Promise((resolve) => + return new Promise((resolve, reject) => mocha.run((failures: number) => { process.exitCode = failures ? 1 : 0; - serverStorage.releaseResources(); + serverStorage.releaseResources().catch((error) => reject(error)); resolve(); }) ); diff --git a/tests/frontend-storage/cypress/integration/FrontendStorage.test.ts b/tests/frontend-storage/cypress/integration/FrontendStorage.test.ts index a55a5197..5e8e81b3 100644 --- a/tests/frontend-storage/cypress/integration/FrontendStorage.test.ts +++ b/tests/frontend-storage/cypress/integration/FrontendStorage.test.ts @@ -33,9 +33,11 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name}`, () => { before(() => { cy.visit(`${serverBaseUrl}/index.html`); }); + beforeEach(async () => { await directoryManager.purgeCreatedDirectories(); }); + after(async () => { await directoryManager.purgeCreatedDirectories(); }); @@ -45,21 +47,23 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name}`, () => { it("should upload a file from buffer to URL", async () => { await testUploadFromBufferToUrl(test); }); + it("should upload a file with relative dir from buffer to URL", async () => { await testUploadFromBufferToUrl(test, { useRelativeDir: true }) }); - if (!Cypress.env("SKIP_FILE_WITH_METADATA_UPLOAD_TO_URL")) { - it("should upload a file with metadata from buffer to URL", async () => { - await testUploadFromBufferToUrl(test, { useMetadata: true }) - }); - } + it("should upload a file with metadata from buffer to URL", async function () { + if (Cypress.env("SKIP_FILE_WITH_METADATA_UPLOAD_TO_URL")) + this.skip(); + await testUploadFromBufferToUrl(test, { useMetadata: true }) + }); }); describe(`${frontendStorage.download.name}() & ${serverStorage.getDownloadUrl.name}()`, () => { it("should download a file to buffer from URL", async () => { await testDownloadFromUrlToBuffer(test); }); + it(`should download a file to stream from URL`, async () => { await testDownloadFromUrlToStream(test); }); @@ -71,28 +75,39 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name}`, () => { it("should upload a file from buffer using transfer config", async () => { await testUploadFromBufferWithConfig(test); }); + it(`should upload a file with relative directory from buffer using transfer config`, async () => { await testUploadFromBufferWithConfig(test, { useRelativeDir: true }); }); - it(`should upload a file from buffer with metadata using transfer config`, async () => { + + it(`should upload a file from buffer with metadata using transfer config`, async function () { + if (Cypress.env("SKIP_FILE_WITH_METADATA_UPLOAD_WITH_CONFIG")) + this.skip(); await testUploadFromBufferWithConfig(test, { useMetadata: true }); }); }); + describe(`${frontendStorage.uploadInMultipleParts.name}() & ${serverStorage.getUploadConfig.name}()`, () => { it(`should upload a file from stream in multiple parts`, async () => { await testUploadMultipart(test); }); + it(`should upload a file with relative directory from stream in multiple parts`, async () => { await testUploadMultipart(test, { useRelativeDir: true }); }); - it(`should upload a file from stream with metadata in multiple parts`, async () => { + + it(`should upload a file from stream with metadata in multiple parts`, async function () { + if (Cypress.env("SKIP_FILE_WITH_METADATA_UPLOAD_WITH_CONFIG")) + this.skip(); await testUploadMultipart(test, { useMetadata: true }); }); }) + describe(`${frontendStorage.download.name}() & ${serverStorage.getDownloadConfig.name}()`, () => { it(`should download a file to buffer using transfer config`, async () => { await testDownloadToBufferWithConfig(test); }); + it(`should download a file to stream using transfer config`, async () => { await testDownloadToStreamWithConfig(test); }); @@ -113,6 +128,7 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name} (Input va expect(caughtError).to.not.be.undefined; expect((caughtError as Error).message).to.equal("Relative directory cannot contain backslashes."); } + const invalidRelativeDirInput = { reference: { baseDirectory: "testBaseDirectory", @@ -144,6 +160,7 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name} (Input va ); }); }); + describe(`${frontendStorage.upload.name}()`, () => { it("should throw if relativeDirectory is invalid (buffer)", async () => { await testRelativeDirectoryValidation( @@ -154,6 +171,7 @@ describe(`${FrontendStorage.name}: ${frontendStorage.constructor.name} (Input va ); }); }); + describe(`${frontendStorage.uploadInMultipleParts.name}()`, () => { it("should throw if relativeDirectory is invalid (stream)", async () => { await testRelativeDirectoryValidation( diff --git a/tests/frontend-storage/package.json b/tests/frontend-storage/package.json index 61a93c8e..fe83b0ea 100644 --- a/tests/frontend-storage/package.json +++ b/tests/frontend-storage/package.json @@ -40,7 +40,7 @@ "dependencies": { "@itwin/cloud-agnostic-core": "workspace:*", "@itwin/object-storage-core": "workspace:*", - "cypress": "^13.12.0", + "cypress": "^13.16.0", "inversify": "^6.0.1" }, "devDependencies": { @@ -48,9 +48,8 @@ "@types/express": "^4.17.13", "@types/node": "^18.11.18", "axios": "^1.7.4", - "cross-env": "^7.0.3", - "cspell": "^5.18.5", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "eslint": "^8.57.1", "express": "^4.18.0", "rimraf": "^2.6.2", "sort-package-json": "^1.53.1", diff --git a/utils/common-config/cspell.json b/utils/common-config/cspell.json index e06ffa52..53f7352d 100644 --- a/utils/common-config/cspell.json +++ b/utils/common-config/cspell.json @@ -1,23 +1,24 @@ { - "version": "0.1", + "version": "0.2", "language": "en", "ignorePaths": [ - "node_modules/**", - "src/assets/**", - "lib/**", - "dist/**", + "**/node_modules/**", + "**/src/assets/**", + "**/lib/**", + "**/dist/**", "*.log", "tsconfig.json", "package.json", "**/cypress/support/**" ], "words": [ - "itwin", - "inversify", - "minio", "alicloud", + "Downscoped", + "iframes", + "inversify", + "itwin", "kbps", - "presigner", - "iframes" + "minio", + "presigner" ] } diff --git a/utils/common-config/package.json b/utils/common-config/package.json index bb7de12f..fa66695b 100644 --- a/utils/common-config/package.json +++ b/utils/common-config/package.json @@ -28,8 +28,8 @@ "@rushstack/eslint-patch": "^1.2.0", "@typescript-eslint/eslint-plugin": "^5.59.9", "@typescript-eslint/parser": "^5.59.9", - "cspell": "^5.18.5", - "eslint": "^8.42.0", + "cspell": "^8.16.0", + "eslint": "^8.57.1", "eslint-config-prettier": "^8.8.0", "eslint-import-resolver-node": "^0.3.7", "eslint-plugin-deprecation": "^1.4.1",