diff --git a/.github/DISCUSSION_TEMPLATE/feature-request.yaml b/.github/DISCUSSION_TEMPLATE/feature-request.yaml index 73d36af7c3..3fdc84636b 100644 --- a/.github/DISCUSSION_TEMPLATE/feature-request.yaml +++ b/.github/DISCUSSION_TEMPLATE/feature-request.yaml @@ -6,6 +6,14 @@ body: attributes: value: | Please use this form to request new feature for Immich + + - type: checkboxes + attributes: + label: I have searched the existing feature requests to make sure this is not a duplicate request. + options: + - label: "Yes" + required: true + - type: textarea id: feature attributes: diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 1325fb02c2..12ffc89ea2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -87,6 +87,16 @@ body: validations: required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant logs below. (code formatting is + enabled, no need for backticks) + render: shell + validations: + required: false + - type: textarea attributes: label: Additional information diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index c3c3fea017..99574590f8 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -58,7 +58,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Login to GitHub Container Registry uses: docker/login-action@v3 diff --git a/.github/workflows/docker-cleanup.yml b/.github/workflows/docker-cleanup.yml index fd404e84ef..4811579f74 100644 --- a/.github/workflows/docker-cleanup.yml +++ b/.github/workflows/docker-cleanup.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Clean temporary images if: "${{ env.TOKEN != '' }}" - uses: stumpylog/image-cleaner-action/ephemeral@v0.5.0 + uses: stumpylog/image-cleaner-action/ephemeral@v0.6.0 with: token: "${{ env.TOKEN }}" owner: "immich-app" @@ -64,7 +64,7 @@ jobs: steps: - name: Clean untagged images if: "${{ env.TOKEN != '' }}" - uses: stumpylog/image-cleaner-action/untagged@v0.5.0 + uses: stumpylog/image-cleaner-action/untagged@v0.6.0 with: token: "${{ env.TOKEN }}" owner: "immich-app" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c1df0a6edf..9e21e4c6f0 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -66,7 +66,7 @@ jobs: uses: docker/setup-qemu-action@v3.0.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.2.0 + uses: docker/setup-buildx-action@v3.3.0 - name: Login to Docker Hub # Only push to Docker Hub when making a release diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a5df111d7..d74c961393 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,19 +10,6 @@ concurrency: cancel-in-progress: true jobs: - server-e2e-jobs: - name: Server (e2e-jobs) - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Run e2e tests - run: make server-e2e-jobs - doc-tests: name: Docs runs-on: ubuntu-latest diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..cd61814ff8 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,5 @@ +/.github/ @bo0tzz +/docker/ @bo0tzz +/server/ @danieldietzler +/machine-learning/ @mertalev +/e2e/ @danieldietzler diff --git a/Makefile b/Makefile index 55875e732b..ede70237fe 100644 --- a/Makefile +++ b/Makefile @@ -16,9 +16,6 @@ stage: pull-stage: docker compose -f ./docker/docker-compose.staging.yml pull -server-e2e-jobs: - docker compose -f ./server/e2e/docker-compose.server-e2e.yml up --renew-anon-volumes --abort-on-container-exit --exit-code-from immich-server --remove-orphans --build - .PHONY: e2e e2e: docker compose -f ./e2e/docker-compose.yml up --build -V --remove-orphans diff --git a/cli/.eslintrc.cjs b/cli/.eslintrc.cjs index 18a48ac7e1..fe8044df81 100644 --- a/cli/.eslintrc.cjs +++ b/cli/.eslintrc.cjs @@ -21,6 +21,7 @@ module.exports = { 'unicorn/prefer-module': 'off', 'unicorn/prevent-abbreviations': 'off', 'unicorn/no-process-exit': 'off', + 'unicorn/import-style': 'off', curly: 2, 'prettier/prettier': 0, }, diff --git a/cli/Dockerfile b/cli/Dockerfile index 6195d8d822..18f38fb4ab 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine3.19@sha256:ef3f47741e161900ddd07addcaca7e76534a9205e4cd73b2ed091ba339004a75 as core +FROM node:20-alpine3.19@sha256:7e227295e96f5b00aa79555ae166f50610940d888fc2e321cf36304cbd17d7d6 as core WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ diff --git a/cli/package-lock.json b/cli/package-lock.json index 563cf7cd5f..11154fec52 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -30,7 +30,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", + "eslint-plugin-unicorn": "^52.0.0", "glob": "^10.3.1", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -54,8 +54,8 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.11.0", - "typescript": "^5.3.3" + "@types/node": "^20.12.7", + "typescript": "^5.4.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1193,12 +1193,6 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1230,9 +1224,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1251,22 +1245,22 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1286,15 +1280,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -1314,13 +1308,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1331,15 +1325,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1358,9 +1352,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1371,19 +1365,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1399,18 +1393,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1424,13 +1418,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1447,9 +1441,9 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -1464,24 +1458,23 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.4.0" + "vitest": "1.5.0" } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -1489,12 +1482,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1530,9 +1523,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1544,9 +1537,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1556,9 +1549,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -1909,12 +1902,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/core-js-compat": { "version": "3.36.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", @@ -2194,9 +2181,9 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -3139,9 +3126,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -4258,9 +4245,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "engines": { "node": ">=14.0.0" @@ -4368,9 +4355,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4431,20 +4418,6 @@ "punycode": "^2.1.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4456,9 +4429,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -4511,9 +4484,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -4552,16 +4525,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -4573,9 +4546,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -4590,8 +4563,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/cli/package.json b/cli/package.json index d33de8918e..97d1cfd3ef 100644 --- a/cli/package.json +++ b/cli/package.json @@ -28,7 +28,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", + "eslint-plugin-unicorn": "^52.0.0", "glob": "^10.3.1", "mock-fs": "^5.2.0", "prettier": "^3.2.5", diff --git a/cli/src/commands/asset.ts b/cli/src/commands/asset.ts index aa45ce5470..67c38af6e3 100644 --- a/cli/src/commands/asset.ts +++ b/cli/src/commands/asset.ts @@ -56,14 +56,13 @@ class UploadFile extends File { export const upload = async (paths: string[], baseOptions: BaseOptions, options: UploadOptionsDto) => { await authenticate(baseOptions); - const files = await scan(paths, options); - if (files.length === 0) { + const scanFiles = await scan(paths, options); + if (scanFiles.length === 0) { console.log('No files found, exiting'); return; } - const { newFiles, duplicates } = await checkForDuplicates(files, options); - + const { newFiles, duplicates } = await checkForDuplicates(scanFiles, options); const newAssets = await uploadFiles(newFiles, options); await updateAlbums([...newAssets, ...duplicates], options); await deleteFiles(newFiles, options); @@ -84,7 +83,12 @@ const scan = async (pathsToCrawl: string[], options: UploadOptionsDto) => { return files; }; -const checkForDuplicates = async (files: string[], { concurrency }: UploadOptionsDto) => { +const checkForDuplicates = async (files: string[], { concurrency, skipHash }: UploadOptionsDto) => { + if (skipHash) { + console.log('Skipping hash check, assuming all files are new'); + return { newFiles: files, duplicates: [] }; + } + const progressBar = new SingleBar( { format: 'Checking files | {bar} | {percentage}% | ETA: {eta}s | {value}/{total} assets' }, Presets.shades_classic, @@ -147,17 +151,32 @@ const uploadFiles = async (files: string[], { dryRun, concurrency }: UploadOptio uploadProgress.start(totalSize, 0); uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) }); - let totalSizeUploaded = 0; + let duplicateCount = 0; + let duplicateSize = 0; + let successCount = 0; + let successSize = 0; + const newAssets: Asset[] = []; + try { for (const items of chunk(files, concurrency)) { await Promise.all( items.map(async (filepath) => { const stats = statsMap.get(filepath) as Stats; const response = await uploadFile(filepath, stats); - totalSizeUploaded += stats.size ?? 0; - uploadProgress.update(totalSizeUploaded, { value_formatted: byteSize(totalSizeUploaded) }); + newAssets.push({ id: response.id, filepath }); + + if (response.duplicate) { + duplicateCount++; + duplicateSize += stats.size ?? 0; + } else { + successCount++; + successSize += stats.size ?? 0; + } + + uploadProgress.update(successSize, { value_formatted: byteSize(successSize + duplicateSize) }); + return response; }), ); @@ -166,7 +185,10 @@ const uploadFiles = async (files: string[], { dryRun, concurrency }: UploadOptio uploadProgress.stop(); } - console.log(`Successfully uploaded ${newAssets.length} asset${s(newAssets.length)} (${byteSize(totalSizeUploaded)})`); + console.log(`Successfully uploaded ${successCount} new asset${s(successCount)} (${byteSize(successSize)})`); + if (duplicateCount > 0) { + console.log(`Skipped ${duplicateCount} duplicate asset${s(duplicateCount)} (${byteSize(duplicateSize)})`); + } return newAssets; }; diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 1bcb5e3272..7f1f0f1a41 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -76,7 +76,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:dec2018ae55885fed717f25c289b8c9cff0bf5fbb9e619fb49b6161ac493c016 + image: prom/prometheus@sha256:4f6c47e39a9064028766e8c95890ed15690c30f00c4ba14e7ce6ae1ded0295b1 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus @@ -88,7 +88,7 @@ services: command: ['./run.sh', '-disable-reporting'] ports: - 3000:3000 - image: grafana/grafana:10.4.1-ubuntu@sha256:65e0e7d0f0b001cb0478bce5093bff917677dc308dd27a0aa4b3ac38e4fd877c + image: grafana/grafana:10.4.2-ubuntu@sha256:4f55071b556fb03f12b41423c98a185ed6695ed9ff2558e35805f0dd765fd958 volumes: - grafana-data:/var/lib/grafana diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7f27798c5e..9516ea9ca7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -69,9 +69,8 @@ services: POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} volumes: - - pgdata:/var/lib/postgresql/data + - ${DB_DATA_LOCATION}:/var/lib/postgresql/data restart: always volumes: - pgdata: model-cache: diff --git a/docker/example.env b/docker/example.env index 40d379f482..234ba3a401 100644 --- a/docker/example.env +++ b/docker/example.env @@ -14,5 +14,6 @@ DB_PASSWORD=postgres DB_HOSTNAME=immich_postgres DB_USERNAME=postgres DB_DATABASE_NAME=immich +DB_DATA_LOCATION=./postgres REDIS_HOSTNAME=immich_redis diff --git a/docker/hwaccel.ml.yml b/docker/hwaccel.ml.yml index 5305271df9..3afc5ceafd 100644 --- a/docker/hwaccel.ml.yml +++ b/docker/hwaccel.ml.yml @@ -27,8 +27,6 @@ services: count: 1 capabilities: - gpu - - compute - - video openvino: device_cgroup_rules: diff --git a/docs/README.md b/docs/README.md index aaba2fa1e1..0c6c2c27be 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # Website -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. +This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. ### Installation diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 452f1d91eb..033863bf51 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -6,7 +6,7 @@ The admin password can be reset by running the [reset-admin-password](/docs/administration/server-commands.md) command on the immich-server. -### How can I see list of all users in Immich? +### How can I see a list of all users in Immich? You can see the list of all users by running [list-users](/docs/administration/server-commands.md) Command on the Immich-server. @@ -24,14 +24,14 @@ You can see the list of all users by running [list-users](/docs/administration/s ### I cannot log into the application after an update. What can I do? -First, verify that the mobile app and server are both running the same version (major and minor). +Verify that the mobile app and server are both running the same version (major and minor). :::note -App store updates sometimes take longer because the stores (Google play store and Apple app store) -need to approve the update first which may take some time. +App store updates sometimes take longer because the stores (Google Play Store and Apple App Store) +need to approve the update first, and it can take some time. ::: -If you still cannot login to the app, try the following: +If you still cannot log in to the app, try the following: - Check the mobile logs - Make sure login credentials are correct by logging in on the web app @@ -40,6 +40,11 @@ If you still cannot login to the app, try the following: ## Assets +### Does Immich change the file? + +No, Immich does not touch the original file under any circumstances, +all edited metadata are saved in the companion sidecar file and the database. + ### Can I add my existing photo library? Yes, with an [External Library](/docs/features/libraries.md). @@ -50,11 +55,11 @@ Template changes will only apply to _new_ assets. To retroactively apply the tem ### Why are only photos and not videos being uploaded to Immich? -This often happens when using a reverse proxy (such as nginx or Cloudflare tunnel) in front of Immich. Make sure to set your reverse proxy to allow large `POST` requests. In `nginx`, set `client_max_body_size 50000M;` or similar. Also check the disk space of your reverse proxy, in some cases proxies cache requests to disk before passing them on, and if disk space runs out the request fails. +This often happens when using a reverse proxy (such as Nginx or Cloudflare tunnel) in front of Immich. Make sure to set your reverse proxy to allow large `POST` requests. In `nginx`, set `client_max_body_size 50000M;` or similar. Also, check the disk space of your reverse proxy. In some cases, proxies cache requests to disk before passing them on, and if disk space runs out, the request fails. ### Why are some photos stored in the file system with the wrong date? -There are a few different scenarios that can lead to this situation. The solution is to run the storage migration job again. The job is only _automatically_ run once per asset, after upload. If metadata extraction originally failed, the jobs were cleared/cancelled, etc. the job may not have run automatically the first time. +There are a few different scenarios that can lead to this situation. The solution is to rerun the storage migration job. The job is only automatically run once per asset after upload. If metadata extraction originally failed, the jobs were cleared/canceled, etc., the job may not have run automatically the first time. ### How can I hide photos from the timeline? @@ -68,23 +73,27 @@ See [Backup and Restore](/docs/administration/backup-and-restore.md). No, it currently does not. There is an [open feature request on GitHub](https://github.com/immich-app/immich/discussions/4348). -### Does Immich support filtering of NSFW images? +### Does Immich support the filtering of NSFW images? No, it currently does not. There is an [open feature request on Github](https://github.com/immich-app/immich/discussions/2451). ### Why are there so many thumbnail generation jobs? -There are three thubmanil jobs for each asset: +There are three thumbnail jobs for each asset: - Blurred (thumbhash) -- Small (webp) -- Large (jpeg) +- Preview (Webp) +- Thumbnail (Jpeg) Also, there are additional jobs for person (face) thumbnails. +### Why do files from WhatsApp not appear with the correct date? + +Files sent on WhatsApp are saved without metadata on the file. Therefore, Immich has no way of knowing the original date of the file when files are uploaded from WhatsApp, not the order of arrival on the device. [See #3527](https://github.com/immich-app/immich/issues/3527). + ### What happens if an asset exists in more than one account? -There are no requirements for assets to be unique across users. If multiple users upload the same image they are processed as if they were distinct assets and jobs run and thumbnails are generated accordingly. +There are no requirements for assets to be unique across users. If multiple users upload the same image, it is processed as if it were a distinct asset, and jobs run and thumbnails are generated accordingly. ### Why do HDR videos appear pale in Immich player but look normal after download? @@ -96,40 +105,36 @@ Immich always keeps your original files. Alongside that, it generates a transcod ### How can I delete transcoded videos without deleting the original? -The transcoded version of an asset can be deleted by setting a transcode policy that makes it unnecessary, then running a transcoding job for that asset. This can be done on a per-asset basis by starting a transcoding job for a single asset with the _Refresh encoded videos_ button in the asset viewer options, or for all assets by running transcoding jobs for all assets from the administration page. +The transcoded version of an asset can be deleted by setting a transcode policy that makes it unnecessary and then running a transcoding job for that asset. This can be done on a per-asset basis by starting a transcoding job for a single asset with the _Refresh encoded videos_ button in the asset viewer options or for all assets by running transcoding jobs for all assets from the administration page. To update the transcode policy, navigate to Administration > Video Transcoding Settings > Transcoding Policy and select a policy from the drop-down. This policy will determine whether an existing transcode will be deleted or overwritten in the transcoding job. If a video should be transcoded according to this policy, an existing transcode is overwritten. If not, then it is deleted. :::note -For example, say you have existing transcodes with the policy "Videos higher than normal resolution or not in the desired format" and switch to a narrower policy: "Videos not in the desired format". If an asset was only transcoded due to its resolution, then running a transcoding job for it will now delete the existing transcode. This is because resolution is no longer part of the transcode policy and the transcode is unnecessary as a result. Likewise, if you set the policy to "Don't transcode any videos" and run transcoding jobs for all assets, this will delete all existing transcodes as they are all unnecessary. +For example, say you have existing transcodes with the policy "Videos higher than normal resolution or not in the desired format" and switch to a narrower policy: "Videos not in the desired format." If an asset was only transcoded due to its resolution, running a transcoding job for it will delete the existing transcode. This is because resolution is no longer part of the transcode policy and the transcode is unnecessary. Likewise, if you set the policy to "Don't transcode any videos" and run transcoding jobs for all assets, this will delete all existing transcodes as they are unnecessary. ::: ### Is it possible to compress images during backup? -No. Our golden rule is that the original assets should always be untouched, so we don't think this feature is a good fit for Immich. +No. Our design principle is that the original assets should always be untouched. ### How can I move all data (photos, persons, albums) from one user to another? -This is not officially supported, but can be accomplished with some database updates. You can do this on the command line (in the PostgreSQL container using the `psql` command), or you can add for example an [Adminer](https://www.adminer.org/) container to the `docker-compose.yml` file, so that you can use a web-interface. - -:::warning -This is an advanced operation. If you can't do it with the steps described here, this is not for you. -::: +This is not officially supported but can be accomplished with some database updates. You can do this on the command line (in the PostgreSQL container using the `psql` command), or you can add, for example, an [Adminer](https://www.adminer.org/) container to the `docker-compose.yml` file so that you can use a web interface.
Steps 1. **MAKE A BACKUP** - See [backup and restore](/docs/administration/backup-and-restore.md). -2. Find the id of both the 'source' and the 'destination' user (it's the id column in the users table) +2. Find the ID of both the 'source' and the 'destination' user (it's the id column in the `users` table) 3. Three tables need to be updated: ```sql -// reassign albums +// Reassign albums UPDATE albums SET "ownerId" = '' WHERE "ownerId" = ''; -// reassign people +// Reassign people UPDATE person SET "ownerId" = '' WHERE "ownerId" = ''; // reassign assets @@ -159,7 +164,7 @@ No, not yet. For updates on this planned feature, follow the [GitHub discussion] ### Can I add an external library while keeping the existing album structure? -We haven't put in an official mechanism to create albums from external libraries at the moment, but there are some [workarounds from the community](https://github.com/immich-app/immich/discussions/4279) to help you achieve that. +We haven't implemented an official mechanism for creating albums from external libraries, but there are some [workarounds from the community](https://github.com/immich-app/immich/discussions/4279) to help you achieve that. ### What happens to duplicates in external libraries? @@ -171,7 +176,7 @@ Duplicate checking only exists for upload libraries, using the file hash. Furthe ### How does smart search work? -Immich uses CLIP models, for more information about CLIP and its capabilities read about it [here](https://openai.com/research/clip). +Immich uses CLIP models. For more information about CLIP and its capabilities, read about it [here](https://openai.com/research/clip). ### How does facial recognition work? @@ -189,33 +194,31 @@ However, disabling all jobs will not disable the machine learning service itself ### I'm getting errors about models being corrupt or failing to download. What do I do? -You can delete the model cache volume, which is where models are downloaded to. This will give the service a clean environment to download the model again. If models are failing to download entirely, you can manually download them from [Huggingface](https://huggingface.co/immich-app) and place them in the cache folder. - -### Why did Immich decide to remove object detection? - -The feature added keywords to images for metadata search, but wasn't used for smart search. Smart search made it unnecessary as it isn't limited to exact keywords. Combined with it causing crashes on some devices, using many dependencies and causing user confusion as to how search worked, it was better to remove the job altogether. -For more info see [here](https://github.com/immich-app/immich/pull/5903) +You can delete the model cache volume, where models are downloaded. This will give the service a clean environment to download the model again. If models are failing to download entirely, you can manually download them from [Huggingface][huggingface] and place them in the cache folder. ### Can I use a custom CLIP model? -No, this is not supported. Only models listed in the [Huggingface](https://huggingface.co/immich-app) page are compatible. Feel free to make a feature request if there's a model not listed here that you think should be added. +No, this is not supported. Only models listed in the [Huggingface][huggingface] page are compatible. Feel free to make a feature request if there's a model not listed here that you think should be added. ### I want to be able to search in other languages besides English. How can I do that? You can change to a multilingual model listed [here](https://huggingface.co/collections/immich-app/multilingual-clip-654eb08c2382f591eeb8c2a7) by going to Administration > Machine Learning Settings > Smart Search and replacing the name of the model. Be sure to re-run Smart Search on all assets after this change. You can then search in over 100 languages. :::note -Feel free to make a feature request if there's a model you want to use that isn't in [Immich Huggingface list](https://huggingface.co/immich-app). +Feel free to make a feature request if there's a model you want to use that isn't in [Immich Huggingface list][huggingface]. ::: -### Does Immich support Facial Recognition for videos ? +### Does Immich support Facial Recognition for videos? -Immich's machine learning feature operate on the generated thumbnail. If a face is visible in the video's thumbnail it will be picked up by facial recognition. +Immich's machine learning feature operates on the generated thumbnail. If a face is visible in the video's thumbnail it will be picked up by facial recognition. Scanning the entire video for faces may be implemented in the future. ### Does Immich have animal recognition? No. +:::tip +You can use [Smart Search](/docs/features/smart-search.md) for this to some extent. For example, if you have a Golden Retriever and a Chihuahua, type these words in the smart search and watch the results. +::: ### I'm getting a lot of "faces" that aren't faces, what can I do? @@ -248,8 +251,8 @@ The initial backup is the most intensive due to the number of jobs running. The - Lower the job concurrency for these jobs to 1. - Under Settings > Transcoding Settings > Threads, set the number of threads to a low number like 1 or 2. - Under Settings > Machine Learning Settings > Facial Recognition > Model Name, you can change the facial recognition model to `buffalo_s` instead of `buffalo_l`. The former is a smaller and faster model, albeit not as good. - - You _must_ re-run the Face Detection job for all images after this for facial recognition on new images to work properly. -- If these changes are not enough, see [below](/docs/FAQ#how-can-i-disable-machine-learning) for how you can disable machine learning. +- For facial recognition on new images to work properly, You must re-run the Face Detection job for all images after this. +- If these changes are not enough, see [below](/docs/FAQ#how-can-i-disable-machine-learning) for instructions on how to disable machine learning. ### Can I limit the amount of CPU and RAM usage? @@ -280,13 +283,17 @@ On a normal machine, 2 or 3 concurrent jobs can probably max the CPU. Beyond thi Do not exaggerate with the amount of jobs because you're probably thoroughly overloading the server. -More detail can be found [here](https://discord.com/channels/979116623879368755/994044917355663450/1174711719994605708) +More details can be found [here](https://discord.com/channels/979116623879368755/994044917355663450/1174711719994605708) ::: ### Why is Immich using so much of my CPU? -When a large amount of assets are uploaded to Immich it makes sense that the CPU and RAM will be heavily used due to machine learning work and creating image thumbnails. -Once this process completes, the percentage of CPU usage will drop to around 3-5% usage +When a large number of assets are uploaded to Immich, it makes sense that the CPU and RAM will be heavily used for machine learning work and creating image thumbnails. +Once this process is completed, the percentage of CPU usage will drop to around 3-5% usage + +### My server shows Server Status Offline | Version Unknown what can I do? + +You need to enable Websocket on your reverse proxy. --- @@ -311,7 +318,7 @@ For a further hardened system, you can add the following block to every containe ```yaml security_opt: - # Prevent escalation of privileges after container is started + # Prevent escalation of privileges after the container is started - no-new-privileges:true cap_drop: # Prevent access to raw network traffic @@ -322,7 +329,7 @@ cap_drop: Data for Immich comes in two forms: -1. **Metadata** stored in a postgres database, persisted via the `pg_data` volume +1. **Metadata** stored in a Postgres database, persisted via the `pg_data` volume 2. **Files** (originals, thumbs, profile, etc.), stored in the `UPLOAD_LOCATION` folder, more [info](/docs/administration/backup-and-restore#asset-types-and-storage-locations). To remove the **Metadata** you can stop Immich and delete the volume. @@ -359,3 +366,5 @@ If your version of Immich is below 1.92.0 and the crash occurs after logs about ### Why does Immich log migration errors on startup? Sometimes Immich logs errors such as "duplicate key value violates unique constraint" or "column (...) of relation (...) already exists". Because of Immich's container structure, this error can be seen when both immich and immich-microservices start at the same time and attempt to migrate or create the database structure. Since the database migration is run sequentially and inside of transactions, this error message does not cause harm to your installation of Immich and can safely be ignored. If needed, you can manually restart Immich by running `docker restart immich immich-microservices`. + +[huggingface]: https://huggingface.co/immich-app diff --git a/docs/docs/administration/backup-and-restore.md b/docs/docs/administration/backup-and-restore.md index 897dbf7df8..e949f8d998 100644 --- a/docs/docs/administration/backup-and-restore.md +++ b/docs/docs/administration/backup-and-restore.md @@ -63,7 +63,7 @@ services: ... backup: container_name: immich_db_dumper - image: prodrigestivill/postgres-backup-local + image: prodrigestivill/postgres-backup-local:14 env_file: - .env environment: @@ -86,6 +86,12 @@ Then you can restore with the same command but pointed at the latest dump. gunzip < db_dumps/last/immich-latest.sql.gz | docker exec -i immich_postgres psql --username=postgres ``` +:::note +If you see the error `ERROR: type "earth" does not exist`, or you have problems with Reverse Geocoding after a restore, add the following `sed` fragment to your restore command. + +Example: `gunzip < "/path/to/backup/dump.sql.gz" | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" | docker exec -i immich_postgres psql --username=postgres` +::: + ## Filesystem Immich stores two types of content in the filesystem: (1) original, unmodified content, and (2) generated content. Only the original content needs to be backed-up, which includes the following folders: diff --git a/docs/docs/administration/postgres-standalone.md b/docs/docs/administration/postgres-standalone.md index 42e77f70bb..57cb75a789 100644 --- a/docs/docs/administration/postgres-standalone.md +++ b/docs/docs/administration/postgres-standalone.md @@ -10,9 +10,11 @@ Running with a pre-existing Postgres server can unlock powerful administrative f ## Prerequisites -You must install pgvecto.rs using their [instructions](https://docs.pgvecto.rs/getting-started/installation.html). After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`. +You must install pgvecto.rs into your instance of Postgres using their [instructions][vectors-install]. After installation, add `shared_preload_libraries = 'vectors.so'` to your `postgresql.conf`. If you already have some `shared_preload_libraries` set, you can separate each extension with a comma. For example, `shared_preload_libraries = 'pg_stat_statements, vectors.so'`. :::note +Immich is known to work with Postgres versions 14, 15, and 16. Earlier versions are unsupported. + Make sure the installed version of pgvecto.rs is compatible with your version of Immich. For example, if your Immich version uses the dedicated database image `tensorchord/pgvecto-rs:pg14-v0.2.1`, you must install pgvecto.rs `>= 0.2.1, < 0.3.0`. ::: @@ -30,6 +32,10 @@ DB_URL='postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport # DB_URL='postgresql://immichdbusername:immichdbpassword@postgreshost:postgresport/immichdatabasename?sslmode=require&sslmode=no-verify' ``` +:::info +When `DB_URL` is defined, the other database (`DB_*`) variables are ignored, with the exception of `DB_VECTOR_EXTENSION`. +::: + ## With superuser permission Typically Immich expects superuser permission in the database, which you can grant by running `ALTER USER WITH SUPERUSER;` at the `psql` console. If you prefer not to grant superuser permissions, follow the instructions in the next section. @@ -50,8 +56,7 @@ ALTER DATABASE OWNER TO ; CREATE EXTENSION vectors; CREATE EXTENSION earthdistance CASCADE; ALTER DATABASE SET search_path TO "$user", public, vectors; -GRANT USAGE ON SCHEMA vectors TO ; -ALTER DEFAULT PRIVILEGES IN SCHEMA vectors GRANT SELECT ON TABLES TO ; +ALTER SCHEMA vectors OWNER TO ; COMMIT; ``` @@ -63,4 +68,6 @@ When installing a new version of pgvecto.rs, you will need to manually update th #### Permission denied for view -If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat to ;`. +If you get the error `driverError: error: permission denied for view pg_vector_index_stat`, you can fix this by connecting to the Immich database and running `GRANT SELECT ON TABLE pg_vector_index_stat TO ;`. + +[vectors-install]: https://docs.pgvecto.rs/getting-started/installation.html diff --git a/docs/docs/administration/repair-page.md b/docs/docs/administration/repair-page.md index 3c643ee711..e5022970a2 100644 --- a/docs/docs/administration/repair-page.md +++ b/docs/docs/administration/repair-page.md @@ -20,10 +20,6 @@ In any other situation, there are 3 different options that can appear: - OFFLINE PATHS - These files are the result of manually deleting files in the upload library or a failed file move in the past (losing track of a file). -:::tip -To get rid of Offline paths you can follow this [guide](/docs/guides/remove-offline-files.md) -::: - - UNTRACKED FILES - These files are not tracked by the application. They can be the result of failed moves, interrupted uploads, or left behind due to a bug. In addition, you can download the information from a page, mark everything (in order to check hashing) and correct the problem if a match is found in the hashing. diff --git a/docs/docs/administration/system-settings.md b/docs/docs/administration/system-settings.md index 21eeaaee89..e90f49fed3 100644 --- a/docs/docs/administration/system-settings.md +++ b/docs/docs/administration/system-settings.md @@ -37,9 +37,7 @@ You can set the scanning interval using the preset or cron format. For more info ## Logging -By default logs are set to record at the log level, the network administrator can choose a deeper or lower level of logs according to his decision or according to the needs required by the Immich support team. - -Here you can [learn about the different error levels](https://sematext.com/blog/logging-levels/). +The default Immich log level is `Log` (commonly known as `Info`). The Immich administrator can choose a higher or lower log level according to personal preference or as requested by the Immich support team. ## Machine Learning Settings diff --git a/docs/docs/community-guides.mdx b/docs/docs/community-guides.mdx new file mode 100644 index 0000000000..505ec93e77 --- /dev/null +++ b/docs/docs/community-guides.mdx @@ -0,0 +1,12 @@ +# Community Guides + +This page lists community guides that are written around Immich, but not officially supported by the development team. + +:::warning +This list comes with no guarantees about security, performance, reliability, or accuracy. Use at your own risk. +::: + +import CommunityGuides from '../src/components/community-guides.tsx'; +import React from 'react'; + + diff --git a/docs/docs/community-projects.mdx b/docs/docs/community-projects.mdx new file mode 100644 index 0000000000..eb41090cd6 --- /dev/null +++ b/docs/docs/community-projects.mdx @@ -0,0 +1,12 @@ +# Community Projects + +This page lists community projects that are built around Immich, but not officially supported by the development team. + +:::warning +This list comes with no guarantees about security, performance, reliability, or accuracy. Use at your own risk. +::: + +import CommunityProjects from '../src/components/community-projects.tsx'; +import React from 'react'; + + diff --git a/docs/docs/developer/pr-checklist.md b/docs/docs/developer/pr-checklist.md index 32d9b03507..372dd2b1eb 100644 --- a/docs/docs/developer/pr-checklist.md +++ b/docs/docs/developer/pr-checklist.md @@ -9,7 +9,7 @@ When contributing code through a pull request, please check the following: - [ ] `npm run check:svelte` (Type checking via SvelteKit) - [ ] `npm test` (unit tests) -:::tip +:::tip AIO Run all web checks with `npm run check:all` ::: @@ -20,10 +20,14 @@ Run all web checks with `npm run check:all` - [ ] `npm run check` (Type checking via `tsc`) - [ ] `npm test` (unit tests) -:::tip +:::tip AIO Run all server checks with `npm run check:all` ::: +:::info Auto Fix +You can use `npm run __:fix` to potentially correct some issues automatically for `npm run format` and `lint`. +::: + ## OpenAPI The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/docs/developer/open-api.md) for more details. diff --git a/docs/docs/developer/setup.md b/docs/docs/developer/setup.md index d553ca7d8d..b80f11300c 100644 --- a/docs/docs/developer/setup.md +++ b/docs/docs/developer/setup.md @@ -16,7 +16,7 @@ Thanks for being interested in contributing 😊 ## Environment -### Server and web app +### Services This environment includes the services below. Additional details are available in each service's README. @@ -28,7 +28,7 @@ This environment includes the services below. Additional details are available i All the services are packaged to run as with single Docker Compose command. -### Instructions +### Server and web apps 1. Clone the project repo. 2. Run `cp docker/example.env docker/.env`. @@ -47,13 +47,7 @@ You can access the web from `http://your-machine-ip:2283` or `http://localhost:2 **Note:** the "web" development container runs with uid 1000. If that uid does not have read/write permissions on the mounted volumes, you may encounter errors -### Mobile app - -The mobile app `(/mobile)` will required Flutter toolchain 3.13.x to be installed on your system. - -Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine. - -### Connect to a remote backend +#### Connect web to a remote backend If you only want to do web development connected to an existing, remote backend, follow these steps: @@ -66,13 +60,21 @@ If you only want to do web development connected to an existing, remote backend, IMMICH_SERVER_URL=https://demo.immich.app/ npm run dev ``` +### Mobile app + +The mobile app `(/mobile)` will required Flutter toolchain 3.13.x to be installed on your system. + +Please refer to the [Flutter's official documentation](https://flutter.dev/docs/get-started/install) for more information on setting up the toolchain on your machine. + +The mobile app asks you what backend to connect to. You can utilize the demo backend (https://demo.immich.app/) if you don't need to change server code or upload photos. Alternatively, you can run the server yourself per the instructions above. + ## IDE setup ### Lint / format extensions Setting these in the IDE give a better developer experience, auto-formatting code on save, and providing instant feedback on lint issues. -### Dart Code Metris +### Dart Code Metrics The mobile app uses DCM (Dart Code Metrics) for linting and metrics calculation. Please refer to the [Getting Started](https://dcm.dev/docs/getting-started/#installation) page for more information on setting up DCM diff --git a/docs/docs/developer/testing.md b/docs/docs/developer/testing.md index de080fbb52..fecb58f592 100644 --- a/docs/docs/developer/testing.md +++ b/docs/docs/developer/testing.md @@ -8,15 +8,24 @@ Unit are run by calling `npm run test` from the `server` directory. ### End to end tests -The backend has two end-to-end test suites that can be called with the following two commands from the project root directory: +The e2e tests can be run by first starting up a test production environment via: -- `make server-e2e-api` -- `make server-e2e-jobs` +```bash +make e2e +``` -#### API (e2e) +Once the test environment is running, the e2e tests can be run via: -The API e2e tests spin up a test database and execute http requests against the server, validating the expected response codes and functionality for API endpoints. +```bash +cd e2e/ +npm test +``` -#### Jobs (e2e) +The tests check various things including: -The Jobs e2e tests spin up a docker test environment where thumbnail generation, library scanning, and other _job_ workflows are validated. +- Authentication and authorization +- Query param, body, and url validation +- Response codes +- Thumbnail generation +- Metadata extraction +- Library scanning diff --git a/docs/docs/features/img/advanced-search-filters.webp b/docs/docs/features/img/advanced-search-filters.webp new file mode 100644 index 0000000000..822d84faec Binary files /dev/null and b/docs/docs/features/img/advanced-search-filters.webp differ diff --git a/docs/docs/features/img/library-custom-scan-interval.png b/docs/docs/features/img/library-custom-scan-interval.png new file mode 100644 index 0000000000..3d2e8d3dbd Binary files /dev/null and b/docs/docs/features/img/library-custom-scan-interval.png differ diff --git a/docs/docs/features/img/moblie-smart-serach.webp b/docs/docs/features/img/moblie-smart-serach.webp new file mode 100644 index 0000000000..e125fa5c62 Binary files /dev/null and b/docs/docs/features/img/moblie-smart-serach.webp differ diff --git a/docs/docs/features/img/partner-sharing-1.png b/docs/docs/features/img/partner-sharing-1.png index 49ab3aa402..48785a353c 100644 Binary files a/docs/docs/features/img/partner-sharing-1.png and b/docs/docs/features/img/partner-sharing-1.png differ diff --git a/docs/docs/features/img/partner-sharing-2.png b/docs/docs/features/img/partner-sharing-2.png index 8cfc8e39c1..086795b232 100644 Binary files a/docs/docs/features/img/partner-sharing-2.png and b/docs/docs/features/img/partner-sharing-2.png differ diff --git a/docs/docs/features/img/partner-sharing-3.png b/docs/docs/features/img/partner-sharing-3.png index 7aa2590f68..a8627cf859 100644 Binary files a/docs/docs/features/img/partner-sharing-3.png and b/docs/docs/features/img/partner-sharing-3.png differ diff --git a/docs/docs/features/img/partner-sharing-4.png b/docs/docs/features/img/partner-sharing-4.png new file mode 100644 index 0000000000..eca121ff78 Binary files /dev/null and b/docs/docs/features/img/partner-sharing-4.png differ diff --git a/docs/docs/features/img/partner-sharing-5.png b/docs/docs/features/img/partner-sharing-5.png new file mode 100644 index 0000000000..3759e13d9a Binary files /dev/null and b/docs/docs/features/img/partner-sharing-5.png differ diff --git a/docs/docs/features/img/partner-sharing-6.png b/docs/docs/features/img/partner-sharing-6.png new file mode 100644 index 0000000000..b9aee5143a Binary files /dev/null and b/docs/docs/features/img/partner-sharing-6.png differ diff --git a/docs/docs/features/img/partner-sharing-7.png b/docs/docs/features/img/partner-sharing-7.png new file mode 100644 index 0000000000..4cd4be60b5 Binary files /dev/null and b/docs/docs/features/img/partner-sharing-7.png differ diff --git a/docs/docs/features/img/search-ex-1.png b/docs/docs/features/img/search-ex-1.png new file mode 100644 index 0000000000..fa943d31b1 Binary files /dev/null and b/docs/docs/features/img/search-ex-1.png differ diff --git a/docs/docs/features/img/search-ex-2.webp b/docs/docs/features/img/search-ex-2.webp deleted file mode 100644 index c33285bc67..0000000000 Binary files a/docs/docs/features/img/search-ex-2.webp and /dev/null differ diff --git a/docs/docs/features/img/search-ex-3.webp b/docs/docs/features/img/search-ex-3.webp deleted file mode 100644 index 6c5e0bfb78..0000000000 Binary files a/docs/docs/features/img/search-ex-3.webp and /dev/null differ diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index 28c68ced46..628bb880c1 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -161,7 +161,7 @@ The christmas trip library will now be scanned in the background. In the meantim - Click on Create External Library. -:::info Note +:::note If you get an error here, please rename the other external library to something else. This is a bug that will be fixed in a future release. ::: @@ -175,3 +175,14 @@ If you get an error here, please rename the other external library to something - Click on Scan Library Files Within seconds, the assets from the old-pics and videos folders should show up in the main timeline. + +### Set Custom Scan Interval + +:::note +Only an admin can do this. +::: + +You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library. +You can set the scanning interval using the preset or cron format. For more information you can refer to [Crontab Guru](https://crontab.guru/). + + diff --git a/docs/docs/features/ml-hardware-acceleration.md b/docs/docs/features/ml-hardware-acceleration.md index 8f5a889775..4b6012e71c 100644 --- a/docs/docs/features/ml-hardware-acceleration.md +++ b/docs/docs/features/ml-hardware-acceleration.md @@ -10,7 +10,7 @@ You do not need to redo any machine learning jobs after enabling hardware accele ## Supported Backends - ARM NN (Mali) -- CUDA (NVIDIA) +- CUDA (NVIDIA) Note: It is supported with [compute capability](https://developer.nvidia.com/cuda-gpus) 5.2 or higher - OpenVINO (Intel) ## Limitations @@ -43,7 +43,8 @@ You do not need to redo any machine learning jobs after enabling hardware accele 1. If you do not already have it, download the latest [`hwaccel.ml.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`. 2. In the `docker-compose.yml` under `immich-machine-learning`, uncomment the `extends` section and change `cpu` to the appropriate backend. -3. Redeploy the `immich-machine-learning` container with these updated settings. +3. Still in `immich-machine-learning`, add one of -[armnn, cuda, openvino] to the `image` section's tag at the end of the line. +4. Redeploy the `immich-machine-learning` container with these updated settings. #### Single Compose File @@ -60,8 +61,6 @@ deploy: count: 1 capabilities: - gpu - - compute - - video ``` You can add this to the `immich-machine-learning` service instead of extending from `hwaccel.ml.yml`: @@ -69,6 +68,7 @@ You can add this to the `immich-machine-learning` service instead of extending f ```yaml immich-machine-learning: container_name: immich_machine_learning + # Note the `-cuda` at the end image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda # Note the lack of an `extends` section deploy: @@ -79,8 +79,6 @@ immich-machine-learning: count: 1 capabilities: - gpu - - compute - - video volumes: - model-cache:/cache env_file: diff --git a/docs/docs/features/partner-sharing.md b/docs/docs/features/partner-sharing.md index fa9367fbda..c80ff384f3 100644 --- a/docs/docs/features/partner-sharing.md +++ b/docs/docs/features/partner-sharing.md @@ -1,17 +1,57 @@ # Partner Sharing -Immich allows you to share your library with other users. They can then view your library and download the assets. - -You can manage one or multiple users to have access to your library from the [User Settings](docs/features/user-settings.md) page. - - - - - -Accessing the shared library can be done from the Sharing page. - - - :::tip Sharing specific assets For sharing a specific set of assets, you can use the shared album feature of Immich. ::: + +Immich allows you to share your library with other users. They can then view your library and download the assets. You can manage Partner Sharing from the [User Settings](docs/features/user-settings.md) page on the web. + +Partner Sharing includes: + +- Access to all non-archived and trashed photos and videos. +- Access to all metadata, including GPS information. +- Access to share assets via shared links, albums, etc. + +:::info +Partner sharing is one-way. To view your partner's assets, they must also share them with you. +::: + +## Sharing with a Partner + +:::note Duplicates +Partner sharing may result in displaying duplicate assets on the main timeline. +::: + + + + + + + +## Viewing Partner Assets + +Access partner assets via the Sharing page. + + + +## Timeline Integration + +Partner shared photos can be displayed in the main timeline. This feature can be enabled on a per-partner basis and can be viewed and updated on both the web and mobile app. + +### Web: + +Account Settings -> Sharing -> Show in timeline + + + +### Mobile App: + +From the partner’s view, on the top right corner of the app bar + + + +## Removing Access + +In order to remove a partner, you can go to User -> Account Settings -> Sharing and click on the X button. + + diff --git a/docs/docs/features/reverse-geocoding.md b/docs/docs/features/reverse-geocoding.md index 84045c17b0..108a6ea1b1 100644 --- a/docs/docs/features/reverse-geocoding.md +++ b/docs/docs/features/reverse-geocoding.md @@ -8,7 +8,7 @@ During Exif Extraction, assets with latitudes and longitudes are reverse geocode ## Usage -Data from a reverse geocode is displayed in the image details, and used in [Search](/docs/features/search.md). +Data from a reverse geocode is displayed in the image details, and used in [Smart Search](/docs/features/smart-search.md). diff --git a/docs/docs/features/search.md b/docs/docs/features/search.md deleted file mode 100644 index 0805007e54..0000000000 --- a/docs/docs/features/search.md +++ /dev/null @@ -1,14 +0,0 @@ -# Search - -Immich uses Postgres as its search database for both metadata and smart search. - -Smart search is powered by the [pgvecto.rs](https://github.com/tensorchord/pgvecto.rs) extension, utilizing machine learning models like CLIP to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata. - -Metadata search (prefixed with `m:`) can search specifically by text without the use of a model. - -Archived photos are not included in search results by default. To include them, add the query parameter `withArchived=true` to the url. - -Some search examples: - - - diff --git a/docs/docs/features/smart-search.md b/docs/docs/features/smart-search.md new file mode 100644 index 0000000000..65c71a4e80 --- /dev/null +++ b/docs/docs/features/smart-search.md @@ -0,0 +1,49 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Smart Search + +Immich uses Postgres as its search database for both metadata and smart search. + +Smart search is powered by the [pgvecto.rs](https://github.com/tensorchord/pgvecto.rs) extension, utilizing machine learning models like [CLIP](https://openai.com/research/clip) to provide relevant search results. This allows for freeform searches without requiring specific keywords in the image or video metadata. + +Archived photos are not included in search results by default. To include them, mark the checkbox in [advanced search filters](/docs/features/smart-search#advanced-search-filters). + +:::tip Alternative CLIP Models +More powerful models can be used for more accurate search results. For more information, see the related [FAQ](/docs/FAQ#can-i-use-a-custom-clip-model). +::: + +:::info +Smart Search is currently limited to 5,000 results for a single search on the web. +::: + +## Advanced Search Filters + +In addition, Immich offers advanced search functionality, allowing you to find specific content using customizable search filters. These filters include location, one or more faces, specific albums, and more. You can try out the search filters on the [Demo site](https://demo.immich.app). + +Smart search features include: + +- Search for one or more faces (with or without context search). +- Search by Country or State or City or by all three. +- Search by camera make and model. +- Search by date range. +- Search by file name. +- Search by media types: image, video or all (**Note:** Image includes live images). +- Search by condition: not in any album or archive or Favorite or all conditions. + + + + +Some search examples: + + + + + + + + + + + + diff --git a/docs/docs/features/supported-formats.md b/docs/docs/features/supported-formats.md index a2dc56b66a..ce3736db62 100644 --- a/docs/docs/features/supported-formats.md +++ b/docs/docs/features/supported-formats.md @@ -3,7 +3,7 @@ Immich supports a number of image and video formats, the most common of which are outlined here. :::note -For the full list, you can refer to the [Immich source code](https://github.com/immich-app/immich/blob/main/server/src/utils/mime-types.ts). +For the full list, refer to the [Immich source code](https://github.com/immich-app/immich/blob/main/server/src/utils/mime-types.ts). ::: ## Image formats diff --git a/docs/docs/guides/api-album-sync.md b/docs/docs/guides/api-album-sync.md deleted file mode 100644 index c03915e2cc..0000000000 --- a/docs/docs/guides/api-album-sync.md +++ /dev/null @@ -1,130 +0,0 @@ -# API Album Sync (Python Script) - -This is an example of a python script for syncing an album to a local folder. This was used for a digital photoframe so the displayed photos could be managed from the immich web or app UI. - -The script is copied below in it's current form. A repository is hosted [here](https://git.orenit.solutions/open/immichalbumpull). - -:::danger -This guide uses a generated API key. This key gives the same access to your immich instance as the user it is attached to, so be careful how the config file is stored and transferred. -::: - -### Prerequisites - -- Python 3.7+ -- [requests library](https://pypi.org/project/requests/) - -### Installing - -Copy the contents of 'pull.py' (shown below) to your chosen location or clone the repository: - -```bash -git clone https://git.orenit.solutions/open/immichalbumpull -``` - -Edit or create the 'config.ini' file in the same directory as the script with the necessary details: - -```ini title='config.ini' -[immich] -# URL of target immich instance -url = https://photo.example.com -# API key from Account Settings -> API Keys -apikey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -# Full local path to target directory -destination = /home/photo/photos -# immich album name -album = Photoframe -``` - -### Usage - -Run the script directly: - -```bash -./pull.py -``` - -Or from cron (every 5 minutes): - -```bash -*/5 * * * * /usr/bin/python /home/user/immichalbumpull/pull.py -``` - -### Python Script - -```python title='pull.py' -#!/usr/bin/env python - -import requests -import configparser -import os -import shutil - -# Read config file -config = configparser.ConfigParser() -config.read('config.ini') - -url = config['immich']['url'] -apikey = config['immich']['apikey'] -photodir = config['immich']['destination'] -albumname = config['immich']['album'] - -headers = { - 'Accept': 'application/json', - 'x-api-key': apikey -} - -# Set up the directory for the downloaded images -os.makedirs(photodir, exist_ok=True) - -# Get the list of albums from the API -response = requests.get(url + "/api/album", headers=headers) - -# Parse the JSON response -data = response.json() - -# Find the chosen album id -for item in data: - if item['albumName'] == albumname: - albumid = item['id'] - -# Get the list of photos from the API using the albumid -response = requests.get(url + "/api/album/" + albumid, headers=headers) - -# Parse the JSON response and extract the URLs of the images -data = response.json() -image_urls = data['assets'] - -# Download each image from the URL and save it to the directory -headers = { - 'Accept': 'application/octet-stream', - 'x-api-key': apikey -} - -photolist = [] - -for id in image_urls: - # Query asset info endpoint for correct extension - assetinfourl = url + "/api/asset/" + str(id['id']) - response = requests.get(assetinfourl, headers=headers) - assetinfo = response.json() - ext = os.path.splitext(assetinfo['originalFileName']) - - asseturl = url + "/api/download/asset/" + str(id['id']) - response = requests.post(asseturl, headers=headers, stream=True) - - # Build current photo list for deletions below - photo = os.path.basename(asseturl) + ext[1] - photolist.append(photo) - - photofullpath = photodir + '/' + os.path.basename(asseturl) + ext[1] - # Only download file if it doesn't already exist - if not os.path.exists(photofullpath): - with open(photofullpath, 'wb') as f: - for chunk in response.iter_content(1024): - f.write(chunk) - -# Delete old photos removed from album -for filename in os.listdir(photodir): - if filename not in photolist: - os.unlink(os.path.join(photodir, filename)) -``` diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md new file mode 100644 index 0000000000..5898120f26 --- /dev/null +++ b/docs/docs/guides/custom-locations.md @@ -0,0 +1,50 @@ +# Files Custom Locations + +This guide explains storing generated and raw files with docker's volume mount in different locations. + +:::note Backup +It is important to remember to update the backup settings after following the guide to back up the new backup paths if using automatic backup tools. +::: + +In our `.env` file, we will define variables that will help us in the future when we want to move to a more advanced server in the future + +```diff title=".env" +# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables + +# Custom location where your uploaded, thumbnails, and transcoded video files are stored +- {UPLOAD_LOCATION}=./library ++ {UPLOAD_LOCATION}=/custom/location/on/your/system/ ++ {THUMB_LOCATION}=/custom/location/on/your/system/ ++ {ENCODED_VIDEO_LOCATION}=/custom/location/on/your/system/ +... +``` + +After defining the locations for these files, we will edit the `docker-compose.yml` file accordingly and add the new variables to the `immich-server` and `immich-microservices` containers. + +```diff title="docker-compose.yml" +services: + immich-server: + volumes: + - ${UPLOAD_LOCATION}:/usr/src/app/upload ++ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video + - /etc/localtime:/etc/localtime:ro + +... + + immich-microservices: + volumes: + - ${UPLOAD_LOCATION}:/usr/src/app/upload ++ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video + - /etc/localtime:/etc/localtime:ro +``` + +Restart Immich to register the changes. + +``` +docker compose down +docker compose up -d +``` + +Thanks to [Jrasm91](https://github.com/immich-app/immich/discussions/2110#discussioncomment-5477767) for writing the guide. diff --git a/docs/docs/guides/database-gui.md b/docs/docs/guides/database-gui.md index 1d5eedf990..1b0bd7931c 100644 --- a/docs/docs/guides/database-gui.md +++ b/docs/docs/guides/database-gui.md @@ -45,5 +45,5 @@ Open pgAdmin and click "Add New Server". Click on "Save" to connect to the Immich database. :::tip -View [Database Queries](https://immich.app/docs/guides/database-queries/) for common database queries. +View [Database Queries](/docs/guides/database-queries/) for common database queries. ::: diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index e8252f25d6..a8748f65f9 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -17,7 +17,7 @@ The `"originalFileName"` column is the name of the file at time of upload, inclu ::: ```sql title="Find by original filename" -SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848'; +SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; SELECT * FROM "assets" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle ``` @@ -27,21 +27,30 @@ SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09 SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; ``` -```sql title="Find by checksum" (sha1) +:::note +You can calculate the checksum for a particular file by using the command `sha1sum `. +::: + +```sql title="Find by checksum (SHA-1)" SELECT encode("checksum", 'hex') FROM "assets"; SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); ``` ```sql title="Live photos" -SELECT * FROM "assets" where "livePhotoVideoId" IS NOT NULL; +SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL; ``` ```sql title="Without metadata" -SELECT "assets".* FROM "exif" LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId" WHERE "exif"."assetId" IS NULL; +SELECT "assets".* FROM "exif" + LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId" + WHERE "exif"."assetId" IS NULL; ``` ```sql title="size < 100,000 bytes, smallest to largest" -SELECT * FROM "assets" JOIN "exif" ON "assets"."id" = "exif"."assetId" WHERE "exif"."fileSizeInByte" < 100000 ORDER BY "exif"."fileSizeInByte" ASC; +SELECT * FROM "assets" + JOIN "exif" ON "assets"."id" = "exif"."assetId" + WHERE "exif"."fileSizeInByte" < 100000 + ORDER BY "exif"."fileSizeInByte" ASC; ``` ```sql title="Without thumbnails" @@ -54,20 +63,14 @@ SELECT * FROM "assets" WHERE "assets"."type" = 'IMAGE'; ``` ```sql title="Count by type" -SELECT "assets"."type", count(*) FROM "assets" GROUP BY "assets"."type"; +SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type"; ``` ```sql title="Count by type (per user)" -SELECT - "users"."email", "assets"."type", COUNT(*) -FROM - "assets" -JOIN - "users" ON "assets"."ownerId" = "users"."id" -GROUP BY - "assets"."type", "users"."email" -ORDER BY - "users"."email"; +SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets" + JOIN "users" ON "assets"."ownerId" = "users"."id" + GROUP BY "assets"."type", "users"."email" + ORDER BY "users"."email"; ``` ```sql title="Failed file movements" @@ -76,7 +79,7 @@ SELECT * FROM "move_history"; ## Users -```sql title="List" +```sql title="List all users" SELECT * FROM "users"; ``` @@ -87,3 +90,9 @@ SELECT "key", "value" FROM "system_config"; ``` (Only used when not using the [config file](/docs/install/config-file)) + +## Persons + +```sql title="Delete person and unset it for the faces it was associated with" +DELETE FROM person WHERE name = 'PersonNameHere'; +``` diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index ba60e8b118..b1d4b67b2e 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -14,12 +14,6 @@ Edit `docker-compose.yml` to add two new mount points under `volumes:` - ${EXTERNAL_PATH}:/usr/src/app/external ``` -``` - immich-microservices: - volumes: - - ${EXTERNAL_PATH}:/usr/src/app/external -``` - Be sure to add exactly the same line to both `immich-server:` and `immich-microservices:`. Edit `.env` to define `EXTERNAL_PATH`, substituting in the correct path for your computer: diff --git a/docs/docs/guides/remote-machine-learning.md b/docs/docs/guides/remote-machine-learning.md index 087f9aab76..e79f6b33e9 100644 --- a/docs/docs/guides/remote-machine-learning.md +++ b/docs/docs/guides/remote-machine-learning.md @@ -4,12 +4,16 @@ To alleviate [performance issues on low-memory systems](/docs/FAQ.mdx#why-is-imm - Set the URL in Machine Learning Settings on the Admin Settings page to point to the designated ML system, e.g. `http://workstation:3003`. - Copy the following `docker-compose.yml` to your ML system. -- Start the container by running `docker-compose up -d` or `docker compose up -d` (depending on your Docker version). +- Start the container by running `docker compose up -d`. -:::note Info +:::info Starting with version v1.93.0 face detection work and face recognize were split. From now on face detection is done in the immich_machine_learning service, but facial recognition is done in the immich_microservices service. ::: +:::note +The [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/download/hwaccel.ml.yml) file also needs to be in the same folder if trying to use [hardware acceleration](/docs/features/ml-hardware-acceleration). +::: + ```yaml version: '3.8' diff --git a/docs/docs/guides/remove-offline-files.md b/docs/docs/guides/remove-offline-files.md deleted file mode 100644 index 695103e61d..0000000000 --- a/docs/docs/guides/remove-offline-files.md +++ /dev/null @@ -1,176 +0,0 @@ -# Remove Offline Files [Community] - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -:::note -**Before running the script**, please make sure you have a [backup](/docs/administration/backup-and-restore) of your assets and database. -::: - -:::info -**None** of the scripts can delete orphaned files from the external library. -::: - -This page is a guide to get rid of offline files from the repair page. - - - - - -This way works by retrieving a file that contains a list of all the files that are defined as offline files, running a script that uses the [Immich API](/docs/api/delete-assets) in order to remove the offline files. - -1. Create an API key under Admin User -> Account Settings -> API Keys -> New API Key -> Copy to clipboard. -2. Copy and save the code to file -> `Immich Remove Offline Files.py`. -3. Run the script and follow the instructions. - -:::note -You might need to run `pip install halo tabulate tqdm` if these dependencies are missing on your machine. -::: - -```bash title='Python' -#!/usr/bin/env python3 - -# Note: you might need to run "pip install halo tabulate tqdm" if these dependencies are missing on your machine - -import argparse -import json -import requests - -from datetime import datetime -from halo import Halo -from tabulate import tabulate -from tqdm import tqdm -from urllib.parse import urlparse - -def parse_arguments(): - parser = argparse.ArgumentParser(description='Fetch file report and delete orphaned media assets from Immich.') - parser.add_argument('--apikey', help='Immich API key for authentication') - parser.add_argument('--immichaddress', help='Full address for Immich, including protocol and port') - parser.add_argument('--no_prompt', action='store_true', help='Delete orphaned media assets without confirmation') - args = parser.parse_args() - return args - -def filter_entities(response_json, entity_type): - return [ - {'pathValue': entity['pathValue'], 'entityId': entity['entityId'], 'entityType': entity['entityType']} - for entity in response_json.get('orphans', []) if entity.get('entityType') == entity_type - ] - -def main(): - args = parse_arguments() - try: - if args.apikey: - api_key = args.apikey - else: - api_key = input('Enter the Immich API key: ') - - if args.immichaddress: - immich_server = args.immichaddress - else: - immich_server = input('Enter the full web address for Immich, including protocol and port: ') - immich_parsed_url = urlparse(immich_server) - base_url = f'{immich_parsed_url.scheme}://{immich_parsed_url.netloc}' - api_url = f'{base_url}/api' - file_report_url = api_url + '/audit/file-report' - headers = {'x-api-key': api_key} - - print() - spinner = Halo(text='Retrieving list of orphaned media assets...', spinner='dots') - spinner.start() - - try: - response = requests.get(file_report_url, headers=headers) - response.raise_for_status() - spinner.succeed('Success!') - except requests.exceptions.RequestException as e: - spinner.fail(f'Failed to fetch assets: {str(e)}') - - person_assets = filter_entities(response.json(), 'person') - orphan_media_assets = filter_entities(response.json(), 'asset') - - num_entries = len(orphan_media_assets) - - if num_entries == 0: - print('No orphaned media assets found; exiting.') - return - - else: - if not args.no_prompt: - table_data = [] - for asset in orphan_media_assets: - table_data.append([asset['pathValue'], asset['entityId']]) - print(tabulate(table_data, headers=['Path Value', 'Entity ID'], tablefmt='pretty')) - print() - - if person_assets: - print('Found orphaned person assets! Please run the "RECOGNIZE FACES > ALL" job in Immich after running this tool to correct this.') - print() - - if num_entries > 0: - summary = f'There {"is" if num_entries == 1 else "are"} {num_entries} orphaned media asset{"s" if num_entries != 1 else ""}. Would you like to delete {"them" if num_entries != 1 else "it"} from Immich? (yes/no): ' - user_input = input(summary).lower() - print() - - if user_input not in ('y', 'yes'): - print('Exiting without making any changes.') - return - - with tqdm(total=num_entries, desc="Deleting orphaned media assets", unit="asset") as progress_bar: - for asset in orphan_media_assets: - entity_id = asset['entityId'] - asset_url = f'{api_url}/asset' - delete_payload = json.dumps({'force': True, 'ids': [entity_id]}) - headers = {'Content-Type': 'application/json', 'x-api-key': api_key} - response = requests.delete(asset_url, headers=headers, data=delete_payload) - response.raise_for_status() - progress_bar.set_postfix_str(entity_id) - progress_bar.update(1) - print() - print('Orphaned media assets deleted successfully!') - except Exception as e: - print() - print(f"An error occurred: {str(e)}") - -if __name__ == '__main__': - main() -``` - -Thanks to [DooMRunneR](https://discord.com/channels/979116623879368755/1179655214870040596/1194308198413373482) and [Sircharlo](https://discord.com/channels/979116623879368755/1179655214870040596/1195038609812758639) for writing this script. - - - - - -This way works by downloading a JSON file that contains a list of all the files that are defined as offline files, running a script that uses the [Immich API](/docs/api/delete-assets) in order to remove the offline files. - -1. Create an API key under Admin User -> Account Settings -> API Keys -> New API Key -> Copy to clipboard. -2. Download the JSON file under Administration -> repair -> Export. -3. Replace `YOUR_IP_HERE` and `YOUR_API_KEY_HERE` with your actual IP address and API key in the script. -4. Run the script in the same folder where the JSON file is located. - -## Script for Linux based systems: - -```bash title='Bash' -awk -F\" '/entityId/ {print $4}' orphans.json | while read line; do curl --location --request DELETE 'http://YOUR_IP_HERE:2283/api/asset' --header 'Content- Type: application/json' --header 'x-api-key: YOUR_API_KEY_HERE' --data '{ "force": true, "ids": ["'"$line"'"]}';done -``` - -## Script for the Windows system (run through PowerShell): - -```powershell title='PowerShell' -Get-Content orphans.json | Select-String -Pattern 'entityId' | ForEach-Object { - $line = $_ -split '"' | Select-Object -Index 3 - $body = [pscustomobject]@{ - 'ids' = @($line) - 'force' = (' true ' | ConvertFrom-Json) - } | ConvertTo-Json -Depth 3 - Invoke-RestMethod -Uri 'http://YOUR_IP_HERE:2283/api/asset' -Method Delete -Headers @{ - 'Content-Type' = 'application/json' - 'x-api-key' = 'YOUR_API_KEY_HERE' - } -Body $body -} -``` - -Thanks to [DooMRunneR](https://discord.com/channels/979116623879368755/1179655214870040596/1194308198413373482) for writing this script. - - - diff --git a/docs/docs/install/docker-compose.mdx b/docs/docs/install/docker-compose.mdx index 60fcbbe8b8..9045891fd8 100644 --- a/docs/docs/install/docker-compose.mdx +++ b/docs/docs/install/docker-compose.mdx @@ -7,7 +7,8 @@ import ExampleEnv from '!!raw-loader!../../../docker/example.env'; # Docker Compose [Recommended] -Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose. +Docker Compose is the recommended method to run Immich in production. Below are the steps to deploy Immich with Docker Compose. +Immich requires Docker Compose version 2.x. ### Step 1 - Download the required files @@ -66,7 +67,7 @@ docker compose up -d ``` :::info Docker version -If you get an error `unknown shorthand flag: 'd' in -d`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by `apt remove`ing Ubuntu's docker.io package and installing docker and docker-compose via [Docker's official repository](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository). +If you get an error `unknown shorthand flag: 'd' in -d`, you are probably running the wrong Docker version. (This happens, for example, with the docker.io package in Ubuntu 22.04.3 LTS.) You can correct the problem by `apt remove`ing Ubuntu's docker.io package and installing docker and docker-compose via [Docker's official repository][docker-repo]. Note that the correct command really is `docker compose`, not `docker-compose`. If you try the latter on vanilla Ubuntu 22.04 it will fail in a different way: @@ -83,27 +84,31 @@ For more information on how to use the application, please refer to the [Post In ::: :::note GitHub Authentication -Downloading container images might require you to authenticate to the GitHub Container Registry ([steps here](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry)). +Downloading container images might require you to authenticate to the GitHub Container Registry ([steps here][container-auth]). ::: ### Step 4 - Upgrading :::danger Breaking Changes -It is important to follow breaking updates to avoid problems. You can see versions that had breaking changes [here](https://github.com/immich-app/immich/discussions?discussions_q=label%3Abreaking-change+sort%3Adate_created). +It is important to follow breaking updates to avoid problems. You can see versions that had breaking changes [here][breaking]. ::: If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version. -When a new version of Immich is [released](https://github.com/immich-app/immich/releases), the application can be upgraded with the following commands, run in the directory with the `docker-compose.yml` file: +When a new version of Immich is [released][releases], the application can be upgraded with the following commands, run in the directory with the `docker-compose.yml` file: ```bash title="Upgrade Immich" docker compose pull && docker compose up -d ``` :::caution Automatic Updates -Immich is currently under heavy development, which means you can expect [breaking changes](https://github.com/immich-app/immich/discussions?discussions_q=label%3Abreaking-change+sort%3Adate_created) and bugs. Therefore, we recommend reading the release notes prior to updating and to take special care when using automated tools like [Watchtower][watchtower]. +Immich is currently under heavy development, which means you can expect [breaking changes][breaking] and bugs. Therefore, we recommend reading the release notes prior to updating and to take special care when using automated tools like [Watchtower][watchtower]. ::: [compose-file]: https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml [env-file]: https://github.com/immich-app/immich/releases/latest/download/example.env [watchtower]: https://containrrr.dev/watchtower/ +[breaking]: https://github.com/immich-app/immich/discussions?discussions_q=label%3Abreaking-change+sort%3Adate_created +[container-auth]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-to-the-container-registry +[releases]: https://github.com/immich-app/immich/releases +[docker-repo]: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index a8cbe6059f..dce9d4fa59 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -41,19 +41,20 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `IMMICH_REVERSE_GEOCODING_ROOT` | Path of reverse geocoding dump directory | `/usr/src/resources` | microservices | :::tip -`TZ` should be set to a `TZ identifier` from [this list](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List). For example, `TZ="Etc/UTC"`. +`TZ` should be set to a `TZ identifier` from [this list][tz-list]. For example, `TZ="Etc/UTC"`. `TZ` is only used by `exiftool`, which is present in the microservices container, as a fallback in case the timezone cannot be determined from the image metadata. ::: ## Ports -| Variable | Description | Default | Services | -| :---------------------- | :-------------------- | :-------: | :--------------- | -| `SERVER_PORT` | Server Port | `3001` | server | -| `MICROSERVICES_PORT` | Microservices Port | `3002` | microservices | -| `MACHINE_LEARNING_HOST` | Machine Learning Host | `0.0.0.0` | machine learning | -| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning | +| Variable | Description | Default | Services | +| :---------------------- | :-------------------- | :-------: | :-------------------- | +| `HOST` | Host | `0.0.0.0` | server, microservices | +| `SERVER_PORT` | Server Port | `3001` | server | +| `MICROSERVICES_PORT` | Microservices Port | `3002` | microservices | +| `MACHINE_LEARNING_HOST` | Machine Learning Host | `0.0.0.0` | machine learning | +| `MACHINE_LEARNING_PORT` | Machine Learning Port | `3003` | machine learning | ## Database @@ -71,7 +72,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N :::info -When `DB_URL` is defined, the other database (`DB_*`) variables are ignored. +When `DB_URL` is defined, the other database (`DB_*`) variables are ignored, with the exception of `DB_VECTOR_EXTENSION`. ::: @@ -90,7 +91,7 @@ When `DB_URL` is defined, the other database (`DB_*`) variables are ignored. :::info `REDIS_URL` must start with `ioredis://` and then include a `base64` encoded JSON string for the configuration. -More info can be found in the upstream [ioredis](https://ioredis.readthedocs.io/en/latest/API/) documentation. +More info can be found in the upstream [ioredis][redis-api] documentation. - When `REDIS_URL` is defined, the other redis (`REDIS_*`) variables are ignored. - When `REDIS_SOCKET` is defined, the other redis (`REDIS_*`) variables are ignored. @@ -158,7 +159,7 @@ Other machine learning parameters can be tuned from the admin UI. ## Docker Secrets -The following variables support the use of [Docker secrets](https://docs.docker.com/engine/swarm/secrets/) for additional security. +The following variables support the use of [Docker secrets][docker-secrets] for additional security. To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of the `_FILE` variable should be set to the path of a file containing the variable value. @@ -172,8 +173,14 @@ the `_FILE` variable should be set to the path of a file containing the variable | `DB_URL` | `DB_URL_FILE`\*1 | | `REDIS_PASSWORD` | `REDIS_PASSWORD_FILE`\*2 | -\*1: See the [official documentation](https://github.com/docker-library/docs/tree/master/postgres#docker-secrets) for +\*1: See the [official documentation][docker-secrets-docs] for details on how to use Docker Secrets in the Postgres image. -\*2: See [this comment](https://github.com/docker-library/redis/issues/46#issuecomment-335326234) for an example of how +\*2: See [this comment][docker-secrets-example] for an example of how to use use a Docker secret for the password in the Redis container. + +[tz-list]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List +[docker-secrets-example]: https://github.com/docker-library/redis/issues/46#issuecomment-335326234 +[docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets +[docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ +[redis-api]: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository diff --git a/docs/docs/install/kubernetes.md b/docs/docs/install/kubernetes.md index 57db569128..d66f6193d1 100644 --- a/docs/docs/install/kubernetes.md +++ b/docs/docs/install/kubernetes.md @@ -6,7 +6,7 @@ sidebar_position: 40 You can deploy Immich on Kubernetes using [the official Helm chart](https://github.com/immich-app/immich-charts/tree/main/charts/immich). -If you want examples of how other people run Immich on Kubernetes, using the official chart or otherwise, you can find them at https://nanne.dev/k8s-at-home-search/#/immich. +You can view some [examples](https://kubesearch.dev/#/immich) of how other people run Immich on Kubernetes, using the official chart or otherwise. :::caution DNS in Alpine containers Immich makes use of Alpine container images. These can encounter [a DNS resolution bug](https://stackoverflow.com/a/65593511) on Kubernetes clusters if the host diff --git a/docs/docs/install/requirements.md b/docs/docs/install/requirements.md index f56e5c10bc..b611d04f2c 100644 --- a/docs/docs/install/requirements.md +++ b/docs/docs/install/requirements.md @@ -11,6 +11,10 @@ Hardware and software requirements for Immich - [Docker](https://docs.docker.com/get-docker/) - [Docker Compose](https://docs.docker.com/compose/install/) +:::note +Immich requires the command `docker compose` - the similarly named `docker-compose` is [deprecated](https://docs.docker.com/compose/migrate/) and is no longer compatible with Immich. +::: + :::info Podman You can also use Podman to run the application. However, additional configuration might be required. ::: diff --git a/docs/docs/install/script.md b/docs/docs/install/script.md index 0b097e4c79..3c3060e4d6 100644 --- a/docs/docs/install/script.md +++ b/docs/docs/install/script.md @@ -17,12 +17,11 @@ curl -o- https://raw.githubusercontent.com/immich-app/immich/main/install.sh | b The script will perform the following actions: 1. Download [docker-compose.yml](https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml), and the [.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file from the main branch of the [repository](https://github.com/immich-app/immich). -2. Populate the `.env` file with necessary information based on the current directory path. -3. Start the containers. +2. Start the containers. The web application will be available at `http://:2283`, and the server URL for the mobile app will be `http://:2283/api` -The directory which is used to store the library files is `./immich-data` relative to the current directory. +The directory which is used to store the library files is `./immich-app` relative to the current directory. :::tip For common next steps, see [Post Install Steps](/docs/install/post-install.mdx). diff --git a/docs/docs/install/unraid.md b/docs/docs/install/unraid.md index 977ebd5460..5d39b6d487 100644 --- a/docs/docs/install/unraid.md +++ b/docs/docs/install/unraid.md @@ -98,7 +98,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich" > Note: This can take several minutes depending on your Internet speed and Unraid hardware -9. Once on the Docker page you will see several Immich containers, one of them will be labelled `immich_web` and will have a port mapping. Visit the `IP:PORT` displayed in your web browser and you should see the Immich admin setup page. +9. Once on the Docker page you will see several Immich containers, one of them will be labelled `immich_server` and will have a port mapping. Visit the `IP:PORT` displayed in your web browser and you should see the Immich admin setup page. + diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index afe9f0eecd..75513e3d3b 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -144,6 +144,10 @@ const config = { label: 'Discord', href: 'https://discord.com/invite/D8JsnBEuKb', }, + { + label: 'Reddit', + href: 'https://www.reddit.com/r/immich/', + }, ], }, { @@ -157,6 +161,10 @@ const config = { label: 'GitHub', href: 'https://github.com/immich-app/immich', }, + { + label: 'YouTube', + href: 'https://www.youtube.com/@immich-app', + }, ], }, ], diff --git a/docs/package-lock.json b/docs/package-lock.json index d86f69ac9f..6e898886cd 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -2185,9 +2185,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.1.1.tgz", - "integrity": "sha512-2nQfKFcf+MLEM7JXsXwQxPOmQAR6ytKMZVSx7tVi9HEm9WtfwBH1fp6bn8Gj4zLUhjWKCLoysQ9/Wm+EZCQ4yQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.2.1.tgz", + "integrity": "sha512-ZeMAqNvy0eBv2dThEeMuNzzuu+4thqMQakhxsgT5s02A8LqRcdkg+rbcnuNqUIpekQ4GRx3+M5nj0ODJhBXo9w==", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2199,14 +2199,13 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", + "@docusaurus/cssnano-preset": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", - "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@svgr/webpack": "^6.5.1", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", @@ -2227,6 +2226,7 @@ "detect-port": "^1.5.1", "escape-html": "^1.0.3", "eta": "^2.2.0", + "eval": "^0.1.8", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "html-minifier-terser": "^7.2.0", @@ -2235,6 +2235,7 @@ "leven": "^3.1.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.7.6", + "p-map": "^4.0.0", "postcss": "^8.4.26", "postcss-loader": "^7.3.3", "prompts": "^2.4.2", @@ -2271,9 +2272,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.1.1.tgz", - "integrity": "sha512-LnoIDjJWbirdbVZDMq+4hwmrTl2yHDnBf9MLG9qyExeAE3ac35s4yUhJI8yyTCdixzNfKit4cbXblzzqMu4+8g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.2.1.tgz", + "integrity": "sha512-wTL9KuSSbMJjKrfu385HZEzAoamUsbKqwscAQByZw4k6Ja/RWpqgVvt/CbAC+aYEH6inLzOt+MjuRwMOrD3VBA==", "dependencies": { "cssnano-preset-advanced": "^5.3.10", "postcss": "^8.4.26", @@ -2285,9 +2286,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.1.1.tgz", - "integrity": "sha512-BjkNDpQzewcTnST8trx4idSoAla6zZ3w22NqM/UMcFtvYJgmoE4layuTzlfql3VFPNuivvj7BOExa/+21y4X2Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.2.1.tgz", + "integrity": "sha512-0voOKJCn9RaM3np6soqEfo7SsVvf2C+CDTWhW+H/1AyBhybASpExtDEz+7ECck9TwPzFQ5tt+I3zVugUJbJWDg==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2297,15 +2298,13 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.1.1.tgz", - "integrity": "sha512-xN2IccH9+sv7TmxwsDJNS97BHdmlqWwho+kIVY4tcCXkp+k4QuzvWBeunIMzeayY4Fu13A6sAjHGv5qm72KyGA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.2.1.tgz", + "integrity": "sha512-Fs8tXhXKZjNkdGaOy1xSLXSwfjCMT73J3Zfrju2U16uGedRFRjgK0ojpK5tiC7TnunsL3tOFgp1BSMBRflX9gw==", "dependencies": { - "@babel/parser": "^7.22.7", - "@babel/traverse": "^7.22.8", - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2337,12 +2336,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.1.1.tgz", - "integrity": "sha512-xBJyx0TMfAfVZ9ZeIOb1awdXgR4YJMocIEzTps91rq+hJDFJgJaylDtmoRhUxkwuYmNK1GJpW95b7DLztSBJ3A==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.2.1.tgz", + "integrity": "sha512-FyViV5TqhL1vsM7eh29nJ5NtbRE6Ra6LP1PDcPvhwPSlA7eiWGRKAn3jWwMUcmjkos5SYY+sr0/feCdbM3eQHQ==", "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.1.1", + "@docusaurus/types": "3.2.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2356,17 +2355,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.1.1.tgz", - "integrity": "sha512-ew/3VtVoG3emoAKmoZl7oKe1zdFOsI0NbcHS26kIxt2Z8vcXKCUgK9jJJrz0TbOipyETPhqwq4nbitrY3baibg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.2.1.tgz", + "integrity": "sha512-lOx0JfhlGZoZu6pEJfeEpSISZR5dQbJGGvb42IP13G5YThNHhG9R9uoWuo4IOimPqBC7sHThdLA3VLevk61Fsw==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", @@ -2387,17 +2386,18 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.1.1.tgz", - "integrity": "sha512-lhFq4E874zw0UOH7ujzxnCayOyAt0f9YPVYSb9ohxrdCM8B4szxitUw9rIX4V9JLLHVoqIJb6k+lJJ1jrcGJ0A==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.2.1.tgz", + "integrity": "sha512-GHe5b/lCskAR8QVbfWAfPAApvRZgqk7FN3sOHgjCtjzQACZxkHmq6QqyqZ8Jp45V7lVck4wt2Xw2IzBJ7Cz3bA==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2416,15 +2416,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.1.1.tgz", - "integrity": "sha512-NQHncNRAJbyLtgTim9GlEnNYsFhuCxaCNkMwikuxLTiGIPH7r/jpb7O3f3jUMYMebZZZrDq5S7om9a6rvB/YCA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.2.1.tgz", + "integrity": "sha512-TOqVfMVTAHqWNEGM94Drz+PUpHDbwFy6ucHFgyTx9zJY7wPNSG5EN+rd/mU7OvAi26qpOn2o9xTdUmb28QLjEQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2438,13 +2438,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.1.1.tgz", - "integrity": "sha512-xWeMkueM9wE/8LVvl4+Qf1WqwXmreMjI5Kgr7GYCDoJ8zu4kD+KaMhrh7py7MNM38IFvU1RfrGKacCEe2DRRfQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.2.1.tgz", + "integrity": "sha512-AMKq8NuUKf2sRpN1m/sIbqbRbnmk+rSA+8mNU1LNxEl9BW9F/Gng8m9HKlzeyMPrf5XidzS1jqkuTLDJ6KIrFw==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2458,13 +2458,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.1.1.tgz", - "integrity": "sha512-+q2UpWTqVi8GdlLoSlD5bS/YpxW+QMoBwrPrUH/NpvpuOi0Of7MTotsQf9JWd3hymZxl2uu1o3PIrbpxfeDFDQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.2.1.tgz", + "integrity": "sha512-/rJ+9u+Px0eTCiF4TNcNtj3kHf8cp6K1HCwOTdbsSlz6Xn21syZYcy+f1VM9wF6HrvUkXUcbM5TDCvg2IRL6bQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "tslib": "^2.6.0" }, "engines": { @@ -2476,13 +2476,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.1.1.tgz", - "integrity": "sha512-0mMPiBBlQ5LFHTtjxuvt/6yzh8v7OxLi3CbeEsxXZpUzcKO/GC7UA1VOWUoBeQzQL508J12HTAlR3IBU9OofSw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.2.1.tgz", + "integrity": "sha512-XtuJnlMvYfppeVdUyKiDIJAa/gTJKCQU92z8CLZZ9ibJdgVjFOLS10s0hIC0eL5z0U2u2loJz2rZ63HOkNHbBA==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2495,13 +2495,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.1.1.tgz", - "integrity": "sha512-d07bsrMLdDIryDtY17DgqYUbjkswZQr8cLWl4tzXrt5OR/T/zxC1SYKajzB3fd87zTu5W5klV5GmUwcNSMXQXA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.2.1.tgz", + "integrity": "sha512-wiS/kE0Ny5pnjTxVCs8ljRnkL1RVMj59t6jmSsgEX7piDOoaXSMIUaoIt9ogS/v132uO0xEsxHstkRUZHQyPcQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "tslib": "^2.6.0" }, "engines": { @@ -2513,16 +2513,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.1.1.tgz", - "integrity": "sha512-iJ4hCaMmDaUqRv131XJdt/C/jJQx8UreDWTRqZKtNydvZVh/o4yXGRRFOplea1D9b/zpwL1Y+ZDwX7xMhIOTmg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.2.1.tgz", + "integrity": "sha512-uWZ7AxzdeaQSTCwD2yZtOiEm9zyKU+wqCmi/Sf25kQQqqFSBZUStXfaQ8OHP9cecnw893ZpZ811rPhB/wfujJw==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2536,23 +2536,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.1.1.tgz", - "integrity": "sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.2.1.tgz", + "integrity": "sha512-E3OHSmttpEBcSMhfPBq3EJMBxZBM01W1rnaCUTXy9EHvkmB5AwgTfW1PwGAybPAX579ntE03R+2zmXdizWfKnQ==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/plugin-debug": "3.1.1", - "@docusaurus/plugin-google-analytics": "3.1.1", - "@docusaurus/plugin-google-gtag": "3.1.1", - "@docusaurus/plugin-google-tag-manager": "3.1.1", - "@docusaurus/plugin-sitemap": "3.1.1", - "@docusaurus/theme-classic": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-search-algolia": "3.1.1", - "@docusaurus/types": "3.1.1" + "@docusaurus/core": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/plugin-debug": "3.2.1", + "@docusaurus/plugin-google-analytics": "3.2.1", + "@docusaurus/plugin-google-gtag": "3.2.1", + "@docusaurus/plugin-google-tag-manager": "3.2.1", + "@docusaurus/plugin-sitemap": "3.2.1", + "@docusaurus/theme-classic": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-search-algolia": "3.2.1", + "@docusaurus/types": "3.2.1" }, "engines": { "node": ">=18.0" @@ -2575,22 +2575,22 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.1.1.tgz", - "integrity": "sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.2.1.tgz", + "integrity": "sha512-+vSbnQyoWjc6vRZi4vJO2dBU02wqzynsai15KK+FANZudrYaBHtkbLZAQhgmxzBGVpxzi87gRohlMm+5D8f4tA==", "dependencies": { - "@docusaurus/core": "3.1.1", - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/types": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-translations": "3.2.1", + "@docusaurus/types": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -2614,17 +2614,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.1.1.tgz", - "integrity": "sha512-38urZfeMhN70YaXkwIGXmcUcv2CEYK/2l4b05GkJPrbEbgpsIZM3Xc+Js2ehBGGZmfZq8GjjQ5RNQYG+MYzCYg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.2.1.tgz", + "integrity": "sha512-d+adiD7L9xv6EvfaAwUqdKf4orsM3jqgeqAM+HAjgL/Ux0GkVVnfKr+tsoe+4ow4rHe6NUt+nkkW8/K8dKdilA==", "dependencies": { - "@docusaurus/mdx-loader": "3.1.1", - "@docusaurus/module-type-aliases": "3.1.1", - "@docusaurus/plugin-content-blog": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/plugin-content-pages": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-common": "3.1.1", + "@docusaurus/mdx-loader": "3.2.1", + "@docusaurus/module-type-aliases": "3.2.1", + "@docusaurus/plugin-content-blog": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/plugin-content-pages": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2643,18 +2643,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.1.1.tgz", - "integrity": "sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.2.1.tgz", + "integrity": "sha512-bzhCrpyXBXzeydNUH83II2akvFEGfhsNTPPWsk5N7e+odgQCQwoHhcF+2qILbQXjaoZ6B3c48hrvkyCpeyqGHw==", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.1.1", - "@docusaurus/logger": "3.1.1", - "@docusaurus/plugin-content-docs": "3.1.1", - "@docusaurus/theme-common": "3.1.1", - "@docusaurus/theme-translations": "3.1.1", - "@docusaurus/utils": "3.1.1", - "@docusaurus/utils-validation": "3.1.1", + "@docusaurus/core": "3.2.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/plugin-content-docs": "3.2.1", + "@docusaurus/theme-common": "3.2.1", + "@docusaurus/theme-translations": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-validation": "3.2.1", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2673,9 +2673,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.1.1.tgz", - "integrity": "sha512-xvWQFwjxHphpJq5fgk37FXCDdAa2o+r7FX8IpMg+bGZBNXyWBu3MjZ+G4+eUVNpDhVinTc+j6ucL0Ain5KCGrg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.2.1.tgz", + "integrity": "sha512-jAUMkIkFfY+OAhJhv6mV8zlwY6J4AQxJPTgLdR2l+Otof9+QdJjHNh/ifVEu9q0lp3oSPlJj9l05AaP7Ref+cg==", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2685,9 +2685,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.1.1.tgz", - "integrity": "sha512-grBqOLnubUecgKFXN9q3uit2HFbCxTWX4Fam3ZFbMN0sWX9wOcDoA7lwdX/8AmeL20Oc4kQvWVgNrsT8bKRvzg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.2.1.tgz", + "integrity": "sha512-n/toxBzL2oxTtRTOFiGKsHypzn/Pm+sXyw+VSk1UbqbXQiHOwHwts55bpKwbcUgA530Is6kix3ELiFOv9GAMfw==", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2705,11 +2705,12 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-ZJfJa5cJQtRYtqijsPEnAZoduW6sjAQ7ZCWSZavLcV10Fw0Z3gSaPKA/B4micvj2afRZ4gZxT7KfYqe5H8Cetg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.2.1.tgz", + "integrity": "sha512-DPkIS/EPc+pGAV798PUXgNzJFM3HJouoQXgr0KDZuJVz1EkWbDLOcQwLIz8Qx7liI9ddfkN/TXTRQdsTPZNakw==", "dependencies": { - "@docusaurus/logger": "3.1.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils-common": "3.2.1", "@svgr/webpack": "^6.5.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2721,6 +2722,7 @@ "js-yaml": "^4.1.0", "lodash": "^4.17.21", "micromatch": "^4.0.5", + "prompts": "^2.4.2", "resolve-pathname": "^3.0.0", "shelljs": "^0.8.5", "tslib": "^2.6.0", @@ -2740,9 +2742,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.1.1.tgz", - "integrity": "sha512-eGne3olsIoNfPug5ixjepZAIxeYFzHHnor55Wb2P57jNbtVaFvij/T+MS8U0dtZRFi50QU+UPmRrXdVUM8uyMg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.2.1.tgz", + "integrity": "sha512-N5vadULnRLiqX2QfTjVEU3u5vo6RG2EZTdyXvJdzDOdrLCGIZAfnf/VkssinFZ922sVfaFfQ4FnStdhn5TWdVg==", "dependencies": { "tslib": "^2.6.0" }, @@ -2759,12 +2761,13 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.1.1.tgz", - "integrity": "sha512-KlY4P9YVDnwL+nExvlIpu79abfEv6ZCHuOX4ZQ+gtip+Wxj0daccdReIWWtqxM/Fb5Cz1nQvUCc7VEtT8IBUAA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.2.1.tgz", + "integrity": "sha512-+x7IR9hNMXi62L1YAglwd0s95fR7+EtirjTxSN4kahYRWGqOi3jlQl1EV0az/yTEvKbxVvOPcdYicGu9dk4LJw==", "dependencies": { - "@docusaurus/logger": "3.1.1", - "@docusaurus/utils": "3.1.1", + "@docusaurus/logger": "3.2.1", + "@docusaurus/utils": "3.2.1", + "@docusaurus/utils-common": "3.2.1", "joi": "^17.9.2", "js-yaml": "^4.1.0", "tslib": "^2.6.0" @@ -3153,19 +3156,6 @@ "micromark-util-symbol": "^1.0.1" } }, - "node_modules/@slorber/static-site-generator-webpack-plugin": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", - "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", - "dependencies": { - "eval": "^0.1.8", - "p-map": "^4.0.0", - "webpack-sources": "^3.2.2" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", @@ -4300,13 +4290,11 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.14.8" } }, "node_modules/babel-loader": { @@ -6137,21 +6125,22 @@ } }, "node_modules/docusaurus-plugin-openapi": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.7.3.tgz", - "integrity": "sha512-JOMlP4HarpSYQhBHgVCQecy4BXvt3bVLob1PXfBMEBYBy26Y8fwN+QktBdLJ4YLoQ8FoDO13+q5/335b790tiA==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.7.4.tgz", + "integrity": "sha512-JZSAIPQFQ4FyUiHZcQ00/fIRIr1f74rxqVvzjuMmODQvUXvlOgPyxErmGJwYVf4sAxPl05IiYDPtmVeZZixPMg==", "dependencies": { - "@docusaurus/mdx-loader": "^3.0.0", - "@docusaurus/plugin-content-docs": "^3.0.0", - "@docusaurus/utils": "^3.0.0", - "@docusaurus/utils-validation": "^3.0.0", - "axios": "^1.6.7", + "@docusaurus/mdx-loader": "^3.2.0", + "@docusaurus/plugin-content-docs": "^3.2.0", + "@docusaurus/utils": "^3.2.0", + "@docusaurus/utils-common": "^3.2.0", + "@docusaurus/utils-validation": "^3.2.0", + "axios": "^0.26.1", "chalk": "^4.1.2", "clsx": "^1.2.1", "js-yaml": "^4.1.0", "json-refs": "^3.0.15", "json-schema-resolve-allof": "^1.5.0", - "lodash": "^4.17.21", + "lodash": "^4.17.20", "openapi-to-postmanv2": "^1.2.1", "postman-collection": "^4.1.0", "webpack": "^5.88.1" @@ -6173,22 +6162,22 @@ } }, "node_modules/docusaurus-plugin-proxy": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/docusaurus-plugin-proxy/-/docusaurus-plugin-proxy-0.7.3.tgz", - "integrity": "sha512-7DbDtPo6ZQK2kGROwSxtfMrdDiTJ2Bn+OQ591IBVduuH3dwVgzyKgzjxBknfDQjF5peZWDazVHwCV8ZZoI+6ew==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/docusaurus-plugin-proxy/-/docusaurus-plugin-proxy-0.7.4.tgz", + "integrity": "sha512-utnMCZF1ewMjVHTmXqvx0O8jAy1hL6GiJWzo8U8sl5asGI78Qi8GZjVkdj9l3FGDA9LRtCK96rme3Aa0x7dFxg==", "engines": { "node": ">=14" } }, "node_modules/docusaurus-preset-openapi": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/docusaurus-preset-openapi/-/docusaurus-preset-openapi-0.7.3.tgz", - "integrity": "sha512-gXDhnSjl6QeJnUU6txPfNoITKyKKNQoxeiRKLh7npuI5KB3kWWL+jRneYTEph+LhtGf4LRlKNm06ZBS6MODRRw==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/docusaurus-preset-openapi/-/docusaurus-preset-openapi-0.7.4.tgz", + "integrity": "sha512-O2Pje1aa7cTwhfBHTJgD3Gx+MgzhsSOCj97Ez8CWZDOJ2Mta68wFbQQV3UMWfujsvcy3ttNCgAe/LahtFo7jyg==", "dependencies": { - "@docusaurus/preset-classic": "^3.0.0", - "docusaurus-plugin-openapi": "^0.7.3", - "docusaurus-plugin-proxy": "^0.7.3", - "docusaurus-theme-openapi": "^0.7.3" + "@docusaurus/preset-classic": "^3.2.0", + "docusaurus-plugin-openapi": "^0.7.4", + "docusaurus-plugin-proxy": "^0.7.4", + "docusaurus-theme-openapi": "^0.7.4" }, "engines": { "node": ">=18" @@ -6199,11 +6188,11 @@ } }, "node_modules/docusaurus-theme-openapi": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/docusaurus-theme-openapi/-/docusaurus-theme-openapi-0.7.3.tgz", - "integrity": "sha512-KNcCgEcoNZT2uFGM0/SWIt9YOuBQYKnI10py7z8IJ4Ev02wo7c1DStVstlONIohyzFMTjqSbZ8hifZ/29AHJBQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/docusaurus-theme-openapi/-/docusaurus-theme-openapi-0.7.4.tgz", + "integrity": "sha512-E5nZJM/Z/YSWGtkvz8Mw3ueUt+uJcD2LMRK0He23u5ufUZPwGeRxL93DxtOL7mkEvdV6rFzH7LtzysOdqyGy3A==", "dependencies": { - "@docusaurus/theme-common": "^3.0.0", + "@docusaurus/theme-common": "^3.2.0", "@mdx-js/react": "^3.0.0", "@monaco-editor/react": "^4.3.1", "@reduxjs/toolkit": "^1.7.1", @@ -6212,7 +6201,7 @@ "crypto-js": "^4.1.1", "docusaurus-plugin-openapi": "^0.7.3", "immer": "^9.0.7", - "lodash": "^4.17.21", + "lodash": "^4.17.20", "marked": "^11.0.0", "monaco-editor": "^0.31.1", "postman-code-generators": "^1.0.0", @@ -13611,11 +13600,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -16141,9 +16125,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/docs/src/components/community-guides.tsx b/docs/src/components/community-guides.tsx new file mode 100644 index 0000000000..5a17e3b295 --- /dev/null +++ b/docs/src/components/community-guides.tsx @@ -0,0 +1,71 @@ +import Link from '@docusaurus/Link'; +import React from 'react'; + +interface CommunityGuidesProps { + title: string; + description: string; + url: string; +} + +const guides: CommunityGuidesProps[] = [ + { + title: 'Cloudflare Tunnels with SSO/OAuth', + description: `Setting up Cloudflare Tunnels and a SaaS App for immich.`, + url: 'https://github.com/immich-app/immich/discussions/8299', + }, + { + title: 'Database backup in Truenas', + description: `Create a database backup with pgAdmin in Truenas.`, + url: 'https://github.com/immich-app/immich/discussions/8809', + }, + { + title: 'Unraid backup scripts', + description: `Back up your assets in Unarid with a pre-prepared script.`, + url: 'https://github.com/immich-app/immich/discussions/8416', + }, + { + title: 'Sync folders with albums', + description: `synchronize folders in imported library with albums having the folders name.`, + url: 'https://github.com/immich-app/immich/discussions/3382', + }, + { + title: 'Podman/Quadlets Install', + description: 'Documentation for simple podman setup using quadlets.', + url: 'https://github.com/tbelway/immich-podman-quadlets/blob/main/docs/install/podman-quadlet.md', + }, +]; + +function CommunityGuide({ title, description, url }: CommunityGuidesProps): JSX.Element { + return ( +
+
+

+ {title} +

+ +

{description}

+

+ {url} +

+
+
+ + View Guide + +
+
+ ); +} + +export default function CommunityGuides(): JSX.Element { + return ( +
+ {guides.map((guides) => ( + + ))} +
+ ); +} diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx new file mode 100644 index 0000000000..6d23b0dcfc --- /dev/null +++ b/docs/src/components/community-projects.tsx @@ -0,0 +1,96 @@ +import Link from '@docusaurus/Link'; +import React from 'react'; + +interface CommunityProjectProps { + title: string; + description: string; + url: string; +} + +const projects: CommunityProjectProps[] = [ + { + title: 'immich-go', + description: `An alternative to the immich-CLI command that doesn't depend on nodejs installation. It tries its best for importing google photos takeout archives.`, + url: 'https://github.com/simulot/immich-go', + }, + { + title: 'ImmichFrame', + description: 'Run an Immich slideshow in a photo frame.', + url: 'https://github.com/3rob3/ImmichFrame', + }, + { + title: 'API Album Sync', + description: 'A Python script to sync folders as albums.', + url: 'https://git.orenit.solutions/open/immichalbumpull', + }, + { + title: 'Remove offline files', + description: 'A Python script to remove offline files.', + url: 'https://gist.github.com/Thoroslives/ca5d8e1efd15111febc1e7b34ac72668', + }, + { + title: 'Create albums from folders', + description: 'A Python script to create albums based on the folder structure of an external library.', + url: 'https://github.com/Salvoxia/immich-folder-album-creator', + }, + { + title: 'Lightroom Publisher: mi.Immich.Publisher', + description: 'Lightroom plugin to publish photos from Lightroom collections to Immich albums.', + url: 'https://github.com/midzelis/mi.Immich.Publisher', + }, + { + title: 'Immich Duplicate Finder', + description: 'Webapp that uses machine learning to identify near-duplicate images.', + url: 'https://github.com/vale46n1/immich_duplicate_finder', + }, + { + title: 'Immich-Tiktok-Remover', + description: 'Script to search for and remove TikTok videos from your Immich library.', + url: 'https://github.com/mxc2/immich-tiktok-remover', + }, + { + title: 'Immich Android TV', + description: 'Unofficial Immich Android TV app.', + url: 'https://github.com/giejay/Immich-Android-TV', + }, + { + title: 'Powershell Module PSImmich', + description: 'Powershell Module for the Immich API', + url: 'https://github.com/hanpq/PSImmich', + }, +]; + +function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { + return ( +
+
+

+ {title} +

+ +

{description}

+

+ {url} +

+
+
+ + View Project + +
+
+ ); +} + +export default function CommunityProjects(): JSX.Element { + return ( +
+ {projects.map((project) => ( + + ))} +
+ ); +} diff --git a/docs/src/pages/milestones.tsx b/docs/src/pages/milestones.tsx index ea00f486cf..76e9b804d2 100644 --- a/docs/src/pages/milestones.tsx +++ b/docs/src/pages/milestones.tsx @@ -51,12 +51,22 @@ import { mdiVideo, mdiWeb, mdiScaleBalance, + mdiMagnifyScan, } from '@mdi/js'; import Layout from '@theme/Layout'; import React from 'react'; import Timeline, { DateType, Item } from '../components/timeline'; const items: Item[] = [ + { + icon: mdiMagnifyScan, + description: 'Advanced search with filters by date, location and more', + title: 'Search enhancement with advanced filters', + release: 'v1.95.0', + tag: 'v1.95.0', + date: new Date(2024, 1, 20), + dateType: DateType.RELEASE, + }, { icon: mdiScaleBalance, description: 'Immich switches to AGPLv3 license', diff --git a/docs/static/_redirects b/docs/static/_redirects index 5f2d5c5a12..8b02fe11b9 100644 --- a/docs/static/_redirects +++ b/docs/static/_redirects @@ -25,3 +25,6 @@ /docs/developer/contributing /docs/developer/pr-checklist 301 /docs/guides/machine-learning /docs/guides/remote-machine-learning 301 /docs/administration/password-login /docs/administration/system-settings 301 +/docs/features/search /docs/features/smart-search 301 +/docs/guides/api-album-sync /docs/community-projects 301 +/docs/guides/remove-offline-files /docs/community-projects 301 diff --git a/e2e/.eslintrc.cjs b/e2e/.eslintrc.cjs index 3989e86e54..3594073202 100644 --- a/e2e/.eslintrc.cjs +++ b/e2e/.eslintrc.cjs @@ -19,6 +19,7 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-floating-promises': 'error', 'unicorn/prefer-module': 'off', + 'unicorn/import-style': 'off', curly: 2, 'prettier/prettier': 0, 'unicorn/prevent-abbreviations': 'off', diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 333c181d91..6cd8dd90ec 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -23,7 +23,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.1", + "eslint-plugin-unicorn": "^52.0.0", "exiftool-vendored": "^24.5.0", "luxon": "^3.4.4", "pg": "^8.11.3", @@ -33,6 +33,7 @@ "socket.io-client": "^4.7.4", "supertest": "^6.3.4", "typescript": "^5.3.3", + "utimes": "^5.2.1", "vitest": "^1.3.0" } }, @@ -63,7 +64,7 @@ "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", + "eslint-plugin-unicorn": "^52.0.0", "glob": "^10.3.1", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -87,8 +88,8 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^20.11.0", - "typescript": "^5.3.3" + "@types/node": "^20.12.7", + "typescript": "^5.4.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -872,6 +873,50 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -926,12 +971,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.0.tgz", + "integrity": "sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==", "dev": true, "dependencies": { - "playwright": "1.42.1" + "playwright": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -941,9 +986,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", "cpu": [ "arm" ], @@ -954,9 +999,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", "cpu": [ "arm64" ], @@ -967,9 +1012,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", "cpu": [ "arm64" ], @@ -980,9 +1025,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", "cpu": [ "x64" ], @@ -993,9 +1038,22 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", "cpu": [ "arm" ], @@ -1006,9 +1064,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", "cpu": [ "arm64" ], @@ -1019,9 +1077,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", "cpu": [ "arm64" ], @@ -1031,10 +1089,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", "cpu": [ "riscv64" ], @@ -1044,10 +1115,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", "cpu": [ "x64" ], @@ -1058,9 +1142,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", "cpu": [ "x64" ], @@ -1071,9 +1155,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", "cpu": [ "arm64" ], @@ -1084,9 +1168,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", "cpu": [ "ia32" ], @@ -1097,9 +1181,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", "cpu": [ "x64" ], @@ -1133,12 +1217,6 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1158,9 +1236,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1173,9 +1251,9 @@ "dev": true }, "node_modules/@types/pg": { - "version": "8.11.4", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.4.tgz", - "integrity": "sha512-yw3Bwbda6vO+NvI1Ue/YKOwtl31AYvvd/e73O3V4ZkNzuGpTDndLSyc0dQRB2xrQqDePd20pEGIfqSp/GH3pRw==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==", "dev": true, "dependencies": { "@types/node": "*", @@ -1277,22 +1355,22 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1312,15 +1390,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -1340,13 +1418,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1357,15 +1435,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1384,9 +1462,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1397,19 +1475,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1434,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -1449,18 +1527,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1474,13 +1552,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1497,9 +1575,9 @@ "dev": true }, "node_modules/@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -1514,24 +1592,23 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.4.0" + "vitest": "1.5.0" } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -1539,12 +1616,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1553,9 +1630,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1567,9 +1644,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1579,9 +1656,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -1593,6 +1670,12 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1623,6 +1706,18 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1660,6 +1755,25 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1765,15 +1879,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -1904,6 +2009,15 @@ "node": "*" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", @@ -1958,6 +2072,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1985,10 +2108,10 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, "node_modules/cookiejar": { @@ -2085,6 +2208,21 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -2134,6 +2272,12 @@ "integrity": "sha512-Ic85cOuXSP6h7KM0AIJ2hpJ98Bo4hyTUjc4yjMbkvD+8yTxEhfK9+8exT2KKYsSjnCn2tGsKVSZwE7ZgTORQCw==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/engine.io-client": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", @@ -2343,9 +2487,9 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -2687,6 +2831,30 @@ "url": "https://ko-fi.com/tunnckoCore/commissions" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2716,6 +2884,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -2886,6 +3080,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, "node_modules/hasown": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", @@ -2928,6 +3128,19 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -3038,6 +3251,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3432,6 +3654,52 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mlly": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", @@ -3474,12 +3742,53 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -3528,6 +3837,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -3650,12 +3980,6 @@ "node": ">=6" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==", - "dev": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3744,16 +4068,14 @@ } }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "dev": true, "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -3780,9 +4102,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", "dev": true }, "node_modules/pg-int8": { @@ -3804,18 +4126,18 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "dev": true, "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", "dev": true }, "node_modules/pg-types": { @@ -3873,12 +4195,12 @@ } }, "node_modules/playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", + "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", "dev": true, "dependencies": { - "playwright-core": "1.42.1" + "playwright-core": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -3891,9 +4213,9 @@ } }, "node_modules/playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", + "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -3921,9 +4243,9 @@ } }, "node_modules/postcss": { - "version": "8.4.37", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz", - "integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "funding": [ { @@ -4215,6 +4537,20 @@ "node": ">=8" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -4297,9 +4633,9 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -4312,19 +4648,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", "fsevents": "~2.3.2" } }, @@ -4351,6 +4690,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -4366,6 +4725,12 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "node_modules/set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -4539,6 +4904,29 @@ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", "dev": true }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4673,6 +5061,23 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -4700,9 +5105,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "engines": { "node": ">=14.0.0" @@ -4738,10 +5143,16 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { "node": ">=16" @@ -4790,9 +5201,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4853,18 +5264,24 @@ "punycode": "^2.1.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utimes": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/utimes/-/utimes-5.2.1.tgz", + "integrity": "sha512-6S5mCapmzcxetOD/2UEjL0GF5e4+gB07Dh8qs63xylw5ay4XuyW6iQs70FOJo/puf10LCkvhp4jYMQSDUBYEFg==", "dev": true, + "hasInstallScript": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^4.3.0" }, "engines": { - "node": ">=10.12.0" + "node": ">=10.0.0" } }, "node_modules/validate-npm-package-license": { @@ -4878,13 +5295,13 @@ } }, "node_modules/vite": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.2.tgz", - "integrity": "sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", + "integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==", "dev": true, "dependencies": { "esbuild": "^0.20.1", - "postcss": "^8.4.36", + "postcss": "^8.4.38", "rollup": "^4.13.0" }, "bin": { @@ -4933,9 +5350,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -4969,16 +5386,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -4990,9 +5407,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -5007,8 +5424,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, @@ -5033,6 +5450,22 @@ } } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5064,6 +5497,15 @@ "node": ">=8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/e2e/package.json b/e2e/package.json index 45f72217cb..9023de8162 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -33,7 +33,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.1", + "eslint-plugin-unicorn": "^52.0.0", "exiftool-vendored": "^24.5.0", "luxon": "^3.4.4", "pg": "^8.11.3", @@ -43,6 +43,7 @@ "socket.io-client": "^4.7.4", "supertest": "^6.3.4", "typescript": "^5.3.3", + "utimes": "^5.2.1", "vitest": "^1.3.0" } } diff --git a/e2e/src/api/specs/activity.e2e-spec.ts b/e2e/src/api/specs/activity.e2e-spec.ts index 5d3cf72209..d3d7db6972 100644 --- a/e2e/src/api/specs/activity.e2e-spec.ts +++ b/e2e/src/api/specs/activity.e2e-spec.ts @@ -148,7 +148,7 @@ describe('/activity', () => { }); it('should filter by userId', async () => { - const [reaction] = await Promise.all([createActivity({ albumId: album.id, type: ReactionType.Like })]); + const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like }); const response1 = await request(app) .get('/activity') @@ -250,8 +250,7 @@ describe('/activity', () => { }); it('should return a 200 for a duplicate like on the album', async () => { - const [reaction] = await Promise.all([createActivity({ albumId: album.id, type: ReactionType.Like })]); - + const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like }); const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) @@ -261,13 +260,11 @@ describe('/activity', () => { }); it('should not confuse an album like with an asset like', async () => { - const [reaction] = await Promise.all([ - createActivity({ - albumId: album.id, - assetId: asset.id, - type: ReactionType.Like, - }), - ]); + const reaction = await createActivity({ + albumId: album.id, + assetId: asset.id, + type: ReactionType.Like, + }); const { status, body } = await request(app) .post('/activity') .set('Authorization', `Bearer ${admin.accessToken}`) @@ -314,13 +311,11 @@ describe('/activity', () => { }); it('should return a 200 for a duplicate like on an asset', async () => { - const [reaction] = await Promise.all([ - createActivity({ - albumId: album.id, - assetId: asset.id, - type: ReactionType.Like, - }), - ]); + const reaction = await createActivity({ + albumId: album.id, + assetId: asset.id, + type: ReactionType.Like, + }); const { status, body } = await request(app) .post('/activity') diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index de320ee95f..c877afc6bb 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -4,7 +4,9 @@ import { AssetOrder, LoginResponseDto, SharedLinkType, + addAssetsToAlbum, deleteUser, + getAlbumInfo, } from '@immich/sdk'; import { createUserDto, uuidDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; @@ -65,7 +67,6 @@ describe('/album', () => { utils.createAlbum(user2.accessToken, { albumName: user2SharedUser, sharedWithUserIds: [user1.userId], - assetIds: [user1Asset1.id], }), utils.createAlbum(user2.accessToken, { albumName: user2SharedLink }), utils.createAlbum(user2.accessToken, { albumName: user2NotShared }), @@ -77,6 +78,13 @@ describe('/album', () => { }), ]); + await addAssetsToAlbum( + { id: albums[3].id, bulkIdsDto: { ids: [user1Asset1.id] } }, + { headers: asBearerAuth(user1.accessToken) }, + ); + + albums[3] = await getAlbumInfo({ id: albums[3].id }, { headers: asBearerAuth(user2.accessToken) }); + user1Albums = albums.slice(0, 3); user2Albums = albums.slice(3, 6); diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index ddc8dd3ef6..53c4c43468 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -111,7 +111,7 @@ describe('/asset', () => { utils.createAsset(user1.accessToken), ]); - user2Assets = await Promise.all([utils.createAsset(user2.accessToken)]); + user2Assets = [await utils.createAsset(user2.accessToken)]; await Promise.all([ utils.createAsset(timeBucketUser.accessToken, { fileCreatedAt: new Date('1970-01-01').toISOString() }), @@ -816,15 +816,15 @@ describe('/asset', () => { }); it('should not include gps data for webp thumbnails', async () => { - const { status, body, type } = await request(app) - .get(`/asset/thumbnail/${locationAsset.id}?format=WEBP`) - .set('Authorization', `Bearer ${admin.accessToken}`); - await utils.waitForWebsocketEvent({ event: 'assetUpload', id: locationAsset.id, }); + const { status, body, type } = await request(app) + .get(`/asset/thumbnail/${locationAsset.id}?format=WEBP`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); expect(body).toBeDefined(); expect(type).toBe('image/webp'); diff --git a/e2e/src/api/specs/library.e2e-spec.ts b/e2e/src/api/specs/library.e2e-spec.ts index 3e4f971cfd..18becec770 100644 --- a/e2e/src/api/specs/library.e2e-spec.ts +++ b/e2e/src/api/specs/library.e2e-spec.ts @@ -6,12 +6,13 @@ import { getAllLibraries, scanLibrary, } from '@immich/sdk'; -import { existsSync, rmdirSync } from 'node:fs'; +import { cpSync, existsSync } from 'node:fs'; import { Socket } from 'socket.io-client'; import { userDto, uuidDto } from 'src/fixtures'; import { errorDto } from 'src/responses'; import { app, asBearerAuth, testAssetDir, testAssetDirInternal, utils } from 'src/utils'; import request from 'supertest'; +import { utimes } from 'utimes'; import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'; const scan = async (accessToken: string, id: string, dto: ScanLibraryDto = {}) => @@ -26,23 +27,21 @@ describe('/library', () => { beforeAll(async () => { await utils.resetDatabase(); admin = await utils.adminSetup(); + await utils.resetAdminConfig(admin.accessToken); user = await utils.userSetup(admin.accessToken, userDto.user1); library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId, type: LibraryType.External }); websocket = await utils.connectWebsocket(admin.accessToken); + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetA.png`); + utils.createImageFile(`${testAssetDir}/temp/directoryB/assetB.png`); }); afterAll(() => { utils.disconnectWebsocket(websocket); + utils.resetTempFolder(); }); beforeEach(() => { utils.resetEvents(); - const tempDir = `${testAssetDir}/temp`; - if (existsSync(tempDir)) { - rmdirSync(tempDir, { recursive: true }); - } - utils.createImageFile(`${testAssetDir}/temp/directoryA/assetA.png`); - utils.createImageFile(`${testAssetDir}/temp/directoryB/assetB.png`); }); describe('GET /library', () => { @@ -357,95 +356,6 @@ describe('/library', () => { }); }); - describe('DELETE /library/:id', () => { - it('should require authentication', async () => { - const { status, body } = await request(app).delete(`/library/${uuidDto.notFound}`); - - expect(status).toBe(401); - expect(body).toEqual(errorDto.unauthorized); - }); - - it('should not delete the last upload library', async () => { - const libraries = await getAllLibraries( - { $type: LibraryType.Upload }, - { headers: asBearerAuth(admin.accessToken) }, - ); - - const adminLibraries = libraries.filter((library) => library.ownerId === admin.userId); - expect(adminLibraries.length).toBeGreaterThanOrEqual(1); - const lastLibrary = adminLibraries.pop() as LibraryResponseDto; - - // delete all but the last upload library - for (const library of adminLibraries) { - const { status } = await request(app) - .delete(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - expect(status).toBe(204); - } - - const { status, body } = await request(app) - .delete(`/library/${lastLibrary.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(body).toEqual(errorDto.noDeleteUploadLibrary); - expect(status).toBe(400); - }); - - it('should delete an external library', async () => { - const library = await utils.createLibrary(admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.External, - }); - - const { status, body } = await request(app) - .delete(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(204); - expect(body).toEqual({}); - - const libraries = await getAllLibraries({}, { headers: asBearerAuth(admin.accessToken) }); - expect(libraries).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: library.id, - }), - ]), - ); - }); - - it('should delete an external library with assets', async () => { - const library = await utils.createLibrary(admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.External, - importPaths: [`${testAssetDirInternal}/temp`], - }); - - await scan(admin.accessToken, library.id); - await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 2 }); - - const { status, body } = await request(app) - .delete(`/library/${library.id}`) - .set('Authorization', `Bearer ${admin.accessToken}`); - - expect(status).toBe(204); - expect(body).toEqual({}); - - const libraries = await getAllLibraries({}, { headers: asBearerAuth(admin.accessToken) }); - expect(libraries).not.toEqual( - expect.arrayContaining([ - expect.objectContaining({ - id: library.id, - }), - ]), - ); - - // ensure no files get deleted - expect(existsSync(`${testAssetDir}/temp/directoryA/assetA.png`)).toBe(true); - expect(existsSync(`${testAssetDir}/temp/directoryB/assetB.png`)).toBe(true); - }); - }); - describe('GET /library/:id/statistics', () => { it('should require authentication', async () => { const { status, body } = await request(app).get(`/library/${uuidDto.notFound}/statistics`); @@ -549,6 +459,150 @@ describe('/library', () => { const { assets: newAssets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); expect(newAssets.count).toBe(3); + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + }); + + it('should offline missing files', async () => { + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + isOffline: true, + originalFileName: 'assetB.png', + }), + ]), + ); + }); + + it('should scan new files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetC.png`); + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + originalFileName: 'assetC.png', + }), + ]), + ); + }); + + describe('with refreshModifiedFiles=true', () => { + it('should reimport modified files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_000); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_001); + + await scan(admin.accessToken, library.id, { refreshModifiedFiles: true }); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction'); + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + + const { assets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + model: 'NIKON D750', + }); + expect(assets.count).toBe(1); + }); + + it('should not reimport unmodified files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_000); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_000); + + await scan(admin.accessToken, library.id, { refreshModifiedFiles: true }); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction'); + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + + const { assets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + model: 'NIKON D750', + }); + expect(assets.count).toBe(0); + }); + }); + + describe('with refreshAllFiles=true', () => { + it('should reimport all files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_000); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + cpSync(`${testAssetDir}/albums/nature/tanners_ridge.jpg`, `${testAssetDir}/temp/directoryA/assetB.jpg`); + await utimes(`${testAssetDir}/temp/directoryA/assetB.jpg`, 447_775_200_000); + + await scan(admin.accessToken, library.id, { refreshAllFiles: true }); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + await utils.waitForQueueFinish(admin.accessToken, 'metadataExtraction'); + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.jpg`); + + const { assets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + model: 'NIKON D750', + }); + expect(assets.count).toBe(1); + }); }); }); @@ -559,6 +613,72 @@ describe('/library', () => { expect(status).toBe(401); expect(body).toEqual(errorDto.unauthorized); }); + + it('should remove offline files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + utils.createImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets: initialAssets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + }); + expect(initialAssets.count).toBe(3); + + utils.removeImageFile(`${testAssetDir}/temp/directoryA/assetB.png`); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets: offlineAssets } = await utils.metadataSearch(admin.accessToken, { + libraryId: library.id, + isOffline: true, + }); + expect(offlineAssets.count).toBe(1); + + const { status } = await request(app) + .post(`/library/${library.id}/removeOffline`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send(); + expect(status).toBe(204); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + await utils.waitForQueueFinish(admin.accessToken, 'backgroundTask'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets.count).toBe(2); + }); + + it('should not remove online files', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets: assetsBefore } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + expect(assetsBefore.count).toBeGreaterThan(1); + + const { status } = await request(app) + .post(`/library/${library.id}/removeOffline`) + .set('Authorization', `Bearer ${admin.accessToken}`) + .send(); + expect(status).toBe(204); + await utils.waitForQueueFinish(admin.accessToken, 'library'); + + const { assets } = await utils.metadataSearch(admin.accessToken, { libraryId: library.id }); + + expect(assets).toEqual(assetsBefore); + }); }); describe('POST /library/:id/validate', () => { @@ -608,4 +728,93 @@ describe('/library', () => { }); }); }); + + describe('DELETE /library/:id', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).delete(`/library/${uuidDto.notFound}`); + + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should not delete the last upload library', async () => { + const libraries = await getAllLibraries( + { $type: LibraryType.Upload }, + { headers: asBearerAuth(admin.accessToken) }, + ); + + const adminLibraries = libraries.filter((library) => library.ownerId === admin.userId); + expect(adminLibraries.length).toBeGreaterThanOrEqual(1); + const lastLibrary = adminLibraries.pop() as LibraryResponseDto; + + // delete all but the last upload library + for (const library of adminLibraries) { + const { status } = await request(app) + .delete(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + } + + const { status, body } = await request(app) + .delete(`/library/${lastLibrary.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(body).toEqual(errorDto.noDeleteUploadLibrary); + expect(status).toBe(400); + }); + + it('should delete an external library', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + }); + + const { status, body } = await request(app) + .delete(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(204); + expect(body).toEqual({}); + + const libraries = await getAllLibraries({}, { headers: asBearerAuth(admin.accessToken) }); + expect(libraries).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: library.id, + }), + ]), + ); + }); + + it('should delete an external library with assets', async () => { + const library = await utils.createLibrary(admin.accessToken, { + ownerId: admin.userId, + type: LibraryType.External, + importPaths: [`${testAssetDirInternal}/temp`], + }); + + await scan(admin.accessToken, library.id); + await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 2 }); + + const { status, body } = await request(app) + .delete(`/library/${library.id}`) + .set('Authorization', `Bearer ${admin.accessToken}`); + + expect(status).toBe(204); + expect(body).toEqual({}); + + const libraries = await getAllLibraries({}, { headers: asBearerAuth(admin.accessToken) }); + expect(libraries).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: library.id, + }), + ]), + ); + + // ensure no files get deleted + expect(existsSync(`${testAssetDir}/temp/directoryA/assetA.png`)).toBe(true); + expect(existsSync(`${testAssetDir}/temp/directoryB/assetB.png`)).toBe(true); + }); + }); }); diff --git a/e2e/src/api/specs/search.e2e-spec.ts b/e2e/src/api/specs/search.e2e-spec.ts index a613cdd288..d3274ed729 100644 --- a/e2e/src/api/specs/search.e2e-spec.ts +++ b/e2e/src/api/specs/search.e2e-spec.ts @@ -1,4 +1,4 @@ -import { AssetFileUploadResponseDto, LoginResponseDto, deleteAssets, updateAsset } from '@immich/sdk'; +import { AssetFileUploadResponseDto, LoginResponseDto, deleteAssets, getMapMarkers, updateAsset } from '@immich/sdk'; import { DateTime } from 'luxon'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; @@ -32,6 +32,9 @@ describe('/search', () => { let assetGlarus: AssetFileUploadResponseDto; let assetSprings: AssetFileUploadResponseDto; let assetLast: AssetFileUploadResponseDto; + let cities: string[]; + let states: string[]; + let countries: string[]; beforeAll(async () => { await utils.resetDatabase(); @@ -79,7 +82,7 @@ describe('/search', () => { } // note: the coordinates here are not the actual coordinates of the images and are random for most of them - const cities = [ + const coordinates = [ { latitude: 48.853_41, longitude: 2.3488 }, // paris { latitude: 63.0695, longitude: -151.0074 }, // denali { latitude: 52.524_37, longitude: 13.410_53 }, // berlin @@ -101,7 +104,7 @@ describe('/search', () => { ]; const updates = assets.map((asset, i) => - updateAsset({ id: asset.id, updateAssetDto: cities[i] }, { headers: asBearerAuth(admin.accessToken) }), + updateAsset({ id: asset.id, updateAssetDto: coordinates[i] }, { headers: asBearerAuth(admin.accessToken) }), ); await Promise.all(updates); @@ -133,6 +136,12 @@ describe('/search', () => { assetLast = assets.at(-1) as AssetFileUploadResponseDto; await deleteAssets({ assetBulkDeleteDto: { ids: [assetSilver.id] } }, { headers: asBearerAuth(admin.accessToken) }); + + const mapMarkers = await getMapMarkers({}, { headers: asBearerAuth(admin.accessToken) }); + const nonTrashed = mapMarkers.filter((mark) => mark.id !== assetSilver.id); + cities = [...new Set(nonTrashed.map((mark) => mark.city).filter((entry): entry is string => !!entry))].sort(); + states = [...new Set(nonTrashed.map((mark) => mark.state).filter((entry): entry is string => !!entry))].sort(); + countries = [...new Set(nonTrashed.map((mark) => mark.country).filter((entry): entry is string => !!entry))].sort(); }, 30_000); afterAll(async () => { @@ -452,21 +461,7 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=country') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual([ - 'Cuba', - 'France', - 'Georgia', - 'Germany', - 'Ghana', - 'Japan', - 'Morocco', - "People's Republic of China", - 'Russian Federation', - 'Singapore', - 'Spain', - 'Switzerland', - 'United States of America', - ]); + expect(body).toEqual(countries); expect(status).toBe(200); }); @@ -474,23 +469,7 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=state') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual([ - 'Accra, Greater Accra', - 'Berlin', - 'Glarus, Glarus', - 'Havana', - 'Marrakech, Marrakesh-Safi', - 'Mesa County, Colorado', - 'Neshoba County, Mississippi', - 'New York', - 'Page County, Virginia', - 'Paris, Île-de-France', - 'Province of Córdoba, Andalusia', - 'Shanghai Municipality, Shanghai', - 'St.-Petersburg', - 'Tbilisi', - 'Tokyo', - ]); + expect(body).toEqual(states); expect(status).toBe(200); }); @@ -498,24 +477,7 @@ describe('/search', () => { const { status, body } = await request(app) .get('/search/suggestions?type=city') .set('Authorization', `Bearer ${admin.accessToken}`); - expect(body).toEqual([ - 'Accra', - 'Berlin', - 'Glarus', - 'Havana', - 'Marrakesh', - 'Montalbán de Córdoba', - 'New York City', - 'Palisade', - 'Paris', - 'Philadelphia', - 'Saint Petersburg', - 'Shanghai', - 'Singapore', - 'Stanley', - 'Tbilisi', - 'Tokyo', - ]); + expect(body).toEqual(cities); expect(status).toBe(200); }); diff --git a/e2e/src/cli/specs/upload.e2e-spec.ts b/e2e/src/cli/specs/upload.e2e-spec.ts index a74a57c711..b0b61bf51a 100644 --- a/e2e/src/cli/specs/upload.e2e-spec.ts +++ b/e2e/src/cli/specs/upload.e2e-spec.ts @@ -1,4 +1,5 @@ import { LoginResponseDto, getAllAlbums, getAllAssets } from '@immich/sdk'; +import { readFileSync } from 'node:fs'; import { mkdir, readdir, rm, symlink } from 'node:fs/promises'; import { asKeyAuth, immichCli, testAssetDir, utils } from 'src/utils'; import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; @@ -23,7 +24,7 @@ describe(`immich upload`, () => { const { stderr, stdout, exitCode } = await immichCli(['upload', `${testAssetDir}/albums/nature/silver_fir.jpg`]); expect(stderr).toBe(''); expect(stdout.split('\n')).toEqual( - expect.arrayContaining([expect.stringContaining('Successfully uploaded 1 asset')]), + expect.arrayContaining([expect.stringContaining('Successfully uploaded 1 new asset')]), ); expect(exitCode).toBe(0); @@ -35,7 +36,7 @@ describe(`immich upload`, () => { const first = await immichCli(['upload', `${testAssetDir}/albums/nature/silver_fir.jpg`]); expect(first.stderr).toBe(''); expect(first.stdout.split('\n')).toEqual( - expect.arrayContaining([expect.stringContaining('Successfully uploaded 1 asset')]), + expect.arrayContaining([expect.stringContaining('Successfully uploaded 1 new asset')]), ); expect(first.exitCode).toBe(0); @@ -69,7 +70,7 @@ describe(`immich upload`, () => { const { stderr, stdout, exitCode } = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--recursive']); expect(stderr).toBe(''); expect(stdout.split('\n')).toEqual( - expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 assets')]), + expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 new assets')]), ); expect(exitCode).toBe(0); @@ -88,7 +89,7 @@ describe(`immich upload`, () => { ]); expect(stdout.split('\n')).toEqual( expect.arrayContaining([ - expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Successfully uploaded 9 new assets'), expect.stringContaining('Successfully created 1 new album'), expect.stringContaining('Successfully updated 9 assets'), ]), @@ -107,7 +108,7 @@ describe(`immich upload`, () => { it('should add existing assets to albums', async () => { const response1 = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--recursive']); expect(response1.stdout.split('\n')).toEqual( - expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 assets')]), + expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 new assets')]), ); expect(response1.stderr).toBe(''); expect(response1.exitCode).toBe(0); @@ -147,7 +148,7 @@ describe(`immich upload`, () => { ]); expect(stdout.split('\n')).toEqual( expect.arrayContaining([ - expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Successfully uploaded 9 new assets'), expect.stringContaining('Successfully created 1 new album'), expect.stringContaining('Successfully updated 9 assets'), ]), @@ -180,7 +181,7 @@ describe(`immich upload`, () => { expect(stdout.split('\n')).toEqual( expect.arrayContaining([ - expect.stringContaining('Successfully uploaded 9 assets'), + expect.stringContaining('Successfully uploaded 9 new assets'), expect.stringContaining('Deleting assets that have been uploaded'), ]), ); @@ -192,6 +193,32 @@ describe(`immich upload`, () => { }); }); + describe('immich upload --skip-hash', () => { + it('should skip hashing', async () => { + const filename = `albums/nature/silver_fir.jpg`; + await utils.createAsset(admin.accessToken, { + assetData: { + bytes: readFileSync(`${testAssetDir}/${filename}`), + filename: 'silver_fit.jpg', + }, + }); + const { stderr, stdout, exitCode } = await immichCli(['upload', `${testAssetDir}/${filename}`, '--skip-hash']); + + expect(stderr).toBe(''); + expect(stdout.split('\n')).toEqual( + expect.arrayContaining([ + 'Skipping hash check, assuming all files are new', + expect.stringContaining('Successfully uploaded 0 new assets'), + expect.stringContaining('Skipped 1 duplicate asset'), + ]), + ); + expect(exitCode).toBe(0); + + const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); + expect(assets.length).toBe(1); + }); + }); + describe('immich upload --concurrency ', () => { it('should work', async () => { const { stderr, stdout, exitCode } = await immichCli([ @@ -203,7 +230,10 @@ describe(`immich upload`, () => { expect(stderr).toBe(''); expect(stdout.split('\n')).toEqual( - expect.arrayContaining([expect.stringContaining('Successfully uploaded 9 assets')]), + expect.arrayContaining([ + 'Found 9 new files and 0 duplicates', + expect.stringContaining('Successfully uploaded 9 new assets'), + ]), ); expect(exitCode).toBe(0); diff --git a/e2e/src/setup.ts b/e2e/src/setup.ts index a3d96ac17f..e9395ddd35 100644 --- a/e2e/src/setup.ts +++ b/e2e/src/setup.ts @@ -1,7 +1,7 @@ import { exec, spawn } from 'node:child_process'; import { setTimeout } from 'node:timers'; -export default async () => { +const setup = async () => { let _resolve: () => unknown; let _reject: (error: Error) => unknown; @@ -31,3 +31,5 @@ export default async () => { await new Promise((resolve) => exec('docker compose down', () => resolve())); }; }; + +export default setup; diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index d8302a9e31..a46653eb11 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -1,4 +1,5 @@ import { + AllJobStatusResponseDto, AssetFileUploadResponseDto, AssetResponseDto, CreateAlbumDto, @@ -18,11 +19,14 @@ import { defaults, deleteAssets, getAllAssets, + getAllJobsStatus, getAssetInfo, + getConfigDefaults, login, searchMetadata, setAdminOnboarding, signUpAdmin, + updateConfig, validate, } from '@immich/sdk'; import { BrowserContext } from '@playwright/test'; @@ -31,6 +35,7 @@ import { createHash } from 'node:crypto'; import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path, { dirname } from 'node:path'; +import { setTimeout as setAsyncTimeout } from 'node:timers/promises'; import { promisify } from 'node:util'; import pg from 'pg'; import { io, type Socket } from 'socket.io-client'; @@ -136,6 +141,7 @@ export const utils = { 'user_token', 'users', 'system_metadata', + 'system_config', ]; const sql: string[] = []; @@ -145,7 +151,12 @@ export const utils = { } for (const table of tables) { - sql.push(`DELETE FROM ${table} CASCADE;`); + if (table === 'system_metadata') { + // prevent reverse geocoder from being re-initialized + sql.push(`DELETE FROM "system_metadata" where "key" != 'reverse-geocoding-state';`); + } else { + sql.push(`DELETE FROM ${table} CASCADE;`); + } } await client.query(sql.join('\n')); @@ -209,35 +220,33 @@ export const utils = { } }, - waitForWebsocketEvent: async ({ event, id, total: count, timeout: ms }: WaitOptions): Promise => { - if (!id && !count) { - throw new Error('id or count must be provided for waitForWebsocketEvent'); - } - - const type = id ? `id=${id}` : `count=${count}`; - console.log(`Waiting for ${event} [${type}]`); - const set = events[event]; - if ((id && set.has(id)) || (count && set.size >= count)) { - return; - } - + waitForWebsocketEvent: ({ event, id, total: count, timeout: ms }: WaitOptions): Promise => { return new Promise((resolve, reject) => { + if (!id && !count) { + reject(new Error('id or count must be provided for waitForWebsocketEvent')); + } + const timeout = setTimeout(() => reject(new Error(`Timed out waiting for ${event} event`)), ms || 10_000); + const type = id ? `id=${id}` : `count=${count}`; + console.log(`Waiting for ${event} [${type}]`); + const set = events[event]; + const onId = () => { + clearTimeout(timeout); + resolve(); + }; + if ((id && set.has(id)) || (count && set.size >= count)) { + onId(); + return; + } if (id) { - idCallbacks[id] = () => { - clearTimeout(timeout); - resolve(); - }; + idCallbacks[id] = onId; } if (count) { countCallbacks[event] = { count, - callback: () => { - clearTimeout(timeout); - resolve(); - }, + callback: onId, }; } }); @@ -309,9 +318,7 @@ export const utils = { if (!existsSync(dirname(path))) { mkdirSync(dirname(path), { recursive: true }); } - if (!existsSync(path)) { - writeFileSync(path, makeRandomImage()); - } + writeFileSync(path, makeRandomImage()); }, removeImageFile: (path: string) => { @@ -406,6 +413,39 @@ export const utils = { }, ]), + resetTempFolder: () => { + rmSync(`${testAssetDir}/temp`, { recursive: true, force: true }); + mkdirSync(`${testAssetDir}/temp`, { recursive: true }); + }, + + resetAdminConfig: async (accessToken: string) => { + const defaultConfig = await getConfigDefaults({ headers: asBearerAuth(accessToken) }); + await updateConfig({ systemConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) }); + }, + + isQueueEmpty: async (accessToken: string, queue: keyof AllJobStatusResponseDto) => { + const queues = await getAllJobsStatus({ headers: asBearerAuth(accessToken) }); + const jobCounts = queues[queue].jobCounts; + return !jobCounts.active && !jobCounts.waiting; + }, + + waitForQueueFinish: (accessToken: string, queue: keyof AllJobStatusResponseDto, ms?: number) => { + return new Promise(async (resolve, reject) => { + const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 10_000); + + while (true) { + const done = await utils.isQueueEmpty(accessToken, queue); + if (done) { + break; + } + await setAsyncTimeout(200); + } + + clearTimeout(timeout); + resolve(); + }); + }, + cliLogin: async (accessToken: string) => { const key = await utils.createApiKey(accessToken); await immichCli(['login', app, `${key.secret}`]); diff --git a/e2e/vitest.config.ts b/e2e/vitest.config.ts index d7dcde4c38..9b9670c042 100644 --- a/e2e/vitest.config.ts +++ b/e2e/vitest.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ test: { include: ['src/{api,cli}/specs/*.e2e-spec.ts'], globalSetup, - testTimeout: 10_000, + testTimeout: 15_000, poolOptions: { threads: { singleThread: true, diff --git a/install.sh b/install.sh index 232ee1597e..92d9c1b8be 100755 --- a/install.sh +++ b/install.sh @@ -1,62 +1,78 @@ #!/usr/bin/env bash +set -o nounset +set -o pipefail -echo "Starting Immich installation..." - -ip_address=$(hostname -I | awk '{print $1}') - -create_immich_directory() { +create_immich_directory() { local -r Tgt='./immich-app' echo "Creating Immich directory..." - mkdir -p ./immich-app - cd ./immich-app || exit + if [[ -e $Tgt ]]; then + echo "Found existing directory $Tgt, will overwrite YAML files" + else + mkdir "$Tgt" || return + fi + cd "$Tgt" || return } download_docker_compose_file() { echo "Downloading docker-compose.yml..." - curl -L https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml -o ./docker-compose.yml >/dev/null 2>&1 + "${Curl[@]}" "$RepoUrl"/docker-compose.yml -o ./docker-compose.yml } download_dot_env_file() { echo "Downloading .env file..." - curl -L https://github.com/immich-app/immich/releases/latest/download/example.env -o ./.env >/dev/null 2>&1 + "${Curl[@]}" "$RepoUrl"/example.env -o ./.env } start_docker_compose() { echo "Starting Immich's docker containers" - if docker compose >/dev/null 2>&1; then - docker_bin="docker compose" - elif docker-compose >/dev/null 2>&1; then - docker_bin="docker-compose" - else - echo "Cannot find \`docker compose\` or \`docker-compose\`." - exit 1 + if ! docker compose >/dev/null 2>&1; then + echo "failed to find 'docker compose'" + return 1 fi - if $docker_bin up --remove-orphans -d; then - show_friendly_message - exit 0 - else + if ! docker compose up --remove-orphans -d; then echo "Could not start. Check for errors above." - exit 1 + return 1 fi + show_friendly_message } show_friendly_message() { - echo "Successfully deployed Immich!" - echo "You can access the website at http://$ip_address:2283 and the server URL for the mobile app is http://$ip_address:2283/api" - echo "---------------------------------------------------" - echo "If you want to configure custom information of the server, including the database, Redis information, or the backup (or upload) location, etc. + local ip_address + ip_address=$(hostname -I | awk '{print $1}') + cat << EOF +Successfully deployed Immich! +You can access the website at http://$ip_address:2283 and the server URL for the mobile app is http://$ip_address:2283/api +--------------------------------------------------- +If you want to configure custom information of the server, including the database, Redis information, or the backup (or upload) location, etc. - 1. First bring down the containers with the command 'docker-compose down' in the immich-app directory, + 1. First bring down the containers with the command 'docker compose down' in the immich-app directory, 2. Then change the information that fits your needs in the '.env' file, - 3. Finally, bring the containers back up with the command 'docker-compose up --remove-orphans -d' in the immich-app directory" - + 3. Finally, bring the containers back up with the command 'docker compose up --remove-orphans -d' in the immich-app directory +EOF } # MAIN -create_immich_directory -download_docker_compose_file -download_dot_env_file -start_docker_compose +main() { + echo "Starting Immich installation..." + local -r RepoUrl='https://github.com/immich-app/immich/releases/latest/download' + local -a Curl + if command -v curl >/dev/null; then + Curl=(curl -fsSL) + else + echo 'no curl binary found; please install curl and try again' + return 14 + fi + + create_immich_directory || { echo 'error creating Immich directory'; return 10; } + download_docker_compose_file || { echo 'error downloading Docker Compose file'; return 11; } + download_dot_env_file || { echo 'error downloading .env'; return 12; } + start_docker_compose || { echo 'error starting Docker'; return 13; } + return 0; } + +main +Exit=$? +[[ $Exit == 0 ]] || echo "There was an error installing Immich. Exit code: $Exit. Please provide these logs when asking for assistance." +exit "$Exit" diff --git a/machine-learning/Dockerfile b/machine-learning/Dockerfile index 3f111401c0..9a10824586 100644 --- a/machine-learning/Dockerfile +++ b/machine-learning/Dockerfile @@ -1,6 +1,6 @@ ARG DEVICE=cpu -FROM python:3.11-bookworm@sha256:e2ed446c899827ed992f8a5a8875fa0853fcab32581e61418b650322061aa3c4 as builder-cpu +FROM python:3.11-bookworm@sha256:abe34d06fca0165d40375b0e840fd3296ad2d075954d3a400d4efefe0e9b3012 as builder-cpu FROM openvino/ubuntu22_runtime:2023.3.0@sha256:176646df619032ea6c10faf842867119c393e7497b7f88b5e307e932a0fd5aa8 as builder-openvino USER root @@ -36,7 +36,7 @@ RUN python3 -m venv /opt/venv COPY poetry.lock pyproject.toml ./ RUN poetry install --sync --no-interaction --no-ansi --no-root --with ${DEVICE} --without dev -FROM python:3.11-slim-bookworm@sha256:90f8795536170fd08236d2ceb74fe7065dbf74f738d8b84bfbf263656654dc9b as prod-cpu +FROM python:3.11-slim-bookworm@sha256:dad770592ab3582ab2dabcf0e18a863df9d86bd9d23efcfa614110ce49ac20e4 as prod-cpu FROM openvino/ubuntu22_runtime:2023.3.0@sha256:176646df619032ea6c10faf842867119c393e7497b7f88b5e307e932a0fd5aa8 as prod-openvino USER root diff --git a/machine-learning/README.md b/machine-learning/README.md index b30e18ceb4..d7d099a87e 100644 --- a/machine-learning/README.md +++ b/machine-learning/README.md @@ -7,7 +7,7 @@ This project uses [Poetry](https://python-poetry.org/docs/#installation), so be sure to install it first. Running `poetry install --no-root --with dev --with cpu` will install everything you need in an isolated virtual environment. -CUDA and OpenVINO are supported as acceleration APIs. To use them, you can replace `--with cpu` with either of `--with cuda` or `--with openvino`. +CUDA and OpenVINO are supported as acceleration APIs. To use them, you can replace `--with cpu` with either of `--with cuda` or `--with openvino`. In the case of CUDA, a [compute capability](https://developer.nvidia.com/cuda-gpus) of 5.2 or higher is required. To add or remove dependencies, you can use the commands `poetry add $PACKAGE_NAME` and `poetry remove $PACKAGE_NAME`, respectively. Be sure to commit the `poetry.lock` and `pyproject.toml` files with `poetry lock --no-update` to reflect any changes in dependencies. diff --git a/machine-learning/app/models/transforms.py b/machine-learning/app/models/transforms.py index a41b7cd3d8..7ed001b8c2 100644 --- a/machine-learning/app/models/transforms.py +++ b/machine-learning/app/models/transforms.py @@ -7,9 +7,9 @@ _PIL_RESAMPLING_METHODS = {resampling.name.lower(): resampling for resampling in def resize(img: Image.Image, size: int) -> Image.Image: if img.width < img.height: - return img.resize((size, int((img.height / img.width) * size)), resample=Image.BICUBIC) + return img.resize((size, int((img.height / img.width) * size)), resample=Image.Resampling.BICUBIC) else: - return img.resize((int((img.width / img.height) * size), size), resample=Image.BICUBIC) + return img.resize((int((img.width / img.height) * size), size), resample=Image.Resampling.BICUBIC) # https://stackoverflow.com/a/60883103 diff --git a/machine-learning/export/Dockerfile b/machine-learning/export/Dockerfile index fa3f7068ef..92ec7b576a 100644 --- a/machine-learning/export/Dockerfile +++ b/machine-learning/export/Dockerfile @@ -1,4 +1,4 @@ -FROM mambaorg/micromamba:bookworm-slim@sha256:3624db3aee11d2f3f00d25f691aaaf8834b8bc4ec1b340dcdb48ef37281ea604 as builder +FROM mambaorg/micromamba:bookworm-slim@sha256:4de614588f7b3d6598a6cd571297157b57ac85b1a8ff854cf10231bb0c50e3ac as builder ENV NODE_ENV=production \ TRANSFORMERS_CACHE=/cache \ diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock index a6efaa1e66..6b98b8f521 100644 --- a/machine-learning/poetry.lock +++ b/machine-learning/poetry.lock @@ -680,18 +680,18 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.110.0" +version = "0.110.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.0-py3-none-any.whl", hash = "sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b"}, - {file = "fastapi-0.110.0.tar.gz", hash = "sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3"}, + {file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"}, + {file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.36.3,<0.37.0" +starlette = ">=0.37.2,<0.38.0" typing-extensions = ">=4.8.0" [package.extras] @@ -2192,79 +2192,80 @@ files = [ [[package]] name = "pillow" -version = "10.2.0" +version = "10.3.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, - {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, - {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, - {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, - {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, - {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, - {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, - {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, - {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, - {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, - {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, - {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, - {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, - {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, - {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, - {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, - {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, - {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] [package.extras] @@ -2383,47 +2384,47 @@ files = [ [[package]] name = "pydantic" -version = "1.10.14" +version = "1.10.15" description = "Data validation and settings management using python type hints" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-1.10.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054"}, - {file = "pydantic-1.10.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87"}, - {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d"}, - {file = "pydantic-1.10.14-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e"}, - {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9"}, - {file = "pydantic-1.10.14-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a"}, - {file = "pydantic-1.10.14-cp310-cp310-win_amd64.whl", hash = "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf"}, - {file = "pydantic-1.10.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7"}, - {file = "pydantic-1.10.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"}, - {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663"}, - {file = "pydantic-1.10.14-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f"}, - {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046"}, - {file = "pydantic-1.10.14-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca"}, - {file = "pydantic-1.10.14-cp311-cp311-win_amd64.whl", hash = "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f"}, - {file = "pydantic-1.10.14-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c"}, - {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5"}, - {file = "pydantic-1.10.14-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c"}, - {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc"}, - {file = "pydantic-1.10.14-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe"}, - {file = "pydantic-1.10.14-cp37-cp37m-win_amd64.whl", hash = "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01"}, - {file = "pydantic-1.10.14-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee"}, - {file = "pydantic-1.10.14-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597"}, - {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee"}, - {file = "pydantic-1.10.14-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f"}, - {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022"}, - {file = "pydantic-1.10.14-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f"}, - {file = "pydantic-1.10.14-cp38-cp38-win_amd64.whl", hash = "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a"}, - {file = "pydantic-1.10.14-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4"}, - {file = "pydantic-1.10.14-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347"}, - {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7"}, - {file = "pydantic-1.10.14-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea"}, - {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f"}, - {file = "pydantic-1.10.14-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593"}, - {file = "pydantic-1.10.14-cp39-cp39-win_amd64.whl", hash = "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8"}, - {file = "pydantic-1.10.14-py3-none-any.whl", hash = "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c"}, - {file = "pydantic-1.10.14.tar.gz", hash = "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6"}, + {file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"}, + {file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"}, + {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"}, + {file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"}, + {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"}, + {file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"}, + {file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"}, + {file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"}, + {file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"}, + {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"}, + {file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"}, + {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"}, + {file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"}, + {file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"}, + {file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"}, + {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"}, + {file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"}, + {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"}, + {file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"}, + {file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"}, + {file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"}, + {file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"}, + {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"}, + {file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"}, + {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"}, + {file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"}, + {file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"}, + {file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"}, + {file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"}, + {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"}, + {file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"}, + {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"}, + {file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"}, + {file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"}, + {file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"}, + {file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"}, ] [package.dependencies] @@ -2846,28 +2847,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.4" +version = "0.3.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, - {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, - {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, - {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, - {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, - {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, - {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, - {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0e8377cccb2f07abd25e84fc5b2cbe48eeb0fea9f1719cad7caedb061d70e5ce"}, + {file = "ruff-0.3.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:15a4d1cc1e64e556fa0d67bfd388fed416b7f3b26d5d1c3e7d192c897e39ba4b"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d28bdf3d7dc71dd46929fafeec98ba89b7c3550c3f0978e36389b5631b793663"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:379b67d4f49774ba679593b232dcd90d9e10f04d96e3c8ce4a28037ae473f7bb"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c060aea8ad5ef21cdfbbe05475ab5104ce7827b639a78dd55383a6e9895b7c51"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ebf8f615dde968272d70502c083ebf963b6781aacd3079081e03b32adfe4d58a"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d48098bd8f5c38897b03604f5428901b65e3c97d40b3952e38637b5404b739a2"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8a4fda219bf9024692b1bc68c9cff4b80507879ada8769dc7e985755d662ea"}, + {file = "ruff-0.3.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c44e0149f1d8b48c4d5c33d88c677a4aa22fd09b1683d6a7ff55b816b5d074f"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3050ec0af72b709a62ecc2aca941b9cd479a7bf2b36cc4562f0033d688e44fa1"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a29cc38e4c1ab00da18a3f6777f8b50099d73326981bb7d182e54a9a21bb4ff7"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b15cc59c19edca917f51b1956637db47e200b0fc5e6e1878233d3a938384b0b"}, + {file = "ruff-0.3.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e491045781b1e38b72c91247cf4634f040f8d0cb3e6d3d64d38dcf43616650b4"}, + {file = "ruff-0.3.7-py3-none-win32.whl", hash = "sha256:bc931de87593d64fad3a22e201e55ad76271f1d5bfc44e1a1887edd0903c7d9f"}, + {file = "ruff-0.3.7-py3-none-win_amd64.whl", hash = "sha256:5ef0e501e1e39f35e03c2acb1d1238c595b8bb36cf7a170e7c1df1b73da00e74"}, + {file = "ruff-0.3.7-py3-none-win_arm64.whl", hash = "sha256:789e144f6dc7019d1f92a812891c645274ed08af6037d11fc65fcbc183b7d59f"}, + {file = "ruff-0.3.7.tar.gz", hash = "sha256:d5c1aebee5162c2226784800ae031f660c350e7a3402c4d1f8ea4e97e232e3ba"}, ] [[package]] @@ -3047,13 +3048,13 @@ files = [ [[package]] name = "starlette" -version = "0.36.3" +version = "0.37.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.36.3-py3-none-any.whl", hash = "sha256:13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044"}, - {file = "starlette-0.36.3.tar.gz", hash = "sha256:90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080"}, + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, ] [package.dependencies] diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 864390715a..1a6ca76757 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -439,6 +439,8 @@ "shared_link_edit_expire_after_option_hours": "{} hours", "shared_link_edit_expire_after_option_minute": "1 minute", "shared_link_edit_expire_after_option_minutes": "{} minutes", + "shared_link_edit_expire_after_option_months": "{} months", + "shared_link_edit_expire_after_option_year": "{} year", "shared_link_edit_expire_after_option_never": "Never", "shared_link_edit_password": "Password", "shared_link_edit_password_hint": "Enter the share password", @@ -509,5 +511,7 @@ "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89", "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack" -} \ No newline at end of file + "viewer_unstack": "Un-Stack", + "haptic_feedback_title": "Haptic Feedback", + "haptic_feedback_switch": "Enable haptic feedback" +} diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 059c0c976d..33de70d757 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -20,6 +20,7 @@ import 'package:immich_mobile/modules/asset_viewer/ui/gallery_app_bar.dart'; import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; import 'package:immich_mobile/shared/ui/photo_view/photo_view_gallery.dart'; @@ -303,7 +304,9 @@ class GalleryViewerPage extends HookConsumerWidget { scrollDirection: Axis.horizontal, onPageChanged: (value) async { final next = currentIndex.value < value ? value + 1 : value - 1; - HapticFeedback.selectionClick(); + + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + currentIndex.value = value; stackIndex.value = -1; isPlayingVideo.value = false; diff --git a/mobile/lib/modules/backup/ui/album_info_card.dart b/mobile/lib/modules/backup/ui/album_info_card.dart index 5380360ff1..a274f1c5e8 100644 --- a/mobile/lib/modules/backup/ui/album_info_card.dart +++ b/mobile/lib/modules/backup/ui/album_info_card.dart @@ -1,13 +1,13 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { @@ -21,6 +21,7 @@ class AlbumInfoCard extends HookConsumerWidget { ref.watch(backupProvider).selectedBackupAlbums.contains(album); final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final isDarkTheme = context.isDarkTheme; ColorFilter selectedFilter = ColorFilter.mode( @@ -78,7 +79,7 @@ class AlbumInfoCard extends HookConsumerWidget { return GestureDetector( onTap: () { - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); if (isSelected) { ref.read(backupProvider.notifier).removeAlbumForBackup(album); @@ -87,7 +88,7 @@ class AlbumInfoCard extends HookConsumerWidget { } }, onDoubleTap: () { - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); if (isExcluded) { // Remove from exclude album list diff --git a/mobile/lib/modules/backup/ui/album_info_list_tile.dart b/mobile/lib/modules/backup/ui/album_info_list_tile.dart index dcf0923a11..40fdfa8897 100644 --- a/mobile/lib/modules/backup/ui/album_info_list_tile.dart +++ b/mobile/lib/modules/backup/ui/album_info_list_tile.dart @@ -1,6 +1,5 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -8,6 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_toast.dart'; class AlbumInfoListTile extends HookConsumerWidget { @@ -68,7 +68,7 @@ class AlbumInfoListTile extends HookConsumerWidget { return GestureDetector( onDoubleTap: () { - HapticFeedback.selectionClick(); + ref.watch(hapticFeedbackProvider.notifier).selectionClick(); if (isExcluded) { // Remove from exclude album list @@ -93,7 +93,7 @@ class AlbumInfoListTile extends HookConsumerWidget { tileColor: buildTileColor(), contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), onTap: () { - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); if (isSelected) { ref.read(backupProvider.notifier).removeAlbumForBackup(album); } else { diff --git a/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart b/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart index 0c06fc1a1b..03236e3930 100644 --- a/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart +++ b/mobile/lib/modules/home/ui/asset_grid/group_divider_title.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; class GroupDividerTitle extends HookConsumerWidget { const GroupDividerTitle({ @@ -38,7 +38,7 @@ class GroupDividerTitle extends HookConsumerWidget { ); void handleTitleIconClick() { - HapticFeedback.heavyImpact(); + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); if (selected) { onDeselect(); } else { diff --git a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart index 4c520fe6fc..5ece42d5cf 100644 --- a/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart @@ -6,7 +6,7 @@ import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/collection_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart'; @@ -15,6 +15,7 @@ import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; import 'package:immich_mobile/modules/home/ui/control_bottom_app_bar.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'asset_grid_data_structure.dart'; @@ -27,7 +28,7 @@ typedef ImmichAssetGridSelectionListener = void Function( Set, ); -class ImmichAssetGridView extends StatefulWidget { +class ImmichAssetGridView extends ConsumerStatefulWidget { final RenderList renderList; final int assetsPerRow; final double margin; @@ -69,12 +70,12 @@ class ImmichAssetGridView extends StatefulWidget { }); @override - State createState() { + createState() { return ImmichAssetGridViewState(); } } -class ImmichAssetGridViewState extends State { +class ImmichAssetGridViewState extends ConsumerState { final ItemScrollController _itemScrollController = ItemScrollController(); final ScrollOffsetController _scrollOffsetController = ScrollOffsetController(); @@ -314,7 +315,7 @@ class ImmichAssetGridViewState extends State { final now = Timeline.now; if (now > (_hapticFeedbackTS + feedbackInterval)) { _hapticFeedbackTS = now; - HapticFeedback.mediumImpact(); + ref.read(hapticFeedbackProvider.notifier).mediumImpact(); } } } diff --git a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart index a194bc2ade..f06be0289b 100644 --- a/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart +++ b/mobile/lib/modules/home/ui/asset_grid/thumbnail_image.dart @@ -1,14 +1,15 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_thumbnail.dart'; import 'package:immich_mobile/utils/storage_indicator.dart'; import 'package:isar/isar.dart'; -class ThumbnailImage extends StatelessWidget { +class ThumbnailImage extends ConsumerWidget { final Asset asset; final int index; final Asset Function(int index) loadAsset; @@ -37,7 +38,7 @@ class ThumbnailImage extends StatelessWidget { }); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final assetContainerColor = context.isDarkTheme ? Colors.blueGrey : context.themeData.primaryColorLight; @@ -186,7 +187,7 @@ class ThumbnailImage extends StatelessWidget { }, onLongPress: () { onSelect?.call(); - HapticFeedback.heavyImpact(); + ref.read(hapticFeedbackProvider.notifier).heavyImpact(); }, child: Stack( children: [ diff --git a/mobile/lib/modules/login/providers/authentication.provider.dart b/mobile/lib/modules/login/providers/authentication.provider.dart index e010024332..03d06bd140 100644 --- a/mobile/lib/modules/login/providers/authentication.provider.dart +++ b/mobile/lib/modules/login/providers/authentication.provider.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_udid/flutter_udid.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart'; @@ -92,7 +91,6 @@ class AuthenticationNotifier extends StateNotifier { serverUrl: serverUrl, ); } catch (e) { - HapticFeedback.vibrate(); debugPrint("Error logging in $e"); return false; } diff --git a/mobile/lib/modules/memories/ui/memory_lane.dart b/mobile/lib/modules/memories/ui/memory_lane.dart index eb72c15e8e..d48785a78b 100644 --- a/mobile/lib/modules/memories/ui/memory_lane.dart +++ b/mobile/lib/modules/memories/ui/memory_lane.dart @@ -1,10 +1,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart'; import 'package:immich_mobile/modules/memories/providers/memory.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; class MemoryLane extends HookConsumerWidget { @@ -33,7 +33,9 @@ class MemoryLane extends HookConsumerWidget { return GestureDetector( onTap: () { - HapticFeedback.heavyImpact(); + ref + .read(hapticFeedbackProvider.notifier) + .heavyImpact(); context.pushRoute( MemoryRoute( memories: memories, diff --git a/mobile/lib/modules/memories/views/memory_page.dart b/mobile/lib/modules/memories/views/memory_page.dart index aa968303b3..9a7032f828 100644 --- a/mobile/lib/modules/memories/views/memory_page.dart +++ b/mobile/lib/modules/memories/views/memory_page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/modules/memories/ui/memory_card.dart'; import 'package:immich_mobile/modules/memories/ui/memory_epilogue.dart'; import 'package:immich_mobile/modules/memories/ui/memory_progress_indicator.dart'; import 'package:immich_mobile/shared/models/asset.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/ui/immich_image.dart'; @RoutePage() @@ -127,7 +128,7 @@ class MemoryPage extends HookConsumerWidget { } Future onAssetChanged(int otherIndex) async { - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); currentAssetPage.value = otherIndex; updateProgressText(); // Wait for page change animation to finish @@ -169,7 +170,7 @@ class MemoryPage extends HookConsumerWidget { scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { - HapticFeedback.mediumImpact(); + ref.read(hapticFeedbackProvider.notifier).mediumImpact(); if (pageNumber < memories.length) { currentMemoryIndex.value = pageNumber; currentMemory.value = memories[pageNumber]; diff --git a/mobile/lib/modules/settings/services/app_settings.service.dart b/mobile/lib/modules/settings/services/app_settings.service.dart index 98e8464425..b7be3ca5e3 100644 --- a/mobile/lib/modules/settings/services/app_settings.service.dart +++ b/mobile/lib/modules/settings/services/app_settings.service.dart @@ -58,6 +58,7 @@ enum AppSettingsEnum { null, false, ), + enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), ; const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/modules/settings/ui/language_settings.dart b/mobile/lib/modules/settings/ui/language_settings.dart new file mode 100644 index 0000000000..ebe8673135 --- /dev/null +++ b/mobile/lib/modules/settings/ui/language_settings.dart @@ -0,0 +1,81 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/locales.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/modules/backup/background_service/localization.dart'; + +class LanguageSettings extends HookConsumerWidget { + const LanguageSettings({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final currentLocale = context.locale; + final textController = useTextEditingController( + text: locales.keys.firstWhere( + (countryName) => locales[countryName] == currentLocale, + ), + ); + + final selectedLocale = useState(currentLocale); + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + LayoutBuilder( + builder: (context, constraints) { + return DropdownMenu( + width: constraints.maxWidth, + inputDecorationTheme: InputDecorationTheme( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20), + ), + contentPadding: const EdgeInsets.only(left: 16), + ), + menuStyle: MenuStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + ), + backgroundColor: MaterialStatePropertyAll( + context.isDarkTheme + ? Colors.grey[900]! + : context.scaffoldBackgroundColor, + ), + ), + menuHeight: context.height * 0.5, + hintText: "Languages", + label: const Text('Languages'), + dropdownMenuEntries: locales.keys + .map( + (countryName) => DropdownMenuEntry( + value: locales[countryName], + label: countryName, + ), + ) + .toList(), + controller: textController, + onSelected: (value) { + if (value != null) { + selectedLocale.value = value; + } + }, + ); + }, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: selectedLocale.value == currentLocale + ? null + : () { + context.setLocale(selectedLocale.value); + loadTranslations(); + }, + child: const Text('setting_languages_apply').tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/modules/settings/ui/preference_settings/haptic_setting.dart b/mobile/lib/modules/settings/ui/preference_settings/haptic_setting.dart new file mode 100644 index 0000000000..290dd5aafa --- /dev/null +++ b/mobile/lib/modules/settings/ui/preference_settings/haptic_setting.dart @@ -0,0 +1,38 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; +import 'package:immich_mobile/modules/settings/ui/settings_sub_title.dart'; +import 'package:immich_mobile/modules/settings/ui/settings_switch_list_tile.dart'; +import 'package:immich_mobile/modules/settings/utils/app_settings_update_hook.dart'; + +class HapticSetting extends HookConsumerWidget { + const HapticSetting({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final hapticFeedbackSetting = + useAppSettingsState(AppSettingsEnum.enableHapticFeedback); + final isHapticFeedbackEnabled = + useValueNotifier(hapticFeedbackSetting.value); + + onHapticFeedbackChange(bool isEnabled) { + hapticFeedbackSetting.value = isEnabled; + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SettingsSubTitle(title: "haptic_feedback_title".tr()), + SettingsSwitchListTile( + valueNotifier: isHapticFeedbackEnabled, + title: 'haptic_feedback_switch'.tr(), + onChanged: onHapticFeedbackChange, + ), + ], + ); + } +} diff --git a/mobile/lib/modules/settings/ui/preference_settings/preference_setting.dart b/mobile/lib/modules/settings/ui/preference_settings/preference_setting.dart index f75891437c..ccc0e5b161 100644 --- a/mobile/lib/modules/settings/ui/preference_settings/preference_setting.dart +++ b/mobile/lib/modules/settings/ui/preference_settings/preference_setting.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:immich_mobile/modules/settings/ui/preference_settings/haptic_setting.dart'; import 'package:immich_mobile/modules/settings/ui/preference_settings/theme_setting.dart'; import 'package:immich_mobile/modules/settings/ui/settings_sub_page_scaffold.dart'; @@ -11,6 +12,7 @@ class PreferenceSetting extends StatelessWidget { Widget build(BuildContext context) { const preferenceSettings = [ ThemeSetting(), + HapticSetting(), ]; return const SettingsSubPageScaffold(settings: preferenceSettings); diff --git a/mobile/lib/modules/settings/views/settings_page.dart b/mobile/lib/modules/settings/views/settings_page.dart index bfe8899924..3ca723e921 100644 --- a/mobile/lib/modules/settings/views/settings_page.dart +++ b/mobile/lib/modules/settings/views/settings_page.dart @@ -2,14 +2,12 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/modules/backup/background_service/localization.dart'; import 'package:immich_mobile/modules/settings/ui/advanced_settings.dart'; import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/modules/settings/ui/backup_settings/backup_settings.dart'; import 'package:immich_mobile/modules/settings/ui/image_viewer_quality_setting.dart'; +import 'package:immich_mobile/modules/settings/ui/language_settings.dart'; import 'package:immich_mobile/modules/settings/ui/notification_setting.dart'; import 'package:immich_mobile/modules/settings/ui/preference_settings/preference_setting.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -42,70 +40,6 @@ enum SettingSection { const SettingSection(this.title, this.icon); } -class LanguageSettings extends HookConsumerWidget { - const LanguageSettings({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final currentLocale = context.locale; - final textController = useTextEditingController( - text: locales.keys.firstWhere( - (countryName) => locales[countryName] == currentLocale, - ), - ); - - final selectedLocale = useState(currentLocale); - - return ListView( - padding: const EdgeInsets.all(16), - children: [ - DropdownMenu( - inputDecorationTheme: InputDecorationTheme( - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(20), - ), - contentPadding: const EdgeInsets.only(left: 16), - ), - menuStyle: MenuStyle( - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - ), - ), - menuHeight: context.height * 0.5, - hintText: "Languages", - label: const Text('Languages'), - dropdownMenuEntries: locales.keys - .map( - (countryName) => DropdownMenuEntry( - value: locales[countryName], - label: countryName, - ), - ) - .toList(), - controller: textController, - onSelected: (value) { - if (value != null) { - selectedLocale.value = value; - } - }, - ), - const SizedBox(height: 16), - ElevatedButton( - onPressed: selectedLocale.value == currentLocale - ? null - : () { - context.setLocale(selectedLocale.value); - loadTranslations(); - }, - child: const Text('setting_languages_apply').tr(), - ), - ], - ); - } -} - @RoutePage() class SettingsPage extends StatelessWidget { const SettingsPage({super.key}); diff --git a/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart b/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart index 56d064e29b..ec80ed80ea 100644 --- a/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart +++ b/mobile/lib/modules/shared_link/views/shared_link_edit_page.dart @@ -271,6 +271,15 @@ class SharedLinkEditPage extends HookConsumerWidget { value: 60 * 24 * 30, label: "shared_link_edit_expire_after_option_days".tr(args: ["30"]), ), + DropdownMenuEntry( + value: 60 * 24 * 30 * 3, + label: + "shared_link_edit_expire_after_option_months".tr(args: ["3"]), + ), + DropdownMenuEntry( + value: 60 * 24 * 30 * 12, + label: "shared_link_edit_expire_after_option_year".tr(args: ["1"]), + ), ], ); } diff --git a/mobile/lib/shared/models/store.dart b/mobile/lib/shared/models/store.dart index b1f28ec0f0..233f6231af 100644 --- a/mobile/lib/shared/models/store.dart +++ b/mobile/lib/shared/models/store.dart @@ -191,6 +191,7 @@ enum StoreKey { selectedAlbumSortReverse(123, type: bool), mapThemeMode(124, type: int), mapwithPartners(125, type: bool), + enableHapticFeedback(126, type: bool), ; const StoreKey( diff --git a/mobile/lib/shared/providers/haptic_feedback.provider.dart b/mobile/lib/shared/providers/haptic_feedback.provider.dart new file mode 100644 index 0000000000..47373a67e9 --- /dev/null +++ b/mobile/lib/shared/providers/haptic_feedback.provider.dart @@ -0,0 +1,56 @@ +import 'package:flutter/services.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart'; +import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; + +final hapticFeedbackProvider = + StateNotifierProvider((ref) { + return HapticNotifier(ref); +}); + +class HapticNotifier extends StateNotifier { + void build() {} + final Ref _ref; + + HapticNotifier(this._ref) : super(null); + + selectionClick() { + if (_ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableHapticFeedback)) { + HapticFeedback.selectionClick(); + } + } + + lightImpact() { + if (_ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableHapticFeedback)) { + HapticFeedback.lightImpact(); + } + } + + mediumImpact() { + if (_ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableHapticFeedback)) { + HapticFeedback.mediumImpact(); + } + } + + heavyImpact() { + if (_ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableHapticFeedback)) { + HapticFeedback.heavyImpact(); + } + } + + vibrate() { + if (_ref + .read(appSettingsServiceProvider) + .getSetting(AppSettingsEnum.enableHapticFeedback)) { + HapticFeedback.vibrate(); + } + } +} diff --git a/mobile/lib/shared/services/immich_logger.service.dart b/mobile/lib/shared/services/immich_logger.service.dart index 967ab2d5f2..2cdc71c4e9 100644 --- a/mobile/lib/shared/services/immich_logger.service.dart +++ b/mobile/lib/shared/services/immich_logger.service.dart @@ -88,18 +88,19 @@ class ImmichLogger { Future shareLogs() async { final tempDir = await getTemporaryDirectory(); final dateTime = DateTime.now().toIso8601String(); - final filePath = '${tempDir.path}/Immich_log_$dateTime.csv'; + final filePath = '${tempDir.path}/Immich_log_$dateTime.log'; final logFile = await File(filePath).create(); final io = logFile.openWrite(); try { - // Write header - io.write("created_at,level,context,message,stacktrace\n"); - // Write messages for (final m in messages) { - io.write( - '${m.createdAt},${m.level},"${m.context1 ?? ""}","${m.message}","${m.context2 ?? ""}"\n', - ); + final created = m.createdAt; + final level = m.level.name.padRight(8); + final logger = (m.context1 ?? "").padRight(20); + final message = m.message; + final error = m.details != null ? " ${m.details} |" : ""; + final stack = m.context2 != null ? "\n${m.context2!}" : ""; + io.write('$created | $level | $logger | $message |$error$stack\n'); } } finally { await io.flush(); diff --git a/mobile/lib/shared/views/splash_screen.dart b/mobile/lib/shared/views/splash_screen.dart index 47b550f9d0..64bc1ec081 100644 --- a/mobile/lib/shared/views/splash_screen.dart +++ b/mobile/lib/shared/views/splash_screen.dart @@ -25,6 +25,7 @@ class SplashScreenPage extends HookConsumerWidget { void performLoggingIn() async { bool isSuccess = false; bool deviceIsOffline = false; + if (accessToken != null && serverUrl != null) { try { // Resolve API server endpoint from user provided serverUrl @@ -50,15 +51,11 @@ class SplashScreenPage extends HookConsumerWidget { offlineLogin: deviceIsOffline, ); } catch (error, stackTrace) { - ref.read(authenticationProvider.notifier).logout(); - log.severe( 'Cannot set success login info', error, stackTrace, ); - - context.pushRoute(const LoginRoute()); } } @@ -76,6 +73,11 @@ class SplashScreenPage extends HookConsumerWidget { } context.replaceRoute(const TabControllerRoute()); } else { + log.severe( + 'Unable to login through offline or online methods - logging out completely', + ); + + ref.read(authenticationProvider.notifier).logout(); // User was unable to login through either offline or online methods context.replaceRoute(const LoginRoute()); } diff --git a/mobile/lib/shared/views/tab_controller_page.dart b/mobile/lib/shared/views/tab_controller_page.dart index 40de493d0b..e1f6fde1ad 100644 --- a/mobile/lib/shared/views/tab_controller_page.dart +++ b/mobile/lib/shared/views/tab_controller_page.dart @@ -1,13 +1,13 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart'; import 'package:immich_mobile/modules/home/providers/multiselect.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/shared/providers/asset.provider.dart'; +import 'package:immich_mobile/shared/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/shared/providers/tab.provider.dart'; @RoutePage() @@ -53,7 +53,7 @@ class TabControllerPage extends HookConsumerWidget { scrollToTopNotifierProvider.scrollToTop(); } - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); tabsRouter.setActiveIndex(index); ref.read(tabProvider.notifier).state = TabEnum.values[index]; }, @@ -107,7 +107,7 @@ class TabControllerPage extends HookConsumerWidget { scrollToTopNotifierProvider.scrollToTop(); } - HapticFeedback.selectionClick(); + ref.read(hapticFeedbackProvider.notifier).selectionClick(); tabsRouter.setActiveIndex(index); ref.read(tabProvider.notifier).state = TabEnum.values[index]; }, diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index 263687549f..b296bbcb55 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -24,6 +24,7 @@ doc/AssetBulkUploadCheckDto.md doc/AssetBulkUploadCheckItem.md doc/AssetBulkUploadCheckResponseDto.md doc/AssetBulkUploadCheckResult.md +doc/AssetDeltaSyncResponseDto.md doc/AssetFaceResponseDto.md doc/AssetFaceUpdateDto.md doc/AssetFaceUpdateItem.md @@ -149,6 +150,7 @@ doc/SharedLinkType.md doc/SignUpDto.md doc/SmartInfoResponseDto.md doc/SmartSearchDto.md +doc/SyncApi.md doc/SystemConfigApi.md doc/SystemConfigDto.md doc/SystemConfigFFmpegDto.md @@ -218,6 +220,7 @@ lib/api/person_api.dart lib/api/search_api.dart lib/api/server_info_api.dart lib/api/shared_link_api.dart +lib/api/sync_api.dart lib/api/system_config_api.dart lib/api/tag_api.dart lib/api/timeline_api.dart @@ -248,6 +251,7 @@ lib/model/asset_bulk_upload_check_dto.dart lib/model/asset_bulk_upload_check_item.dart lib/model/asset_bulk_upload_check_response_dto.dart lib/model/asset_bulk_upload_check_result.dart +lib/model/asset_delta_sync_response_dto.dart lib/model/asset_face_response_dto.dart lib/model/asset_face_update_dto.dart lib/model/asset_face_update_item.dart @@ -427,6 +431,7 @@ test/asset_bulk_upload_check_dto_test.dart test/asset_bulk_upload_check_item_test.dart test/asset_bulk_upload_check_response_dto_test.dart test/asset_bulk_upload_check_result_test.dart +test/asset_delta_sync_response_dto_test.dart test/asset_face_response_dto_test.dart test/asset_face_update_dto_test.dart test/asset_face_update_item_test.dart @@ -552,6 +557,7 @@ test/shared_link_type_test.dart test/sign_up_dto_test.dart test/smart_info_response_dto_test.dart test/smart_search_dto_test.dart +test/sync_api_test.dart test/system_config_api_test.dart test/system_config_dto_test.dart test/system_config_f_fmpeg_dto_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 9a03fbd61c..730307b9bf 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -191,6 +191,8 @@ Class | Method | HTTP request | Description *SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} | *SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets | *SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} | +*SyncApi* | [**getAllForUserFullSync**](doc//SyncApi.md#getallforuserfullsync) | **GET** /sync/full-sync | +*SyncApi* | [**getDeltaSync**](doc//SyncApi.md#getdeltasync) | **GET** /sync/delta-sync | *SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config | *SystemConfigApi* | [**getConfigDefaults**](doc//SystemConfigApi.md#getconfigdefaults) | **GET** /system-config/defaults | *SystemConfigApi* | [**getMapStyle**](doc//SystemConfigApi.md#getmapstyle) | **GET** /system-config/map/style.json | @@ -240,6 +242,7 @@ Class | Method | HTTP request | Description - [AssetBulkUploadCheckItem](doc//AssetBulkUploadCheckItem.md) - [AssetBulkUploadCheckResponseDto](doc//AssetBulkUploadCheckResponseDto.md) - [AssetBulkUploadCheckResult](doc//AssetBulkUploadCheckResult.md) + - [AssetDeltaSyncResponseDto](doc//AssetDeltaSyncResponseDto.md) - [AssetFaceResponseDto](doc//AssetFaceResponseDto.md) - [AssetFaceUpdateDto](doc//AssetFaceUpdateDto.md) - [AssetFaceUpdateItem](doc//AssetFaceUpdateItem.md) diff --git a/mobile/openapi/doc/AssetDeltaSyncResponseDto.md b/mobile/openapi/doc/AssetDeltaSyncResponseDto.md new file mode 100644 index 0000000000..527203a4b8 --- /dev/null +++ b/mobile/openapi/doc/AssetDeltaSyncResponseDto.md @@ -0,0 +1,17 @@ +# openapi.model.AssetDeltaSyncResponseDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**deleted** | **List** | | [default to const []] +**needsFullSync** | **bool** | | +**upserted** | [**List**](AssetResponseDto.md) | | [default to const []] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/mobile/openapi/doc/CreateLibraryDto.md b/mobile/openapi/doc/CreateLibraryDto.md index 94e96493ec..01a2a0f917 100644 --- a/mobile/openapi/doc/CreateLibraryDto.md +++ b/mobile/openapi/doc/CreateLibraryDto.md @@ -11,7 +11,6 @@ Name | Type | Description | Notes **exclusionPatterns** | **List** | | [optional] [default to const []] **importPaths** | **List** | | [optional] [default to const []] **isVisible** | **bool** | | [optional] -**isWatched** | **bool** | | [optional] **name** | **String** | | [optional] **ownerId** | **String** | | **type** | [**LibraryType**](LibraryType.md) | | diff --git a/mobile/openapi/doc/SyncApi.md b/mobile/openapi/doc/SyncApi.md new file mode 100644 index 0000000000..1b28e10c8c --- /dev/null +++ b/mobile/openapi/doc/SyncApi.md @@ -0,0 +1,135 @@ +# openapi.api.SyncApi + +## Load the API package +```dart +import 'package:openapi/api.dart'; +``` + +All URIs are relative to */api* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**getAllForUserFullSync**](SyncApi.md#getallforuserfullsync) | **GET** /sync/full-sync | +[**getDeltaSync**](SyncApi.md#getdeltasync) | **GET** /sync/delta-sync | + + +# **getAllForUserFullSync** +> List getAllForUserFullSync(limit, updatedUntil, lastCreationDate, lastId, userId) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure API key authorization: cookie +//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer'; +// TODO Configure API key authorization: api_key +//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = SyncApi(); +final limit = 56; // int | +final updatedUntil = 2013-10-20T19:20:30+01:00; // DateTime | +final lastCreationDate = 2013-10-20T19:20:30+01:00; // DateTime | +final lastId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | +final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String | + +try { + final result = api_instance.getAllForUserFullSync(limit, updatedUntil, lastCreationDate, lastId, userId); + print(result); +} catch (e) { + print('Exception when calling SyncApi->getAllForUserFullSync: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **limit** | **int**| | + **updatedUntil** | **DateTime**| | + **lastCreationDate** | **DateTime**| | [optional] + **lastId** | **String**| | [optional] + **userId** | **String**| | [optional] + +### Return type + +[**List**](AssetResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getDeltaSync** +> AssetDeltaSyncResponseDto getDeltaSync(updatedAfter, userIds) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure API key authorization: cookie +//defaultApiClient.getAuthentication('cookie').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('cookie').apiKeyPrefix = 'Bearer'; +// TODO Configure API key authorization: api_key +//defaultApiClient.getAuthentication('api_key').apiKey = 'YOUR_API_KEY'; +// uncomment below to setup prefix (e.g. Bearer) for API key, if needed +//defaultApiClient.getAuthentication('api_key').apiKeyPrefix = 'Bearer'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = SyncApi(); +final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | +final userIds = []; // List | + +try { + final result = api_instance.getDeltaSync(updatedAfter, userIds); + print(result); +} catch (e) { + print('Exception when calling SyncApi->getDeltaSync: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **updatedAfter** | **DateTime**| | + **userIds** | [**List**](String.md)| | [default to const []] + +### Return type + +[**AssetDeltaSyncResponseDto**](AssetDeltaSyncResponseDto.md) + +### Authorization + +[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index ae458f5de1..e7320d5bb2 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -46,6 +46,7 @@ part 'api/person_api.dart'; part 'api/search_api.dart'; part 'api/server_info_api.dart'; part 'api/shared_link_api.dart'; +part 'api/sync_api.dart'; part 'api/system_config_api.dart'; part 'api/tag_api.dart'; part 'api/timeline_api.dart'; @@ -69,6 +70,7 @@ part 'model/asset_bulk_upload_check_dto.dart'; part 'model/asset_bulk_upload_check_item.dart'; part 'model/asset_bulk_upload_check_response_dto.dart'; part 'model/asset_bulk_upload_check_result.dart'; +part 'model/asset_delta_sync_response_dto.dart'; part 'model/asset_face_response_dto.dart'; part 'model/asset_face_update_dto.dart'; part 'model/asset_face_update_item.dart'; diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart new file mode 100644 index 0000000000..fdfd8b9ac7 --- /dev/null +++ b/mobile/openapi/lib/api/sync_api.dart @@ -0,0 +1,150 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class SyncApi { + SyncApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient; + + final ApiClient apiClient; + + /// Performs an HTTP 'GET /sync/full-sync' operation and returns the [Response]. + /// Parameters: + /// + /// * [int] limit (required): + /// + /// * [DateTime] updatedUntil (required): + /// + /// * [DateTime] lastCreationDate: + /// + /// * [String] lastId: + /// + /// * [String] userId: + Future getAllForUserFullSyncWithHttpInfo(int limit, DateTime updatedUntil, { DateTime? lastCreationDate, String? lastId, String? userId, }) async { + // ignore: prefer_const_declarations + final path = r'/sync/full-sync'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (lastCreationDate != null) { + queryParams.addAll(_queryParams('', 'lastCreationDate', lastCreationDate)); + } + if (lastId != null) { + queryParams.addAll(_queryParams('', 'lastId', lastId)); + } + queryParams.addAll(_queryParams('', 'limit', limit)); + queryParams.addAll(_queryParams('', 'updatedUntil', updatedUntil)); + if (userId != null) { + queryParams.addAll(_queryParams('', 'userId', userId)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + path, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [int] limit (required): + /// + /// * [DateTime] updatedUntil (required): + /// + /// * [DateTime] lastCreationDate: + /// + /// * [String] lastId: + /// + /// * [String] userId: + Future?> getAllForUserFullSync(int limit, DateTime updatedUntil, { DateTime? lastCreationDate, String? lastId, String? userId, }) async { + final response = await getAllForUserFullSyncWithHttpInfo(limit, updatedUntil, lastCreationDate: lastCreationDate, lastId: lastId, userId: userId, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// Performs an HTTP 'GET /sync/delta-sync' operation and returns the [Response]. + /// Parameters: + /// + /// * [DateTime] updatedAfter (required): + /// + /// * [List] userIds (required): + Future getDeltaSyncWithHttpInfo(DateTime updatedAfter, List userIds,) async { + // ignore: prefer_const_declarations + final path = r'/sync/delta-sync'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + queryParams.addAll(_queryParams('multi', 'userIds', userIds)); + + const contentTypes = []; + + + return apiClient.invokeAPI( + path, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [DateTime] updatedAfter (required): + /// + /// * [List] userIds (required): + Future getDeltaSync(DateTime updatedAfter, List userIds,) async { + final response = await getDeltaSyncWithHttpInfo(updatedAfter, userIds,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetDeltaSyncResponseDto',) as AssetDeltaSyncResponseDto; + + } + return null; + } +} diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 757f475683..4bbae89285 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -216,6 +216,8 @@ class ApiClient { return AssetBulkUploadCheckResponseDto.fromJson(value); case 'AssetBulkUploadCheckResult': return AssetBulkUploadCheckResult.fromJson(value); + case 'AssetDeltaSyncResponseDto': + return AssetDeltaSyncResponseDto.fromJson(value); case 'AssetFaceResponseDto': return AssetFaceResponseDto.fromJson(value); case 'AssetFaceUpdateDto': diff --git a/mobile/openapi/lib/model/asset_delta_sync_response_dto.dart b/mobile/openapi/lib/model/asset_delta_sync_response_dto.dart new file mode 100644 index 0000000000..5d7679e734 --- /dev/null +++ b/mobile/openapi/lib/model/asset_delta_sync_response_dto.dart @@ -0,0 +1,116 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class AssetDeltaSyncResponseDto { + /// Returns a new [AssetDeltaSyncResponseDto] instance. + AssetDeltaSyncResponseDto({ + this.deleted = const [], + required this.needsFullSync, + this.upserted = const [], + }); + + List deleted; + + bool needsFullSync; + + List upserted; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetDeltaSyncResponseDto && + _deepEquality.equals(other.deleted, deleted) && + other.needsFullSync == needsFullSync && + _deepEquality.equals(other.upserted, upserted); + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (deleted.hashCode) + + (needsFullSync.hashCode) + + (upserted.hashCode); + + @override + String toString() => 'AssetDeltaSyncResponseDto[deleted=$deleted, needsFullSync=$needsFullSync, upserted=$upserted]'; + + Map toJson() { + final json = {}; + json[r'deleted'] = this.deleted; + json[r'needsFullSync'] = this.needsFullSync; + json[r'upserted'] = this.upserted; + return json; + } + + /// Returns a new [AssetDeltaSyncResponseDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static AssetDeltaSyncResponseDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return AssetDeltaSyncResponseDto( + deleted: json[r'deleted'] is Iterable + ? (json[r'deleted'] as Iterable).cast().toList(growable: false) + : const [], + needsFullSync: mapValueOfType(json, r'needsFullSync')!, + upserted: AssetResponseDto.listFromJson(json[r'upserted']), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = AssetDeltaSyncResponseDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = AssetDeltaSyncResponseDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of AssetDeltaSyncResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = AssetDeltaSyncResponseDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'deleted', + 'needsFullSync', + 'upserted', + }; +} + diff --git a/mobile/openapi/lib/model/create_library_dto.dart b/mobile/openapi/lib/model/create_library_dto.dart index 24cc045300..93fb89b701 100644 --- a/mobile/openapi/lib/model/create_library_dto.dart +++ b/mobile/openapi/lib/model/create_library_dto.dart @@ -16,7 +16,6 @@ class CreateLibraryDto { this.exclusionPatterns = const [], this.importPaths = const [], this.isVisible, - this.isWatched, this.name, required this.ownerId, required this.type, @@ -34,14 +33,6 @@ class CreateLibraryDto { /// bool? isVisible; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// - bool? isWatched; - /// /// Please note: This property should have been non-nullable! Since the specification file /// does not include a default value (using the "default:" property), however, the generated @@ -59,7 +50,6 @@ class CreateLibraryDto { _deepEquality.equals(other.exclusionPatterns, exclusionPatterns) && _deepEquality.equals(other.importPaths, importPaths) && other.isVisible == isVisible && - other.isWatched == isWatched && other.name == name && other.ownerId == ownerId && other.type == type; @@ -70,13 +60,12 @@ class CreateLibraryDto { (exclusionPatterns.hashCode) + (importPaths.hashCode) + (isVisible == null ? 0 : isVisible!.hashCode) + - (isWatched == null ? 0 : isWatched!.hashCode) + (name == null ? 0 : name!.hashCode) + (ownerId.hashCode) + (type.hashCode); @override - String toString() => 'CreateLibraryDto[exclusionPatterns=$exclusionPatterns, importPaths=$importPaths, isVisible=$isVisible, isWatched=$isWatched, name=$name, ownerId=$ownerId, type=$type]'; + String toString() => 'CreateLibraryDto[exclusionPatterns=$exclusionPatterns, importPaths=$importPaths, isVisible=$isVisible, name=$name, ownerId=$ownerId, type=$type]'; Map toJson() { final json = {}; @@ -87,11 +76,6 @@ class CreateLibraryDto { } else { // json[r'isVisible'] = null; } - if (this.isWatched != null) { - json[r'isWatched'] = this.isWatched; - } else { - // json[r'isWatched'] = null; - } if (this.name != null) { json[r'name'] = this.name; } else { @@ -117,7 +101,6 @@ class CreateLibraryDto { ? (json[r'importPaths'] as Iterable).cast().toList(growable: false) : const [], isVisible: mapValueOfType(json, r'isVisible'), - isWatched: mapValueOfType(json, r'isWatched'), name: mapValueOfType(json, r'name'), ownerId: mapValueOfType(json, r'ownerId')!, type: LibraryType.fromJson(json[r'type'])!, diff --git a/mobile/openapi/lib/model/video_codec.dart b/mobile/openapi/lib/model/video_codec.dart index 2cc18d1ae0..36e1c681a6 100644 --- a/mobile/openapi/lib/model/video_codec.dart +++ b/mobile/openapi/lib/model/video_codec.dart @@ -26,12 +26,14 @@ class VideoCodec { static const h264 = VideoCodec._(r'h264'); static const hevc = VideoCodec._(r'hevc'); static const vp9 = VideoCodec._(r'vp9'); + static const av1 = VideoCodec._(r'av1'); /// List of all possible values in this [enum][VideoCodec]. static const values = [ h264, hevc, vp9, + av1, ]; static VideoCodec? fromJson(dynamic value) => VideoCodecTypeTransformer().decode(value); @@ -73,6 +75,7 @@ class VideoCodecTypeTransformer { case r'h264': return VideoCodec.h264; case r'hevc': return VideoCodec.hevc; case r'vp9': return VideoCodec.vp9; + case r'av1': return VideoCodec.av1; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/test/asset_delta_sync_response_dto_test.dart b/mobile/openapi/test/asset_delta_sync_response_dto_test.dart new file mode 100644 index 0000000000..20104c08c6 --- /dev/null +++ b/mobile/openapi/test/asset_delta_sync_response_dto_test.dart @@ -0,0 +1,37 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + +// tests for AssetDeltaSyncResponseDto +void main() { + // final instance = AssetDeltaSyncResponseDto(); + + group('test AssetDeltaSyncResponseDto', () { + // List deleted (default value: const []) + test('to test the property `deleted`', () async { + // TODO + }); + + // bool needsFullSync + test('to test the property `needsFullSync`', () async { + // TODO + }); + + // List upserted (default value: const []) + test('to test the property `upserted`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/create_library_dto_test.dart b/mobile/openapi/test/create_library_dto_test.dart index 88911249e7..1dd77af251 100644 --- a/mobile/openapi/test/create_library_dto_test.dart +++ b/mobile/openapi/test/create_library_dto_test.dart @@ -31,11 +31,6 @@ void main() { // TODO }); - // bool isWatched - test('to test the property `isWatched`', () async { - // TODO - }); - // String name test('to test the property `name`', () async { // TODO diff --git a/mobile/openapi/test/sync_api_test.dart b/mobile/openapi/test/sync_api_test.dart new file mode 100644 index 0000000000..ad9ef0f92f --- /dev/null +++ b/mobile/openapi/test/sync_api_test.dart @@ -0,0 +1,31 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.12 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +import 'package:openapi/api.dart'; +import 'package:test/test.dart'; + + +/// tests for SyncApi +void main() { + // final instance = SyncApi(); + + group('tests for SyncApi', () { + //Future> getAllForUserFullSync(int limit, DateTime updatedUntil, { DateTime lastCreationDate, String lastId, String userId }) async + test('test getAllForUserFullSync', () async { + // TODO + }); + + //Future getDeltaSync(DateTime updatedAfter, List userIds) async + test('test getDeltaSync', () async { + // TODO + }); + + }); +} diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 6f0f03b573..b0dd2f6b61 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,8 @@ dependencies: sdk: flutter path_provider_ios: - # TODO: change to stable after 3.16 support + # TODO: upgrade to stable after 3.0.1 is released. 3.0.0 is broken + # https://github.com/fluttercandies/flutter_photo_manager/pull/990#issuecomment-2058066427 photo_manager: ^3.0.0-dev.5 photo_manager_image_provider: ^2.1.0 flutter_hooks: ^0.20.4 diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 38df22f00e..8f53f838b0 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -5566,6 +5566,140 @@ ] } }, + "/sync/delta-sync": { + "get": { + "operationId": "getDeltaSync", + "parameters": [ + { + "name": "updatedAfter", + "required": true, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "userIds", + "required": true, + "in": "query", + "schema": { + "format": "uuid", + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssetDeltaSyncResponseDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Sync" + ] + } + }, + "/sync/full-sync": { + "get": { + "operationId": "getAllForUserFullSync", + "parameters": [ + { + "name": "lastCreationDate", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "lastId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "limit", + "required": true, + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "updatedUntil", + "required": true, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "userId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Sync" + ] + } + }, "/system-config": { "get": { "operationId": "getConfig", @@ -7335,6 +7469,31 @@ ], "type": "object" }, + "AssetDeltaSyncResponseDto": { + "properties": { + "deleted": { + "items": { + "type": "string" + }, + "type": "array" + }, + "needsFullSync": { + "type": "boolean" + }, + "upserted": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + }, + "required": [ + "deleted", + "needsFullSync", + "upserted" + ], + "type": "object" + }, "AssetFaceResponseDto": { "properties": { "boundingBoxX1": { @@ -8000,9 +8159,6 @@ "isVisible": { "type": "boolean" }, - "isWatched": { - "type": "boolean" - }, "name": { "type": "string" }, @@ -11234,7 +11390,8 @@ "enum": [ "h264", "hevc", - "vp9" + "vp9", + "av1" ], "type": "string" } diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index f4c92e52d7..8def6adffd 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -22,18 +22,18 @@ "integrity": "sha512-V33FjR6V+AkGRWYQW3XPm5BLn2loGl2ujSeja1TzdjjEn2zjGgl3ve0dcFf/jEwPZEOqQZl6YwIgIB/clXVqWw==" }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 999fa23fa6..96b071f1f9 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -461,7 +461,6 @@ export type CreateLibraryDto = { exclusionPatterns?: string[]; importPaths?: string[]; isVisible?: boolean; - isWatched?: boolean; name?: string; ownerId: string; "type": LibraryType; @@ -836,6 +835,11 @@ export type AssetIdsResponseDto = { error?: Error2; success: boolean; }; +export type AssetDeltaSyncResponseDto = { + deleted: string[]; + needsFullSync: boolean; + upserted: AssetResponseDto[]; +}; export type SystemConfigFFmpegDto = { accel: TranscodeHWAccel; acceptedAudioCodecs: AudioCodec[]; @@ -2508,6 +2512,40 @@ export function addSharedLinkAssets({ id, key, assetIdsDto }: { body: assetIdsDto }))); } +export function getDeltaSync({ updatedAfter, userIds }: { + updatedAfter: string; + userIds: string[]; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetDeltaSyncResponseDto; + }>(`/sync/delta-sync${QS.query(QS.explode({ + updatedAfter, + userIds + }))}`, { + ...opts + })); +} +export function getAllForUserFullSync({ lastCreationDate, lastId, limit, updatedUntil, userId }: { + lastCreationDate?: string; + lastId?: string; + limit: number; + updatedUntil: string; + userId?: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetResponseDto[]; + }>(`/sync/full-sync${QS.query(QS.explode({ + lastCreationDate, + lastId, + limit, + updatedUntil, + userId + }))}`, { + ...opts + })); +} export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2985,7 +3023,8 @@ export enum AudioCodec { export enum VideoCodec { H264 = "h264", Hevc = "hevc", - Vp9 = "vp9" + Vp9 = "vp9", + Av1 = "av1" } export enum CQMode { Auto = "auto", diff --git a/server/.eslintrc.js b/server/.eslintrc.js index 75138ff235..79c48c4015 100644 --- a/server/.eslintrc.js +++ b/server/.eslintrc.js @@ -10,7 +10,6 @@ module.exports = { root: true, env: { node: true, - jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { @@ -25,6 +24,7 @@ module.exports = { 'unicorn/prefer-top-level-await': 'off', 'unicorn/prefer-event-target': 'off', 'unicorn/no-thenable': 'off', + 'unicorn/import-style': 'off', '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': 'error', diff --git a/server/Dockerfile b/server/Dockerfile index 19f6c76eba..8ed5344395 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,11 +1,11 @@ # dev build -FROM ghcr.io/immich-app/base-server-dev:20240326@sha256:d945aba864051b30888617f36446f86b72c4bc7ad6476b9dd2aaa0c4c4e3c945 as dev +FROM ghcr.io/immich-app/base-server-dev:20240416@sha256:ff2aadf54298e8ceca94031c6fed143236d8d82640fbbf422e0a9d2978e45923 as dev RUN apt-get install --no-install-recommends -yqq tini WORKDIR /usr/src/app COPY server/package.json server/package-lock.json ./ RUN npm ci && \ - # sharp-linux-x64 and sharp-linux-arm64 are the only ones we need + # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need # they're marked as optional dependencies, so we need to copy them manually after pruning rm -rf node_modules/@img/sharp-libvips* && \ rm -rf node_modules/@img/sharp-linuxmusl-x64 @@ -22,9 +22,10 @@ FROM dev AS prod RUN npm run build RUN npm prune --omit=dev --omit=optional COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img +COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build -FROM node:iron-alpine3.18@sha256:fa5d3cf51725bd42d32e67917623038539dbe720dab082f590785c001eb4dfef as web +FROM node:iron-alpine3.18@sha256:3fb85a68652064ab109ed9730f45a3ede11f064afdd3ad9f96ef7e8a3c55f47e as web WORKDIR /usr/src/open-api/typescript-sdk COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ @@ -40,7 +41,7 @@ RUN npm run build # prod build -FROM ghcr.io/immich-app/base-server-prod:20240326@sha256:28ad98fed8d746b5f92de49ff776cfdff7399df163ebeda2f37a01f473965841 +FROM ghcr.io/immich-app/base-server-prod:20240416@sha256:138f4d6fb74b282256583070339eaba6f39fcffa3569ae05b6823d5c37098242 WORKDIR /usr/src/app ENV NODE_ENV=production \ diff --git a/server/bin/immich-test b/server/bin/immich-test deleted file mode 100755 index 93b104f136..0000000000 --- a/server/bin/immich-test +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -node /usr/src/app/node_modules/.bin/jest --config e2e/"$1"/jest-e2e.json --runInBand diff --git a/server/e2e/client/asset-api.ts b/server/e2e/client/asset-api.ts deleted file mode 100644 index f32d586115..0000000000 --- a/server/e2e/client/asset-api.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import request from 'supertest'; - -export const assetApi = { - getAllAssets: async (server: any, accessToken: string) => { - const { body, status } = await request(server).get(`/asset/`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - return body as AssetResponseDto[]; - }, -}; diff --git a/server/e2e/client/auth-api.ts b/server/e2e/client/auth-api.ts deleted file mode 100644 index a8cfe4660a..0000000000 --- a/server/e2e/client/auth-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { LoginResponseDto } from 'src/dtos/auth.dto'; -import { UserResponseDto } from 'src/dtos/user.dto'; -import request from 'supertest'; -import { adminSignupStub, loginResponseStub, loginStub } from 'test/fixtures/auth.stub'; - -export const authApi = { - adminSignUp: async (server: any) => { - const { status, body } = await request(server).post('/auth/admin-sign-up').send(adminSignupStub); - - expect(status).toBe(201); - - return body as UserResponseDto; - }, - adminLogin: async (server: any) => { - const { status, body } = await request(server).post('/auth/login').send(loginStub.admin); - - expect(body).toEqual(loginResponseStub.admin.response); - expect(body).toMatchObject({ accessToken: expect.any(String) }); - expect(status).toBe(201); - - return body as LoginResponseDto; - }, -}; diff --git a/server/e2e/client/index.ts b/server/e2e/client/index.ts deleted file mode 100644 index 41418ddcc0..0000000000 --- a/server/e2e/client/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { assetApi } from 'e2e/client/asset-api'; -import { authApi } from 'e2e/client/auth-api'; -import { libraryApi } from 'e2e/client/library-api'; - -export const api = { - authApi, - assetApi, - libraryApi, -}; diff --git a/server/e2e/client/library-api.ts b/server/e2e/client/library-api.ts deleted file mode 100644 index 70c8c4c360..0000000000 --- a/server/e2e/client/library-api.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CreateLibraryDto, LibraryResponseDto, ScanLibraryDto } from 'src/dtos/library.dto'; -import request from 'supertest'; - -export const libraryApi = { - getAll: async (server: any, accessToken: string) => { - const { body, status } = await request(server).get(`/library/`).set('Authorization', `Bearer ${accessToken}`); - expect(status).toBe(200); - return body as LibraryResponseDto[]; - }, - create: async (server: any, accessToken: string, dto: CreateLibraryDto) => { - const { body, status } = await request(server) - .post(`/library/`) - .set('Authorization', `Bearer ${accessToken}`) - .send(dto); - expect(status).toBe(201); - return body as LibraryResponseDto; - }, - setImportPaths: async (server: any, accessToken: string, id: string, importPaths: string[]) => { - const { body, status } = await request(server) - .put(`/library/${id}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ importPaths }); - expect(status).toBe(200); - return body as LibraryResponseDto; - }, - scanLibrary: async (server: any, accessToken: string, id: string, dto: ScanLibraryDto = {}) => { - const { status } = await request(server) - .post(`/library/${id}/scan`) - .set('Authorization', `Bearer ${accessToken}`) - .send(dto); - expect(status).toBe(204); - }, -}; diff --git a/server/e2e/docker-compose.server-e2e.yml b/server/e2e/docker-compose.server-e2e.yml deleted file mode 100644 index 61b38e1ee0..0000000000 --- a/server/e2e/docker-compose.server-e2e.yml +++ /dev/null @@ -1,33 +0,0 @@ -version: '3.8' - -name: 'immich-test-e2e' - -services: - immich-server: - image: immich-server-dev:latest - build: - context: ../../ - dockerfile: server/Dockerfile - target: dev - command: ['/usr/src/app/bin/immich-test', 'jobs'] - volumes: - - /usr/src/app/node_modules - - ../test/assets:/usr/src/app/test/assets:ro - environment: - - DB_HOSTNAME=database - - DB_USERNAME=postgres - - DB_PASSWORD=postgres - - DB_DATABASE_NAME=e2e_test - - IMMICH_METRICS=true - depends_on: - - database - - database: - image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 - command: -c fsync=off -c shared_preload_libraries=vectors.so - environment: - POSTGRES_PASSWORD: postgres - POSTGRES_USER: postgres - POSTGRES_DB: e2e_test - logging: - driver: none diff --git a/server/e2e/jobs/config/library-watcher-e2e-config.json b/server/e2e/jobs/config/library-watcher-e2e-config.json deleted file mode 100644 index 9f7420ca5a..0000000000 --- a/server/e2e/jobs/config/library-watcher-e2e-config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "reverseGeocoding": { - "enabled": false - }, - "machineLearning": { - "enabled": false - }, - "logging": { - "enabled": false, - "level": "debug" - }, - "library": { - "watch": { - "enabled": true - } - } -} diff --git a/server/e2e/jobs/immich-e2e-config.json b/server/e2e/jobs/immich-e2e-config.json deleted file mode 100644 index 4f018dc164..0000000000 --- a/server/e2e/jobs/immich-e2e-config.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "reverseGeocoding": { - "enabled": false - }, - "machineLearning": { - "enabled": false - }, - "logging": { - "enabled": false, - "level": "debug" - } -} diff --git a/server/e2e/jobs/jest-e2e.json b/server/e2e/jobs/jest-e2e.json deleted file mode 100644 index b7e62d5f46..0000000000 --- a/server/e2e/jobs/jest-e2e.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "modulePaths": [""], - "rootDir": "../..", - "globalSetup": "/e2e/jobs/setup.ts", - "testEnvironment": "node", - "testMatch": ["**/e2e/jobs/specs/*.e2e-spec.[tj]s"], - "testTimeout": 10000, - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "/src/**/*.(t|j)s", - "!/src/**/*.spec.(t|s)s", - "!/src/migrations/**" - ], - "coverageDirectory": "./coverage", - "moduleNameMapper": { - "^test(|/.*)$": "/test/$1", - "^src(|/.*)$": "/src/$1" - } -} diff --git a/server/e2e/jobs/setup.ts b/server/e2e/jobs/setup.ts deleted file mode 100644 index d1f566d372..0000000000 --- a/server/e2e/jobs/setup.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PostgreSqlContainer } from '@testcontainers/postgresql'; -import { access } from 'fs/promises'; -import path from 'path'; - -export default async () => { - let IMMICH_TEST_ASSET_PATH: string = ''; - - if (process.env.IMMICH_TEST_ASSET_PATH === undefined) { - IMMICH_TEST_ASSET_PATH = path.normalize(`${__dirname}/../../test/assets/`); - process.env.IMMICH_TEST_ASSET_PATH = IMMICH_TEST_ASSET_PATH; - } else { - IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH; - } - - const directoryExists = async (dirPath: string) => - await access(dirPath) - .then(() => true) - .catch(() => false); - - if (!(await directoryExists(`${IMMICH_TEST_ASSET_PATH}/albums`))) { - throw new Error( - `Test assets not found. Please checkout https://github.com/immich-app/test-assets into ${IMMICH_TEST_ASSET_PATH} before testing`, - ); - } - - if (process.env.DB_HOSTNAME === undefined) { - // DB hostname not set which likely means we're not running e2e through docker compose. Start a local postgres container. - const pg = await new PostgreSqlContainer('tensorchord/pgvecto-rs:pg14-v0.2.0') - .withExposedPorts(5432) - .withDatabase('immich') - .withUsername('postgres') - .withPassword('postgres') - .withReuse() - .start(); - - process.env.DB_URL = pg.getConnectionUri(); - } - - process.env.NODE_ENV = 'development'; - process.env.IMMICH_CONFIG_FILE = path.normalize(`${__dirname}/immich-e2e-config.json`); - process.env.TZ = 'Z'; -}; diff --git a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts b/server/e2e/jobs/specs/library-watcher.e2e-spec.ts deleted file mode 100644 index 20a9d32020..0000000000 --- a/server/e2e/jobs/specs/library-watcher.e2e-spec.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { api } from 'e2e/client'; -import fs from 'node:fs/promises'; -import path from 'node:path'; -import { LoginResponseDto } from 'src/dtos/auth.dto'; -import { LibraryResponseDto } from 'src/dtos/library.dto'; -import { AssetType } from 'src/entities/asset.entity'; -import { LibraryType } from 'src/entities/library.entity'; -import { StorageEventType } from 'src/interfaces/storage.interface'; -import { LibraryService } from 'src/services/library.service'; -import { - IMMICH_TEST_ASSET_PATH, - IMMICH_TEST_ASSET_TEMP_PATH, - restoreTempFolder, - testApp, - waitForEvent, -} from 'test/utils'; - -describe(`Library watcher (e2e)`, () => { - let server: any; - let admin: LoginResponseDto; - let libraryService: LibraryService; - const configFilePath = process.env.IMMICH_CONFIG_FILE; - - beforeAll(async () => { - process.env.IMMICH_CONFIG_FILE = path.normalize(`${__dirname}/../config/library-watcher-e2e-config.json`); - - const app = await testApp.create(); - server = app.getHttpServer(); - libraryService = testApp.get(LibraryService); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - }); - - afterEach(async () => { - await libraryService.teardown(); - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - process.env.IMMICH_CONFIG_FILE = configFilePath; - }); - - describe('Event handling', () => { - describe('Single import path', () => { - beforeEach(async () => { - await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - }); - - it('should import a new file', async () => { - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD); - - const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(afterAssets.length).toEqual(1); - }); - - it('should import new files with case insensitive extensions', async () => { - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file2.JPG`, - ); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file3.Jpg`, - ); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file4.jpG`, - ); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file5.jPg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD, 4); - - const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(afterAssets.length).toEqual(4); - }); - - it('should update a changed file', async () => { - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD); - - const originalAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(originalAssets.length).toEqual(1); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/prairie_falcon.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/file.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.CHANGE); - - const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(afterAssets).toEqual([ - expect.objectContaining({ - // Make sure we keep the original asset id - id: originalAssets[0].id, - type: AssetType.IMAGE, - exifInfo: expect.objectContaining({ - make: 'Canon', - model: 'Canon EOS R5', - exifImageWidth: 800, - exifImageHeight: 533, - exposureTime: '1/4000', - }), - }), - ]); - }); - }); - - describe('Multiple import paths', () => { - beforeEach(async () => { - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1`, { recursive: true }); - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir2`, { recursive: true }); - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, { recursive: true }); - - await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [ - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir1`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir2`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, - ], - }); - }); - - it('should add new files in multiple import paths', async () => { - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir1/file2.jpg`, - ); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/polemonium_reptans.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir2/file3.jpg`, - ); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/tanners_ridge.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir3/file4.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD, 3); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assets.length).toEqual(3); - }); - - it('should offline a removed file', async () => { - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/polemonium_reptans.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir1/file.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD); - - const addedAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(addedAssets.length).toEqual(1); - - await fs.unlink(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1/file.jpg`); - - await waitForEvent(libraryService, StorageEventType.UNLINK); - - const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(afterAssets[0].isOffline).toEqual(true); - }); - }); - }); - - describe('Configuration', () => { - let library: LibraryResponseDto; - - beforeEach(async () => { - library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [ - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir1`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir2`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, - ], - }); - - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir1`, { recursive: true }); - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir2`, { recursive: true }); - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir3`, { recursive: true }); - }); - - it('should use an updated import path', async () => { - await fs.mkdir(`${IMMICH_TEST_ASSET_TEMP_PATH}/dir4`, { recursive: true }); - - await api.libraryApi.setImportPaths(server, admin.accessToken, library.id, [ - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir4`, - ]); - - await fs.copyFile( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/polemonium_reptans.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/dir4/file.jpg`, - ); - - await waitForEvent(libraryService, StorageEventType.ADD); - - const afterAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(afterAssets.length).toEqual(1); - }); - }); -}); diff --git a/server/e2e/jobs/specs/library.e2e-spec.ts b/server/e2e/jobs/specs/library.e2e-spec.ts deleted file mode 100644 index 3ae27e631e..0000000000 --- a/server/e2e/jobs/specs/library.e2e-spec.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { api } from 'e2e/client'; -import fs from 'node:fs'; -import { LibraryController } from 'src/controllers/library.controller'; -import { LoginResponseDto } from 'src/dtos/auth.dto'; -import { LibraryType } from 'src/entities/library.entity'; -import request from 'supertest'; -import { errorStub } from 'test/fixtures/error.stub'; -import { uuidStub } from 'test/fixtures/uuid.stub'; -import { IMMICH_TEST_ASSET_PATH, IMMICH_TEST_ASSET_TEMP_PATH, restoreTempFolder, testApp } from 'test/utils'; -import { utimes } from 'utimes'; - -describe(`${LibraryController.name} (e2e)`, () => { - let server: any; - let admin: LoginResponseDto; - - beforeAll(async () => { - const app = await testApp.create(); - server = app.getHttpServer(); - }); - - beforeEach(async () => { - await testApp.reset(); - await restoreTempFolder(); - await api.authApi.adminSignUp(server); - admin = await api.authApi.adminLogin(server); - }); - - afterAll(async () => { - await testApp.teardown(); - await restoreTempFolder(); - }); - - describe('POST /library/:id/scan', () => { - it('should offline missing files', async () => { - await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { - recursive: true, - }); - - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(onlineAssets.length).toBeGreaterThan(1); - - await restoreTempFolder(); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - isOffline: true, - originalFileName: 'el_torcal_rocks.jpg', - }), - expect.objectContaining({ - isOffline: true, - originalFileName: 'tanners_ridge.jpg', - }), - ]), - ); - }); - - it('should scan new files', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/silver_fir.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/silver_fir.jpg`, - ); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - originalFileName: 'el_torcal_rocks.jpg', - }), - expect.objectContaining({ - originalFileName: 'silver_fir.jpg', - }), - ]), - ); - }); - - describe('with refreshModifiedFiles=true', () => { - it('should reimport modified files', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_000); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/tanners_ridge.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_001); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id, { refreshModifiedFiles: true }); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assets.length).toBe(1); - - expect(assets[0]).toEqual( - expect.objectContaining({ - originalFileName: 'el_torcal_rocks.jpg', - exifInfo: expect.objectContaining({ - dateTimeOriginal: '2023-09-25T08:33:30.880Z', - exifImageHeight: 534, - exifImageWidth: 800, - exposureTime: '1/15', - fNumber: 22, - fileSizeInByte: 114_225, - focalLength: 35, - iso: 1000, - make: 'NIKON CORPORATION', - model: 'NIKON D750', - }), - }), - ); - }); - - it('should not reimport unmodified files', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_000); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/tanners_ridge.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_000); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id, { refreshModifiedFiles: true }); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assets.length).toBe(1); - - expect(assets[0]).toEqual( - expect.objectContaining({ - originalFileName: 'el_torcal_rocks.jpg', - exifInfo: expect.objectContaining({ - dateTimeOriginal: '2012-08-05T11:39:59.000Z', - }), - }), - ); - }); - }); - - describe('with refreshAllFiles=true', () => { - it('should reimport all files', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/el_torcal_rocks.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_000); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - await fs.promises.cp( - `${IMMICH_TEST_ASSET_PATH}/albums/nature/tanners_ridge.jpg`, - `${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, - ); - - await utimes(`${IMMICH_TEST_ASSET_TEMP_PATH}/el_torcal_rocks.jpg`, 447_775_200_000); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id, { refreshAllFiles: true }); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assets.length).toBe(1); - - expect(assets[0]).toEqual( - expect.objectContaining({ - originalFileName: 'el_torcal_rocks.jpg', - exifInfo: expect.objectContaining({ - exifImageHeight: 534, - exifImageWidth: 800, - exposureTime: '1/15', - fNumber: 22, - fileSizeInByte: 114_225, - focalLength: 35, - iso: 1000, - make: 'NIKON CORPORATION', - model: 'NIKON D750', - }), - }), - ); - }); - }); - }); - - describe('POST /library/:id/removeOffline', () => { - it('should require authentication', async () => { - const { status, body } = await request(server).post(`/library/${uuidStub.notFound}/removeOffline`).send({}); - - expect(status).toBe(401); - expect(body).toEqual(errorStub.unauthorized); - }); - - it('should remove offline files', async () => { - await fs.promises.cp(`${IMMICH_TEST_ASSET_PATH}/albums/nature`, `${IMMICH_TEST_ASSET_TEMP_PATH}/albums/nature`, { - recursive: true, - }); - - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_TEMP_PATH}`], - }); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const onlineAssets = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(onlineAssets.length).toBeGreaterThan(1); - - await restoreTempFolder(); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const { status } = await request(server) - .post(`/library/${library.id}/removeOffline`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send(); - expect(status).toBe(204); - - const assets = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assets).toEqual([]); - }); - - it('should not remove online files', async () => { - const library = await api.libraryApi.create(server, admin.accessToken, { - ownerId: admin.userId, - type: LibraryType.EXTERNAL, - importPaths: [`${IMMICH_TEST_ASSET_PATH}/albums/nature`], - }); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const assetsBefore = await api.assetApi.getAllAssets(server, admin.accessToken); - expect(assetsBefore.length).toBeGreaterThan(1); - - await api.libraryApi.scanLibrary(server, admin.accessToken, library.id); - - const { status } = await request(server) - .post(`/library/${library.id}/removeOffline`) - .set('Authorization', `Bearer ${admin.accessToken}`) - .send(); - expect(status).toBe(204); - - const assetsAfter = await api.assetApi.getAllAssets(server, admin.accessToken); - - expect(assetsAfter).toEqual(assetsBefore); - }); - }); -}); diff --git a/server/package-lock.json b/server/package-lock.json index 085def2389..286f1006b9 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,7 +9,6 @@ "version": "1.101.0", "license": "GNU Affero General Public License version 3", "dependencies": { - "@babel/runtime": "^7.22.11", "@nestjs/bullmq": "^10.0.1", "@nestjs/common": "^10.2.2", "@nestjs/config": "^3.0.0", @@ -21,11 +20,10 @@ "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", - "@opentelemetry/auto-instrumentations-node": "^0.43.0", - "@opentelemetry/exporter-prometheus": "^0.49.0", - "@opentelemetry/sdk-node": "^0.49.0", + "@opentelemetry/auto-instrumentations-node": "^0.44.0", + "@opentelemetry/exporter-prometheus": "^0.50.0", + "@opentelemetry/sdk-node": "^0.50.0", "@socket.io/postgres-adapter": "^0.3.1", - "@types/picomatch": "^2.3.3", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^5.1.1", @@ -35,7 +33,6 @@ "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", "exiftool-vendored": "~24.6.0", - "exiftool-vendored.pl": "12.78", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", @@ -49,11 +46,12 @@ "luxon": "^3.4.2", "mnemonist": "^0.39.8", "nest-commander": "^3.11.1", + "nestjs-cls": "^4.3.0", "nestjs-otel": "^5.1.5", "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", - "reflect-metadata": "^0.1.13", + "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", "sharp": "^0.33.0", @@ -66,6 +64,7 @@ "@nestjs/cli": "^10.1.16", "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.2.2", + "@swc/core": "^1.4.14", "@testcontainers/postgresql": "^10.2.1", "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", @@ -74,38 +73,32 @@ "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", "@types/imagemin": "^8.0.1", - "@types/jest": "29.5.12", - "@types/jest-when": "^3.5.2", "@types/js-yaml": "^4.0.9", "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", "@types/node": "^20.5.7", - "@types/supertest": "^6.0.0", + "@types/picomatch": "^2.3.3", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", + "@vitest/coverage-v8": "^1.5.0", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", - "jest": "^29.6.4", - "jest-when": "^3.6.0", + "eslint-plugin-unicorn": "^52.0.0", "mock-fs": "^5.2.0", "prettier": "^3.0.2", "prettier-plugin-organize-imports": "^3.2.3", "rimraf": "^5.0.1", "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", - "supertest": "^6.3.3", - "testcontainers": "^10.2.1", - "ts-jest": "^29.1.1", - "ts-loader": "^9.4.4", - "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.3.3", - "utimes": "^5.2.1" + "unplugin-swc": "^1.4.5", + "utimes": "^5.2.1", + "vitest": "^1.5.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -315,271 +308,21 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", - "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", - "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -593,37 +336,15 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", - "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -694,9 +415,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", - "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -705,245 +426,13 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", - "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.3", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.3", - "@babel/types": "^7.23.3", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", - "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -977,7 +466,8 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -989,7 +479,8 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1004,6 +495,374 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1020,9 +879,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -1095,9 +954,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.2.tgz", - "integrity": "sha512-lSbgu8iayAod8O0YcoXK3+bMFGThY2svtN35Zlm9VepsB3jfyIcoupKknEht7Kh9Q8ITjsp0J4KpYo9l4+FhNg==", + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz", + "integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==", "dependencies": { "@grpc/proto-loader": "^0.7.10", "@js-sdsl/ordered-map": "^4.4.2" @@ -1107,9 +966,9 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", - "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", + "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -1811,105 +1670,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1919,205 +1679,6 @@ "node": ">=8" } }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -2130,102 +1691,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -2241,9 +1715,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2266,9 +1740,9 @@ "devOptional": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2612,9 +2086,9 @@ } }, "node_modules/@nestjs/config": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.1.tgz", - "integrity": "sha512-tFZyLJKanSAu51ygQ6ZBSpx95pRcwS6qSpJDW6FFgRQzkOaOUXpL8qD8yMNoYoYxuJCxph+waiBaWKgFWxn3sw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.2.tgz", + "integrity": "sha512-vGICPOui5vE6kPz1iwQ7oCnp3qWgqxldPmBQ9onkVoKlBtyc83KJCr7CjuVtf4OdovMAVcux1d8Q6jglU2ZphA==", "dependencies": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", @@ -2921,9 +2395,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.49.1.tgz", - "integrity": "sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.50.0.tgz", + "integrity": "sha512-JdZuKrhOYggqOpUljAq4WWNi5nB10PmgoF0y2CvedLGXd0kSawb/UBnWT8gg1ND3bHCNHStAIVT0ELlxJJRqrA==", "dependencies": { "@opentelemetry/api": "^1.0.0" }, @@ -2932,54 +2406,54 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.43.0.tgz", - "integrity": "sha512-2WvHUSi/QVeVG8ObPD0Ls6WevfIbQjspxIQRuHaQFWXhmEwy/MsEcoQUjbNKXwO5516aS04GTydKEoRKsMwhdA==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.44.0.tgz", + "integrity": "sha512-wd24SiEsxDLbKslvUvyzOesNXhjjxV52Co5parTYXuMixil3SNNgon1WosbQo65jFzuvbASAMZNmc1MjtVp+ng==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/instrumentation-amqplib": "^0.35.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.39.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.39.1", - "@opentelemetry/instrumentation-bunyan": "^0.36.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.36.0", - "@opentelemetry/instrumentation-connect": "^0.34.0", - "@opentelemetry/instrumentation-cucumber": "^0.4.0", - "@opentelemetry/instrumentation-dataloader": "^0.7.0", - "@opentelemetry/instrumentation-dns": "^0.34.0", - "@opentelemetry/instrumentation-express": "^0.36.1", - "@opentelemetry/instrumentation-fastify": "^0.34.0", - "@opentelemetry/instrumentation-fs": "^0.10.0", - "@opentelemetry/instrumentation-generic-pool": "^0.34.0", - "@opentelemetry/instrumentation-graphql": "^0.38.1", - "@opentelemetry/instrumentation-grpc": "^0.49.1", - "@opentelemetry/instrumentation-hapi": "^0.35.0", - "@opentelemetry/instrumentation-http": "^0.49.1", - "@opentelemetry/instrumentation-ioredis": "^0.38.0", - "@opentelemetry/instrumentation-knex": "^0.34.0", - "@opentelemetry/instrumentation-koa": "^0.38.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.35.0", - "@opentelemetry/instrumentation-memcached": "^0.34.0", - "@opentelemetry/instrumentation-mongodb": "^0.41.0", - "@opentelemetry/instrumentation-mongoose": "^0.36.0", - "@opentelemetry/instrumentation-mysql": "^0.36.0", - "@opentelemetry/instrumentation-mysql2": "^0.36.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.35.0", - "@opentelemetry/instrumentation-net": "^0.34.0", - "@opentelemetry/instrumentation-pg": "^0.39.1", - "@opentelemetry/instrumentation-pino": "^0.36.0", - "@opentelemetry/instrumentation-redis": "^0.37.0", - "@opentelemetry/instrumentation-redis-4": "^0.37.0", - "@opentelemetry/instrumentation-restify": "^0.36.0", - "@opentelemetry/instrumentation-router": "^0.35.0", - "@opentelemetry/instrumentation-socket.io": "^0.37.0", - "@opentelemetry/instrumentation-tedious": "^0.8.0", - "@opentelemetry/instrumentation-winston": "^0.35.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.28.7", - "@opentelemetry/resource-detector-aws": "^1.4.0", - "@opentelemetry/resource-detector-container": "^0.3.7", - "@opentelemetry/resource-detector-gcp": "^0.29.7", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/instrumentation-amqplib": "^0.36.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.40.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.40.0", + "@opentelemetry/instrumentation-bunyan": "^0.37.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.37.0", + "@opentelemetry/instrumentation-connect": "^0.35.0", + "@opentelemetry/instrumentation-cucumber": "^0.5.0", + "@opentelemetry/instrumentation-dataloader": "^0.8.0", + "@opentelemetry/instrumentation-dns": "^0.35.0", + "@opentelemetry/instrumentation-express": "^0.37.0", + "@opentelemetry/instrumentation-fastify": "^0.35.0", + "@opentelemetry/instrumentation-fs": "^0.11.0", + "@opentelemetry/instrumentation-generic-pool": "^0.35.0", + "@opentelemetry/instrumentation-graphql": "^0.39.0", + "@opentelemetry/instrumentation-grpc": "^0.50.0", + "@opentelemetry/instrumentation-hapi": "^0.36.0", + "@opentelemetry/instrumentation-http": "^0.50.0", + "@opentelemetry/instrumentation-ioredis": "^0.39.0", + "@opentelemetry/instrumentation-knex": "^0.35.0", + "@opentelemetry/instrumentation-koa": "^0.39.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.36.0", + "@opentelemetry/instrumentation-memcached": "^0.35.0", + "@opentelemetry/instrumentation-mongodb": "^0.42.0", + "@opentelemetry/instrumentation-mongoose": "^0.37.0", + "@opentelemetry/instrumentation-mysql": "^0.37.0", + "@opentelemetry/instrumentation-mysql2": "^0.37.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.36.0", + "@opentelemetry/instrumentation-net": "^0.35.0", + "@opentelemetry/instrumentation-pg": "^0.40.0", + "@opentelemetry/instrumentation-pino": "^0.37.0", + "@opentelemetry/instrumentation-redis": "^0.38.0", + "@opentelemetry/instrumentation-redis-4": "^0.38.0", + "@opentelemetry/instrumentation-restify": "^0.37.0", + "@opentelemetry/instrumentation-router": "^0.36.0", + "@opentelemetry/instrumentation-socket.io": "^0.38.0", + "@opentelemetry/instrumentation-tedious": "^0.9.0", + "@opentelemetry/instrumentation-winston": "^0.36.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.28.8", + "@opentelemetry/resource-detector-aws": "^1.4.1", + "@opentelemetry/resource-detector-container": "^0.3.8", + "@opentelemetry/resource-detector-gcp": "^0.29.8", "@opentelemetry/resources": "^1.12.0", - "@opentelemetry/sdk-node": "^0.49.1" + "@opentelemetry/sdk-node": "^0.50.0" }, "engines": { "node": ">=14" @@ -2989,9 +2463,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.22.0.tgz", - "integrity": "sha512-Nfdxyg8YtWqVWkyrCukkundAjPhUXi93JtVQmqDT1mZRVKqA7e2r7eJCrI+F651XUBMp0hsOJSGiFk3QSpaIJw==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.23.0.tgz", + "integrity": "sha512-wazGJZDRevibOJ+VgyrT+9+8sybZAxpZx2G7vy30OAtk92OpZCg7HgNxT11NUx0VBDWcRx1dOatMYGOVplQ7QA==", "engines": { "node": ">=14" }, @@ -3000,11 +2474,11 @@ } }, "node_modules/@opentelemetry/core": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", - "integrity": "sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.23.0.tgz", + "integrity": "sha512-hdQ/a9TMzMQF/BO8Cz1juA43/L5YGtCSiKoOHmrTEf7VMDAZgy8ucpWx3eQTnQ3gBloRcWtzvcrMZABC3PTSKQ==", "dependencies": { - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -3014,13 +2488,13 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.49.1.tgz", - "integrity": "sha512-FgzGl6OH22f+Wb1dh/TnoQSnZE2SCADhHx06nMqxivSqRJ9t3AhUdMsEOFt2IMjZClE705pcsLHk10BCJ79vsA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.50.0.tgz", + "integrity": "sha512-6jBrGqzpU1b2gCPUWTSSW+G3ejbZRx9SYhhFg0MO6v8R51mcln9KH6oIdTDrA+3Ie3L18bpygKrIWA9VPWEifg==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-metrics": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-metrics": "1.23.0" }, "engines": { "node": ">=14" @@ -3030,16 +2504,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.49.1.tgz", - "integrity": "sha512-Zbd7f3zF7fI2587MVhBizaW21cO/SordyrZGtMtvhoxU6n4Qb02Gx71X4+PzXH620e0+JX+Pcr9bYb1HTeVyJA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.50.0.tgz", + "integrity": "sha512-w/NF4TrwHxx+Uz1M0rCOSVr6KgcoQPv3zF9JRqcebY2euD7ddWnLP0hE8JavyA1uq4UchnMp9faAk9n7hTCePw==", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" }, "engines": { "node": ">=14" @@ -3049,15 +2523,15 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.49.1.tgz", - "integrity": "sha512-KOLtZfZvIrpGZLVvblKsiVQT7gQUZNKcUUH24Zz6Xbi7LJb9Vt6xtUZFYdR5IIjvt47PIqBKDWUQlU0o1wAsRw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.50.0.tgz", + "integrity": "sha512-L7OtIMT7MsFqkmhbQlPBGRXt7152VN5esHpQEJYIBFedOEo3Da+yHpu5ojMZtPzpIvSpB5Xr5lnJUjJCbkttCA==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" }, "engines": { "node": ">=14" @@ -3067,16 +2541,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.49.1.tgz", - "integrity": "sha512-n8ON/c9pdMyYAfSFWKkgsPwjYoxnki+6Olzo+klKfW7KqLWoyEkryNkbcMIYnGGNXwdkMIrjoaP0VxXB26Oxcg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.50.0.tgz", + "integrity": "sha512-vavD9Ow6yOLiD+ocuS/oeciCsXNdsN41aYUrEljNaLXogvnkfMhJ+JLAhOnRSpzlVtRp7Ciw2BYGdYSebR0OsA==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", - "@opentelemetry/otlp-proto-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", + "@opentelemetry/otlp-proto-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" }, "engines": { "node": ">=14" @@ -3086,14 +2560,14 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.22.0.tgz", - "integrity": "sha512-XcFs6rGvcTz0qW5uY7JZDYD0yNEXdekXAb6sFtnZgY/cHY6BQ09HMzOjv9SX+iaXplRDcHr1Gta7VQKM1XXM6g==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.23.0.tgz", + "integrity": "sha512-2LOGvNUGONuIcWhynFaJorVyqv03uZkURScciLmOxvBf2lWTNPEj77br1dCpShIWBM+YlrH7Tc+JXAs+GC7DqA==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -3118,11 +2592,11 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.49.1.tgz", - "integrity": "sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.50.0.tgz", + "integrity": "sha512-bhGhbJiZKpuu7wTaSak4hyZcFPlnDeuSF/2vglze8B4w2LubcSbbOnkVTzTs5SXtzh4Xz8eRjaNnAm+u2GYufQ==", "dependencies": { - "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/api-logs": "0.50.0", "@types/shimmer": "^1.0.2", "import-in-the-middle": "1.7.1", "require-in-the-middle": "^7.1.1", @@ -3137,12 +2611,12 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.35.0.tgz", - "integrity": "sha512-rb3hIWA7f0HXpXpfElnGC6CukRxy58/OJ6XYlTzpZJtNJPao7BuobZjkQEscaRYhUzgi7X7R1aKkIUOTV5JFrg==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.36.0.tgz", + "integrity": "sha512-qIXHiYbU8hfFHtI/y5ntv2k4g1stlQBHXu383xCqFdg3XbkLuKIyQ+QnnP8Ybb12qh+hzuXstEpFiF0dSmuhZA==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3153,11 +2627,11 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.39.0.tgz", - "integrity": "sha512-D+oG/hIBDdwCNq7Y6BEuddjcwDVD0C8NhBE7A85mRZ9RLG0bKoWrhIdVvbpqEoa0U5AWe9Y98RX4itNg7WTy4w==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.40.0.tgz", + "integrity": "sha512-DF1Ja1AQNV8K5tAW4STcI0KKuu1IDzCM1aa66UDbJwZfEMq0QedzKS7FjoIB+wV4Mk8+WJQRtzISFmjDiquxmw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/propagator-aws-xray": "^1.3.1", "@opentelemetry/resources": "^1.8.0", "@opentelemetry/semantic-conventions": "^1.0.0", @@ -3171,13 +2645,13 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.39.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.39.1.tgz", - "integrity": "sha512-QnvIMVpzRYqQHSXydGUksbhBjPbMyHSUBwi6ocN7gEXoI711+tIY3R1cfRutl0u3M67A/fAvPI3IgACfJaFORg==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.40.0.tgz", + "integrity": "sha512-USjyLBbpYIp4YPcTqcvspSmckZ/i2r/LSBkcoiIfCGNsbWyEK3mBDUTV3ULL6cuA0zGvYAabNDFZ2qm5vCvAuw==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/propagation-utils": "^0.30.7", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/propagation-utils": "^0.30.8", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3188,12 +2662,12 @@ } }, "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.36.0.tgz", - "integrity": "sha512-sHD5BSiqSrgWow7VmugEFzV8vGdsz5m+w1v9tK6YwRzuAD7vbo57chluq+UBzIqStoCH+0yOzRzSALH7hrfffg==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.37.0.tgz", + "integrity": "sha512-NKHuTIn1JrxfKZ8sVZjwT/bD3wyijkipiWzBvvxGj8pJJJNP5shVHWcEJhk5tQsfKQc4LproTAPdklex1o5/ag==", "dependencies": { - "@opentelemetry/api-logs": "^0.49.1", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/api-logs": "^0.50.0", + "@opentelemetry/instrumentation": "^0.50.0", "@types/bunyan": "1.8.9" }, "engines": { @@ -3204,11 +2678,11 @@ } }, "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.36.0.tgz", - "integrity": "sha512-gMfxzryOIP/mvSLXBJp/QxSr2NvS+cC1dkIXn+aSOzYoU1U3apeF3nAyuikmY9dRCQDV7wHPslqbi+pCmd4pAQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.37.0.tgz", + "integrity": "sha512-3owM6+vqu+akYWnRY4hTmzDPoa3NrX++pmRMwJZPd6+zPlu7NTXPFpjxIhM0IzhGUbv3H5V9ULQw/CUUHTeh2g==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3219,12 +2693,12 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.34.0.tgz", - "integrity": "sha512-PJO99nfyUp3JSoBMhwZsOQDm/XKfkb/QQ8YTsNX4ZJ28phoRcNLqe36mqIMp80DKmKAX4xkxCAyrSYtW8QqZxA==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.35.0.tgz", + "integrity": "sha512-COcQdFNVXyqaVGSuBgIyLu1qZUebjWyQhMAdMOOulCRznhquoyffeymQDZ0LB8XjD7kkG8if8PbykL4b8tjbNA==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/connect": "3.4.36" }, @@ -3236,11 +2710,11 @@ } }, "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.4.0.tgz", - "integrity": "sha512-n53QvozzgMS9imEclow2nBYJ/jtZlZqiKIqDUi2/g0nDi08F555JhDS03d/Z+4NJxbu7bDLAg12giCV9KZN/Jw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.5.0.tgz", + "integrity": "sha512-81id1pd8/bLAJT5+kr0V9iMKNCImE+LAX1E02qFui0lyTP9IGk2gQXPf3U68fyHxxJOjUqr1VPkDL/Pcjrfrfg==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3251,11 +2725,11 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.7.0.tgz", - "integrity": "sha512-sIaevxATJV5YaZzBTTcTaDEnI+/1vxYs+lVk1honnvrEAaP0FA9C/cFrQEN0kP2BDHkHRE/t6y5lGUqusi/h3A==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.8.0.tgz", + "integrity": "sha512-1LHr3W6BZm2VyoFo2boYD1wUlDD3V4dIFoCVg+PwQgHqampiJbl8raEMaa66RNl1LOq+vw4LMgbBqWIUog1Few==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "engines": { "node": ">=14" @@ -3265,11 +2739,11 @@ } }, "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.34.0.tgz", - "integrity": "sha512-3tmXdvrzHQ7S3v82Cm36PTYLtgg2+hVm00K1xB3uzP08GEo9w/F8DW4me9z6rDroVGiLIg621RZ6dzjBcmmFCg==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.35.0.tgz", + "integrity": "sha512-ci7hIJcp8AWsJntRMt397VGYh2Isou/u8nDX/fPa9nlbIR6VssgcHTpAV6j98Q2+N3/kTPcdTs/WtLI2g8Wwgg==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "semver": "^7.5.4" }, @@ -3281,13 +2755,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.36.1.tgz", - "integrity": "sha512-ltIE4kIMa+83QjW/p7oe7XCESF29w3FQ9/T1VgShdX7fzm56K2a0xfEX1vF8lnHRGERYxIWX9D086C6gJOjVGA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.37.0.tgz", + "integrity": "sha512-ts6yI7huK41clpcSHqP0NhZbikO4nWFHu/GF2+88IMMKTUBw8IJPnrvXeOUSg9B60uyGaftxfKvfKENtevBFig==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0" }, "engines": { "node": ">=14" @@ -3297,12 +2771,12 @@ } }, "node_modules/@opentelemetry/instrumentation-fastify": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.34.0.tgz", - "integrity": "sha512-2Qu66XBkfJ8tr6H+RHBTyw/EX73N9U7pvNa49aonDnT9/mK58k7AKOscpRnKXOvHqc2YIdEPRcBIWxhksPFZVA==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.35.0.tgz", + "integrity": "sha512-lUHj4lYmJswKHR0twg3KFSiIkfTlF4tyh+VwyrrNDYy7yGDqzftP3tP4coVdhT78RRp3DpcAGggbfrIJbdP9pA==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3313,12 +2787,12 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.10.0.tgz", - "integrity": "sha512-XtMoNINVsIQTQHjtxe7A0Lng96wxA5DSD5CYVVvpquG6HJRdZ4xNe9DTU03YtoEFqlN9qTfvGb/6ILzhKhiG8g==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.11.0.tgz", + "integrity": "sha512-vVhrKNYc8S/tN5kRsuGDNaCdDVFoMSGFSrOKt9NaB53edWNB2psoUwR+aHUg0U+87zVnXMvRf2e0yDssg3WMcg==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3329,11 +2803,11 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.34.0.tgz", - "integrity": "sha512-jdI7tfVVwZJuTu4j2kAvJtx4wlEQKIXSZnZG4RdqRHc56KqQQDuVTBLvUgmDXvnSVclH9ayf4oaAV08R9fICtw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.35.0.tgz", + "integrity": "sha512-2Vpy4Vj3RaeAN+uxG9kI2SnKKoitgcOmpD/GR5xjNKQEv2av5X1Gor71ZH2Ec3NjB9HpOLp9QRJg+misFnyYZQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3344,11 +2818,11 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.38.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.38.1.tgz", - "integrity": "sha512-mSt4ztn3EVlLtZJ+tDEqq5GUEYdY8cbTT9SeVJFmXSfdSQkPZn0ovo/dRe6dUcplM60gg4w+llw8SZuQN0iZfQ==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.39.0.tgz", + "integrity": "sha512-TUe4DJJ2S89L45eO3esGrQKvIGVasj7gY4Nk7PecxkgeVnKO5gJ41mlbhUOF44vXxYQq53470FNnhe396+JlBQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "engines": { "node": ">=14" @@ -3358,12 +2832,12 @@ } }, "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.49.1.tgz", - "integrity": "sha512-f8mQjFi5/PiP4SK3VDU1/3sUUgs6exMtBgcnNycgCKgN40htiPT+MuDRwdRnRMNI/4vNQ7p1/5r4Q5oN0GuRBw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.50.0.tgz", + "integrity": "sha512-/ZFuvHtrHyxfRJX5CJ8yPKokAIcvTbIJAoR4AN+gBq1YqecWuCr4XG52p5YU5qDrbwOBtShOrC8d4GdFZOky6Q==", "dependencies": { - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -3373,12 +2847,12 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.35.0.tgz", - "integrity": "sha512-j7q99aTLHfjNKW94qJnEaDatgz+q2psTKs7lxZO4QHRnoDltDk39a44/+AkI1qBJNw5xyLjrApqkglfbWJ2abg==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.36.0.tgz", + "integrity": "sha512-0NnXuRF89Windosn+iUpq5Fn/Foy8PMJxtLfe6CakDJIUGPj/g1+erz5irqSOc0P5mM3rEqKC/cYCoSIMKo/eA==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/hapi__hapi": "20.0.13" }, @@ -3390,13 +2864,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.49.1.tgz", - "integrity": "sha512-Yib5zrW2s0V8wTeUK/B3ZtpyP4ldgXj9L3Ws/axXrW1dW0/mEFKifK50MxMQK9g5NNJQS9dWH7rvcEGZdWdQDA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.50.0.tgz", + "integrity": "sha512-bsd6Nv0FtN9C6M6vX/kgPzvJY9UhJc4CZZNvqDbsfVQv3/MWvPrYgthf41AhrehqeDnpfn/QGzNKtdWUduGanQ==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/semantic-conventions": "1.22.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/semantic-conventions": "1.23.0", "semver": "^7.5.2" }, "engines": { @@ -3407,11 +2881,11 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.38.0.tgz", - "integrity": "sha512-c9nQFhRjFAtpInTks7z5v9CiOCiR8U9GbIhIv0TLEJ/r0wqdKNLfLZzCrr9XQ9WasxeOmziLlPFhpRBAd9Q4oA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.39.0.tgz", + "integrity": "sha512-Xh0d6LajMXr4zQRxjub/eiRtqQcFBDfoP7mIwmkirA6wBxq+4fEe2QrEMMEyUCPl1MDMsLS8YKUu2r95W/0iUw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/ioredis4": "npm:@types/ioredis@^4.28.10" @@ -3424,11 +2898,11 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.34.0.tgz", - "integrity": "sha512-6kZOEvNJOylTQunU5zSSi4iTuCkwIL9nwFnZg7719p61u3d6Qj3X4xi9su46VE3M0dH7vEoxUW+nb/0ilm+aZg==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.35.0.tgz", + "integrity": "sha512-pIfOn3HSPBFHWd55rS2YPLXq4pBGjLbKwrNnd0x8/nM5TPNy3GueSaruGvMZwkQ68Z1nBB42hLXRKozgErBlqw==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3439,13 +2913,13 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.38.0.tgz", - "integrity": "sha512-lQujF4I3wdcrOF14miCV2pC72H+OJKb2LrrmTvTDAhELQDN/95v0doWgT9aHybUGkaAeB3QG4d09sved548TlA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.39.0.tgz", + "integrity": "sha512-eSqPzDykJVF9AcOuQvYqYCA/TN8tnU9/RYgrdPclaQcH6nfp0ZbQqLsAMGOwatfwJ8p06FAj+koPBy5NQNFMow==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0", "@types/koa": "2.14.0", "@types/koa__router": "12.0.3" }, @@ -3457,11 +2931,11 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.35.0.tgz", - "integrity": "sha512-wCXe+iCF7JweMgY3blLM2Y1G0GSwLEeSA61z/y1UwzvBLEEXt7vL6qOl2mkNcUL9ZbLDS+EABatBH+vFO6DV5Q==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.36.0.tgz", + "integrity": "sha512-wnqHo4CJfMmXMGJonT4kPGwiEAFlO1a9IrWxVoc1+3B+yD9wKFRby+dVy0s5Ddarwl0SD5ljUZklXMgcEQjlQQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "engines": { "node": ">=14" @@ -3471,11 +2945,11 @@ } }, "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.34.0.tgz", - "integrity": "sha512-RleFfaag3Evg4pTzHwDBwo1KiFgnCtiT4V6MQRRHadytNGdpcL+Ynz32ydDdiOXeadt7xpRI7HSvBy0quGTXSw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.35.0.tgz", + "integrity": "sha512-UrdzoXShPEfJ2ppjCNHcioHF/Bcd3Jr6pEzayUuRfIXiurOhsHlTnUHPH1rHDunEOcR8FGUicuerEDdDDDU+dA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/memcached": "^2.2.6" }, @@ -3487,11 +2961,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.41.0.tgz", - "integrity": "sha512-DlSH0oyEuTW5gprCUppb0Qe3pK3cpUUFW5eTmayWNyICI1LFunwtcrULTNv6UiThD/V5ykAf/GGGEa7KFAmkog==", + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.42.0.tgz", + "integrity": "sha512-pnUewN9r9Kw3on+6hM24COP4X4zJP5PafXCd1b/qka45Savyle7IaJyJODWVHkX6hN8ERsG853cNsYrsO/f/lQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/sdk-metrics": "^1.9.1", "@opentelemetry/semantic-conventions": "^1.0.0" }, @@ -3503,12 +2977,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.36.0.tgz", - "integrity": "sha512-UelQ8dLQRLTdck3tPJdZ17b+Hk9usLf1cY2ou5THAaZpulUdpg62Q9Hx2RHRU71Rp2/YMDk25og7GJhuWScfEA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.37.0.tgz", + "integrity": "sha512-VBGfiERp2fxE35OjUFAlfAOKkPOwYx3FqqV8LG7mq6SGK6yhOaU7RvE1b//bhGWoVYohVyxaBIMRVZr4xpmOJQ==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3519,11 +2993,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.36.0.tgz", - "integrity": "sha512-2mt/032SLkiuddzMrq3YwM0bHksXRep69EzGRnBfF+bCbwYvKLpqmSFqJZ9T3yY/mBWj+tvdvc1+klXGrh2QnQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.37.0.tgz", + "integrity": "sha512-swp9B4oIOcduYfOslc7tbFDjOCgKlcSdVOO+emVnaOEvYG19NcGdc82WH6Zqmctl5oOEBVeKHS+wgjjbwBiMWA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/mysql": "2.15.22" }, @@ -3535,11 +3009,11 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.36.0.tgz", - "integrity": "sha512-F63lKcl/R+if2j5Vz66c2/SLXQEtLlFkWTmYb8NQSgmcCaEKjML4RRRjZISIT4IBwdpanJ2qmNuXVM6MYqhBXw==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.37.0.tgz", + "integrity": "sha512-KaXZr8B13IovmSN5Xe5qblp34VPsLaooivHMnhOwj2so7ivB1PcGGkesWH5knXC/9iQryiIFXwSDdHrd4R5iXQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@opentelemetry/sql-common": "^0.40.0" }, @@ -3551,11 +3025,11 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.35.0.tgz", - "integrity": "sha512-INKA7CIOteTSRVxP7SQaFby11AYU3uezI93xDaDRGY4TloXNVoyw5n6UmcVJU4yDn6xY2r7zZ2SVHvblUc21/g==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.36.0.tgz", + "integrity": "sha512-ku1HdUWFwd6ajJh0pTPwDmcRZF8sbjLCTQXAjUTrJEfmiXavKHwFBZnyF9/5NWedK8FrfZjZ7+hH9heiDigMNQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3566,11 +3040,11 @@ } }, "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.34.0.tgz", - "integrity": "sha512-gjybNOQQqbXmD1qVHNO2qBJI4V6p3QQ7xKg3pnC/x7wRdxn+siLQj7QIVxW85C3mymngoJJdRs6BwI3qPUfsPQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.35.0.tgz", + "integrity": "sha512-L5qw1G7vwxuePpuLKxpbIg49wH0brfgVhbMaMa09YX4HM6yj1KQRRGMMFxB00NQo2u42kuv/7F7AB/lWeG0yUA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3581,12 +3055,12 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.39.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.39.1.tgz", - "integrity": "sha512-pX5ujDOyGpPcrZlzaD3LJzmyaSMMMKAP+ffTHJp9vasvZJr+LifCk53TMPVUafcXKV/xX/IIkvADO+67M1Z25g==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.40.0.tgz", + "integrity": "sha512-z3v8OzfImnycykWgqIdS44aUZlRwq51yYIo8GfmiRBd8yyMl2ESQyv6z/IAWBWyT015IWGy3ZTijySe65P9J1w==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0", "@opentelemetry/sql-common": "^0.40.0", "@types/pg": "8.6.1", "@types/pg-pool": "2.0.4" @@ -3609,11 +3083,11 @@ } }, "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.36.0.tgz", - "integrity": "sha512-oEz+BJEYRBMAUu7MVJFJhhlsBuwLaUGjbJciKZRIeGX+fUtgcbQGV+a2Ris9jR3yFzWZrYg0aNBSCbGqvPCtMQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.37.0.tgz", + "integrity": "sha512-5V8Cz25pNbbiz0ewWuuKmSCygzoqzgDB3EeliixCnxOzu5Fp7nVKfHlQ775uxylLuODCYwwgVhU6L1Sif4j4AA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "engines": { "node": ">=14" @@ -3623,11 +3097,11 @@ } }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.37.0.tgz", - "integrity": "sha512-9G0T74kheu37k+UvyBnAcieB5iowxska3z2rhUcSTL8Cl0y/CvMn7sZ7txkUbXt0rdX6qeEUdMLmbsY2fPUM7Q==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.38.0.tgz", + "integrity": "sha512-PzcYLkPvClE7NyjsuqvjCWnCO8wt6ZaYHM6AB9Ga9EQKFh1oNEr2dXCnqVHMuxjzbTWF2+c86yPZAdCEfyKfmA==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0" }, @@ -3639,11 +3113,11 @@ } }, "node_modules/@opentelemetry/instrumentation-redis-4": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.37.0.tgz", - "integrity": "sha512-WNO+HALvPPvjbh7UEEIuay0Z0d2mIfSCkBZbPRwZttDGX6LYGc2WnRgJh3TnYqjp7/y9IryWIbajAFIebj1OBA==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.38.0.tgz", + "integrity": "sha512-ugDb2txE2o76zwasSzOpmlFlUUnQu4MT2WpO+UmmS5sN4pClU+zsZy0QxjHB03PUJMLCRfsbZf2ez0jkpm7yag==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0" }, @@ -3655,12 +3129,12 @@ } }, "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.36.0.tgz", - "integrity": "sha512-QbOh8HpnnRn4xxFXX77Gdww6M78yx7dRiIKR6+H3j5LH5u6sYckTXw3TGPSsXsaM4DQHy0fOw15sAcJoWkC+aQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.37.0.tgz", + "integrity": "sha512-eJyfLsV243Jx4wL8FSZ08Q9Ih6ojOIh6csAJJSNqdFtSMBBQXKVcTw5g2nOi2I2d0AmPNYhaA5H0FLLz6ZIMXw==", "dependencies": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3671,11 +3145,11 @@ } }, "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.35.0.tgz", - "integrity": "sha512-MdxGJuNTIy/2qDI8yow6cRBQ87m6O//VuHIlawe8v0x1NsTOSwS72xm+BzTuY9D0iMqiJUiTlE3dBs8DA91MTw==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.36.0.tgz", + "integrity": "sha512-HhVw2OuhlHlbzq0X3rUf3ilyBcM1UsFABESKU6NF1MlVWm1OlZDuZLmlk5EvuU/u9cjduXp61jVj5s4OCW+knQ==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3686,11 +3160,11 @@ } }, "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.37.0.tgz", - "integrity": "sha512-aIztxmx/yis/goEndnoITrZvDDr1GdCtlsWo9ex7MhUIjqq5nJbTuyigf3GmU86XFFhSThxfQuJ9DpJyPxfBfA==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.38.0.tgz", + "integrity": "sha512-gls1SrTxTy2swwtrn2O+b8wK2MmnQTxEkmpZ6YPh+52mlcvOpW35L+Wfx/aM/hWOw8Za1Wlns710UBH4p88M2Q==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" }, "engines": { @@ -3701,11 +3175,11 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.8.0.tgz", - "integrity": "sha512-BBRW8+Qm2PLNkVMynr3Q7L4xCAOCOs0J9BJIJ8ZGoatW42b2H4qhMhq35jfPDvEL5u5azxHDapmUVYrDJDjAfA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.9.0.tgz", + "integrity": "sha512-VmrKxXmfrzc0Sa5AtDMB96M/lr8cOiU61nYVpY0VtS4QPD7cqqwjeBzBvZpvSu1OZS/zbYg+B9ILOl2cHiG5+g==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/tedious": "^4.0.10" }, @@ -3717,11 +3191,11 @@ } }, "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.35.0.tgz", - "integrity": "sha512-ymcuA3S2flnLmH1GS0105H91iDLap8cizOCaLMCp7Xz7r4L+wFf1zfix9M+iSkxcPFshHRt8LFA/ELXw51nk0g==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.36.0.tgz", + "integrity": "sha512-NTuGpUA9AGC7EmKQyMn3ABiqJ3XvHihRcxdvqhs8E72nHmIU3n6om+DCYYE3fpEQwbC9N14ir8fqA5Oxn0Ynag==", "dependencies": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" }, "engines": { "node": ">=14" @@ -3731,11 +3205,11 @@ } }, "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", - "integrity": "sha512-z6sHliPqDgJU45kQatAettY9/eVF58qVPaTuejw9YWfSRqid9pXPYeegDCSdyS47KAUgAtm+nC28K3pfF27HWg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.50.0.tgz", + "integrity": "sha512-JUmjmrCmE1/fc4LjCQMqLfudgSl5OpUkzx7iA94b4jgeODM7zWxUoVXL7/CT7fWf47Cn+pmKjMvTCSESqZZ3mA==", "dependencies": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" }, "engines": { "node": ">=14" @@ -3745,13 +3219,13 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.49.1.tgz", - "integrity": "sha512-DNDNUWmOqtKTFJAyOyHHKotVox0NQ/09ETX8fUOeEtyNVHoGekAVtBbvIA3AtK+JflP7LC0PTjlLfruPM3Wy6w==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.50.0.tgz", + "integrity": "sha512-J500AczSD7xEsjXpwNzSh5HQqxW73PT3CCNsi1VEWCE+8UPgVfkHYIGRHGoch35DV+CMe1svbi7gAk3e5eCSVA==", "dependencies": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", "protobufjs": "^7.2.3" }, "engines": { @@ -3762,12 +3236,12 @@ } }, "node_modules/@opentelemetry/otlp-proto-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.49.1.tgz", - "integrity": "sha512-x1qB4EUC7KikUl2iNuxCkV8yRzrSXSyj4itfpIO674H7dhI7Zv37SFaOJTDN+8Z/F50gF2ISFH9CWQ4KCtGm2A==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.50.0.tgz", + "integrity": "sha512-hlbn3eZbhxoK79Sq1ddj1f7qcx+PzsPQC/SFpJvaWgTaqacCbqJmpzWDKfRRCAC7iGX2Hj/sgpf8vysazqyMOw==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", "protobufjs": "^7.2.3" }, "engines": { @@ -3778,16 +3252,16 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.49.1.tgz", - "integrity": "sha512-Z+koA4wp9L9e3jkFacyXTGphSWTbOKjwwXMpb0CxNb0kjTHGUxhYRN8GnkLFsFo5NbZPjP07hwAqeEG/uCratQ==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.50.0.tgz", + "integrity": "sha512-s0sl1Yfqd5q1Kjrf6DqXPWzErL+XHhrXOfejh4Vc/SMTNqC902xDsC8JQxbjuramWt/+hibfguIvi7Ns8VLolA==", "dependencies": { - "@opentelemetry/api-logs": "0.49.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-logs": "0.49.1", - "@opentelemetry/sdk-metrics": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/api-logs": "0.50.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-logs": "0.50.0", + "@opentelemetry/sdk-metrics": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" }, "engines": { "node": ">=14" @@ -3797,9 +3271,9 @@ } }, "node_modules/@opentelemetry/propagation-utils": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.7.tgz", - "integrity": "sha512-QkxOkuCQdq8YgJstEMF4ntSyr0ivCrcQc49uvO2pyccrniu2DwA+JD071aM4BXfNVSCeOuhIyW/3QPiZYl4zdA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.8.tgz", + "integrity": "sha512-ZKjsUm//SvL8I9JS+bJpAXGpe0Fb+sO+AiWS0fb7EKKLEm3GoNAO7CDMs8GMZBZ91ElR3tBjdUKf/9MzUdYHBA==", "engines": { "node": ">=14" }, @@ -3822,11 +3296,11 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.22.0.tgz", - "integrity": "sha512-qBItJm9ygg/jCB5rmivyGz1qmKZPsL/sX715JqPMFgq++Idm0x+N9sLQvWFHFt2+ZINnCSojw7FVBgFW6izcXA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.23.0.tgz", + "integrity": "sha512-cZ6rl8y2bdxYQ4e+zP2CQ+QmuPebaLBLO1skjFpj3eEu7zar+6hBzUP3llMOUupkQeQSwXz+4c8dZ26OhYfG/g==", "dependencies": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" }, "engines": { "node": ">=14" @@ -3836,11 +3310,11 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.22.0.tgz", - "integrity": "sha512-pMLgst3QIwrUfepraH5WG7xfpJ8J3CrPKrtINK0t7kBkuu96rn+HDYQ8kt3+0FXvrZI8YJE77MCQwnJWXIrgpA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.23.0.tgz", + "integrity": "sha512-6iArixfgIl3ZgzeltQ5jyiKbjZygM+MbM84pXi1HL0Qs4x4Ck5rM6wEtjhZffFnlDMWEkEqrnM0xF6bTfbiMAQ==", "dependencies": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" }, "engines": { "node": ">=14" @@ -3858,12 +3332,12 @@ } }, "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.7.tgz", - "integrity": "sha512-7o/waBJ08JrKED4blHGyBPIN1HMM1KEvhbO1HmdA+tsUqsGwZdTjsdMKFW7hc1TvAu4AQEnuvMy/Q5OByVr95A==", + "version": "0.28.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.8.tgz", + "integrity": "sha512-njdK7S90OX99H0nlDh45vmGU3Bn46JSNjciU18NefyU6R3Dq5ZUU13DWxSlrCBWAiH/+SFJyHQMFsXRnTOFp6w==", "dependencies": { "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" }, "engines": { "node": ">=14" @@ -3873,13 +3347,13 @@ } }, "node_modules/@opentelemetry/resource-detector-aws": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.0.tgz", - "integrity": "sha512-Cn8eQ/heLqrNrPuHGG7xUkk//VQt4hzVIPurmLlCI0wrDV6HR+yykBvRkJBuSdLzbjeQ/qNbGel9OvTmA6PBQA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.1.tgz", + "integrity": "sha512-QsPJwXDxlt+IWQazST7renk9KDOAK3hsywb0Mw6gEwgEoe12EvdVcT+mCknTc+hu5WzxiTmFMtnn1TWab7To1g==", "dependencies": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" }, "engines": { "node": ">=14" @@ -3889,12 +3363,12 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.7.tgz", - "integrity": "sha512-AYqwffGVuGLuzzVOQMLNHTztwyvsep9noxN9HTQ/grwmJSWZ6851kNx+W735K7v6GZEDmXeLpBn+J3TeqKQUJA==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.8.tgz", + "integrity": "sha512-YusLYSo8Rr1yKEhakaJ2XgbfbLses5t8YsSRHhO0b6SLliZOFq9ymxoWgtuwyabNCqMMJJYuvrE3Nq0AL6sbcQ==", "dependencies": { "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" }, "engines": { "node": ">=14" @@ -3904,9 +3378,9 @@ } }, "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.29.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.7.tgz", - "integrity": "sha512-uUHKfoOgBCZCEPCU6FWnRrbYuz1miaeIfos0Xe38YuR06vQvddhqZ0tewYunJpfECfKEcjSjY0eDe2QIRLMkXw==", + "version": "0.29.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.8.tgz", + "integrity": "sha512-jvFtsnrQI7CLDxehp6BxtudbZ3a0HZ+Dj/vFy251280efc15gQdlvFUqXbKBw6ODt/3o38ilw8e0qePJbm2eAg==", "dependencies": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.0.0", @@ -3921,12 +3395,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.22.0.tgz", - "integrity": "sha512-+vNeIFPH2hfcNL0AJk/ykJXoUCtR1YaDUZM+p3wZNU4Hq98gzq+7b43xbkXjadD9VhWIUQqEwXyY64q6msPj6A==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", + "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -3936,12 +3410,12 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.49.1.tgz", - "integrity": "sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.50.0.tgz", + "integrity": "sha512-PeUEupBB29p9nlPNqXoa1PUWNLsZnxG0DCDj3sHqzae+8y76B/A5hvZjg03ulWdnvBLYpnJslqzylG9E0IL87g==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0" }, "engines": { "node": ">=14" @@ -3952,12 +3426,12 @@ } }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz", - "integrity": "sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.23.0.tgz", + "integrity": "sha512-4OkvW6+wST4h6LFG23rXSTf6nmTf201h9dzq7bE0z5R9ESEVLERZz6WXwE7PSgg1gdjlaznm1jLJf8GttypFDg==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", "lodash.merge": "^4.6.2" }, "engines": { @@ -3968,23 +3442,23 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.49.1.tgz", - "integrity": "sha512-feBIT85ndiSHXsQ2gfGpXC/sNeX4GCHLksC4A9s/bfpUbbgbCSl0RvzZlmEpCHarNrkZMwFRi4H0xFfgvJEjrg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.50.0.tgz", + "integrity": "sha512-LhIXHnvcnhRYcPwG9VG4G6lJ7x4ElYF6UYHHmXA7e4ZWzSUEFmAPfR1IBWv358aD1KwffcEBu7J6zeAR7lPZag==", "dependencies": { - "@opentelemetry/api-logs": "0.49.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.49.1", - "@opentelemetry/exporter-trace-otlp-http": "0.49.1", - "@opentelemetry/exporter-trace-otlp-proto": "0.49.1", - "@opentelemetry/exporter-zipkin": "1.22.0", - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-logs": "0.49.1", - "@opentelemetry/sdk-metrics": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", - "@opentelemetry/sdk-trace-node": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/api-logs": "0.50.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.50.0", + "@opentelemetry/exporter-trace-otlp-http": "0.50.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.50.0", + "@opentelemetry/exporter-zipkin": "1.23.0", + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-logs": "0.50.0", + "@opentelemetry/sdk-metrics": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", + "@opentelemetry/sdk-trace-node": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -3994,13 +3468,13 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz", - "integrity": "sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz", + "integrity": "sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==", "dependencies": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" }, "engines": { "node": ">=14" @@ -4010,15 +3484,15 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.22.0.tgz", - "integrity": "sha512-gTGquNz7ue8uMeiWPwp3CU321OstQ84r7PCDtOaCicjbJxzvO8RZMlEC4geOipTeiF88kss5n6w+//A0MhP1lQ==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.23.0.tgz", + "integrity": "sha512-dwnin5Go2r6VzJZkVc9JBPupssWp7j2EFto+S7qRkwQ00WDykWeq3x2Skk7I1Jr448FeBSvGCQVPgV5e6s6O3w==", "dependencies": { - "@opentelemetry/context-async-hooks": "1.22.0", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/propagator-b3": "1.22.0", - "@opentelemetry/propagator-jaeger": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/context-async-hooks": "1.23.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/propagator-b3": "1.23.0", + "@opentelemetry/propagator-jaeger": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", "semver": "^7.5.2" }, "engines": { @@ -4029,9 +3503,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.22.0.tgz", - "integrity": "sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", + "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==", "engines": { "node": ">=14" } @@ -4135,6 +3609,254 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -4159,24 +3881,6 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -4204,38 +3908,255 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, + "node_modules/@swc/core": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.14.tgz", + "integrity": "sha512-tHXg6OxboUsqa/L7DpsCcFnxhLkqN/ht5pCwav1HnvfthbiNIJypr86rNx4cUnQDJepETviSqBTIjxa7pSpGDQ==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@swc/counter": "^0.1.2", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.4.14", + "@swc/core-darwin-x64": "1.4.14", + "@swc/core-linux-arm-gnueabihf": "1.4.14", + "@swc/core-linux-arm64-gnu": "1.4.14", + "@swc/core-linux-arm64-musl": "1.4.14", + "@swc/core-linux-x64-gnu": "1.4.14", + "@swc/core-linux-x64-musl": "1.4.14", + "@swc/core-win32-arm64-msvc": "1.4.14", + "@swc/core-win32-ia32-msvc": "1.4.14", + "@swc/core-win32-x64-msvc": "1.4.14" + }, + "peerDependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.14.tgz", + "integrity": "sha512-8iPfLhYNspBl836YYsfv6ErXwDUqJ7IMieddV3Ey/t/97JAEAdNDUdtTKDtbyP0j/Ebyqyn+fKcqwSq7rAof0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.14.tgz", + "integrity": "sha512-9CqSj8uRZ92cnlgAlVaWMaJJBdxtNvCzJxaGj5KuIseeG6Q0l1g+qk8JcU7h9dAsH9saHTNwNFBVGKQo0W0ujg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.14.tgz", + "integrity": "sha512-mfd5JArPITTzMjcezH4DwMw+BdjBV1y25Khp8itEIpdih9ei+fvxOOrDYTN08b466NuE2dF2XuhKtRLA7fXArQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.14.tgz", + "integrity": "sha512-3Lqlhlmy8MVRS9xTShMaPAp0oyUt0KFhDs4ixJsjdxKecE0NJSV/MInuDmrkij1C8/RQ2wySRlV9np5jK86oWw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.14.tgz", + "integrity": "sha512-n0YoCa64TUcJrbcXIHIHDWQjdUPdaXeMHNEu7yyBtOpm01oMGTKP3frsUXIABLBmAVWtKvqit4/W1KVKn5gJzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.14.tgz", + "integrity": "sha512-CGmlwLWbfG1dB4jZBJnp2IWlK5xBMNLjN7AR5kKA3sEpionoccEnChOEvfux1UdVJQjLRKuHNV9yGyqGBTpxfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.14.tgz", + "integrity": "sha512-xq4npk8YKYmNwmr8fbvF2KP3kUVdZYfXZMQnW425gP3/sn+yFQO8Nd0bGH40vOVQn41kEesSe0Z5O/JDor2TgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.14.tgz", + "integrity": "sha512-imq0X+gU9uUe6FqzOQot5gpKoaC00aCUiN58NOzwp0QXEupn8CDuZpdBN93HiZswfLruu5jA1tsc15x6v9p0Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.14.tgz", + "integrity": "sha512-cH6QpXMw5D3t+lpx6SkErHrxN0yFzmQ0lgNAJxoDRiaAdDbqA6Col8UqUJwUS++Ul6aCWgNhCdiEYehPaoyDPA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.14.tgz", + "integrity": "sha512-FmZ4Tby4wW65K/36BKzmuu7mlq7cW5XOxzvufaSNVvQ5PN4OodAlqPjToe029oma4Av+ykJiif64scMttyNAzg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "devOptional": true + }, + "node_modules/@swc/types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", + "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", + "devOptional": true, + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@testcontainers/postgresql": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.7.2.tgz", - "integrity": "sha512-BB1C4SUDhWYY4X9afHtVYXDcYpvtRz7M4CtNEI3gA5A8Zm09msg5NUmhDSJiNAO8/xILv5LHcMJ0NZPBjgM9Jg==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.8.2.tgz", + "integrity": "sha512-UYbcHlK2XnP5DkwIOB8v+atz/ZfSpyEXR//FFhvAKNlLetMTv/8W73Hbhmvv50TCd2J3biFnatyo//uJm9e7ng==", "dev": true, "dependencies": { - "testcontainers": "^10.7.2" + "testcontainers": "^10.8.2" } }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@turf/boolean-point-in-polygon": { "version": "6.5.0", @@ -4296,47 +4217,6 @@ "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.122.tgz", "integrity": "sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==" }, - "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/bcrypt": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", @@ -4390,12 +4270,6 @@ "@types/express": "*" } }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true - }, "node_modules/@types/cookies": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", @@ -4492,15 +4366,6 @@ "@types/node": "*" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", - "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/hapi__catbox": { "version": "10.2.6", "resolved": "https://registry.npmjs.org/@types/hapi__catbox/-/hapi__catbox-10.2.6.tgz", @@ -4575,49 +4440,6 @@ "@types/node": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest-when": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.5.tgz", - "integrity": "sha512-H9MDPIrz7NOu6IXP9OHExNN9LnJbGYAzRsGIDKxWr7Fth9vovemNV8yFbkUWLSEmuA8PREvAEvt9yK0PPLmFHA==", - "dev": true, - "dependencies": { - "@types/jest": "*" - } - }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -4625,9 +4447,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/keygrip": { @@ -4685,12 +4507,6 @@ "@types/node": "*" } }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true - }, "node_modules/@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", @@ -4728,9 +4544,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dependencies": { "undici-types": "~5.26.4" } @@ -4814,7 +4630,8 @@ "node_modules/@types/picomatch": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.3.tgz", - "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==" + "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", + "dev": true }, "node_modules/@types/qs": { "version": "6.9.8", @@ -4884,33 +4701,6 @@ "@types/node": "*" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/superagent": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.1.tgz", - "integrity": "sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA==", - "dev": true, - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*" - } - }, - "node_modules/@types/supertest": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", - "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", - "dev": true, - "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, "node_modules/@types/tedious": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", @@ -4939,38 +4729,23 @@ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, - "node_modules/@types/yargs": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", - "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -4990,15 +4765,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -5018,13 +4793,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5035,15 +4810,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5062,9 +4837,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5075,19 +4850,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5112,9 +4887,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5127,18 +4902,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5152,13 +4927,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -5174,6 +4949,143 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitest/coverage-v8": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.5.0" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/expect": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.5.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -5361,9 +5273,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -5389,9 +5301,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "devOptional": true, "engines": { "node": ">=0.4.0" @@ -5677,7 +5589,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/argparse": { "version": "2.0.1", @@ -5709,12 +5622,6 @@ "node": ">=8" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -5724,6 +5631,15 @@ "safer-buffer": "~2.1.0" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -5734,133 +5650,11 @@ "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "node_modules/b4a": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==" }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6079,27 +5873,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -6137,14 +5910,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", - "engines": { - "node": ">=4" - } - }, "node_modules/buildcheck": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", @@ -6248,6 +6013,15 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", @@ -6273,15 +6047,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001581", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", @@ -6302,6 +6067,24 @@ } ] }, + "node_modules/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6317,20 +6100,23 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -6371,21 +6157,6 @@ "node": ">=6.0" } }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, "node_modules/cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -6534,22 +6305,6 @@ "node": ">=0.10.0" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -6595,18 +6350,6 @@ "color-support": "bin.js" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -6632,12 +6375,6 @@ "node": ">= 6" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "node_modules/compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -6739,12 +6476,6 @@ "node": ">= 0.6" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "node_modules/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", @@ -6770,12 +6501,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true - }, "node_modules/core-js-compat": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", @@ -6907,32 +6632,12 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/cron": { "version": "3.1.6", @@ -6988,18 +6693,16 @@ } } }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "dependencies": { + "type-detect": "^4.0.0" }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=6" } }, "node_modules/deep-is": { @@ -7045,15 +6748,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -7092,25 +6786,6 @@ "node": ">=8" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "node_modules/diacritics": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", @@ -7120,7 +6795,8 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } @@ -7274,18 +6950,6 @@ "integrity": "sha512-sYSQhJCJa4aGA1wYol5cMQgekDBlbVfTRavlGZVr3WZpDdOPcp6a6xUnFfrt8TqZhsBYYbDxJZCjGfHuGupCRQ==", "dev": true }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -7371,6 +7035,44 @@ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", "dev": true }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -7494,9 +7196,9 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -7678,6 +7380,15 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -7716,35 +7427,6 @@ "node": ">=0.8.x" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/exiftool-vendored": { "version": "24.6.0", "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.6.0.tgz", @@ -7774,6 +7456,7 @@ "version": "12.78.0", "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.78.0.tgz", "integrity": "sha512-K8j9NgxRpTFskFuXEl0AGsc692yYyThe4i3SXgx7xc0fu/vwD2c7tRGljkEtvaweYnMmfrF4DhCpuTu0aux6sg==", + "optional": true, "os": [ "!win32" ] @@ -7783,31 +7466,6 @@ "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==" }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", @@ -7950,15 +7608,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -8179,35 +7828,6 @@ "webpack": "^5.11.0" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -8376,15 +7996,6 @@ "node": ">=14" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/geo-tz": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/geo-tz/-/geo-tz-8.0.2.tgz", @@ -8425,6 +8036,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -8443,15 +8063,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -8476,18 +8087,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/glob": { "version": "10.3.12", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", @@ -8718,15 +8317,6 @@ "he": "bin/he" } }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -8774,15 +8364,6 @@ "node": ">= 6" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/i18n-iso-countries": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.11.0.tgz", @@ -8825,9 +8406,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -8859,25 +8440,6 @@ "module-details-from-path": "^1.0.3" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -9033,15 +8595,6 @@ "node": ">=8" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9111,30 +8664,14 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -9164,29 +8701,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", @@ -9225,694 +8739,10 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-when": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jest-when/-/jest-when-3.6.0.tgz", - "integrity": "sha512-+cZWTy0ekAJo7M9Om0Scdor1jm3wDiYJWmXE8U22UVnkH54YCXAuaqz3P+up/FdtOg8g4wHOxV7Thd7nKhT6Dg==", - "dev": true, - "peerDependencies": { - "jest": ">= 25" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/joi": { - "version": "17.12.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", - "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", + "version": "17.12.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.3.tgz", + "integrity": "sha512-2RRziagf555owrm9IRVtdKynOBeITiDpuZqIpgwqXShPncPKNiRQoiGsl/T8SQdq+8ugRzH2LqY67irr2y/d+g==", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -9945,18 +8775,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -10027,15 +8845,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -10074,15 +8883,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10106,6 +8906,15 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -10115,6 +8924,22 @@ "node": ">=6.11.5" } }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10168,12 +8993,6 @@ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -10205,13 +9024,13 @@ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "get-func-name": "^2.0.1" } }, "node_modules/luxon": { @@ -10234,6 +9053,17 @@ "node": ">=12" } }, + "node_modules/magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -10260,16 +9090,8 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } + "optional": true, + "peer": true }, "node_modules/media-typer": { "version": "0.3.0", @@ -10460,6 +9282,18 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "node_modules/mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, "node_modules/mnemonist": { "version": "0.39.8", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", @@ -10610,6 +9444,24 @@ "dev": true, "optional": true }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10693,6 +9545,20 @@ "node": ">=16" } }, + "node_modules/nestjs-cls": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-4.3.0.tgz", + "integrity": "sha512-MVTun6tqCZih8AJXRj8uBuuFyJhQrIA9m9fStiQjbBXUkE3BrlMRvmLzyw8UcneB3xtFFTfwkAh5PYKRulyaOg==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@nestjs/common": "> 7.0.0 < 11", + "@nestjs/core": "> 7.0.0 < 11", + "reflect-metadata": "*", + "rxjs": ">= 7" + } + }, "node_modules/nestjs-otel": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/nestjs-otel/-/nestjs-otel-5.1.5.tgz", @@ -10751,12 +9617,6 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -10806,18 +9666,6 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -11036,11 +9884,6 @@ "node": ">=6" } }, - "node_modules/packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -11170,6 +10013,21 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -11183,15 +10041,13 @@ } }, "node_modules/pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -11217,9 +10073,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -11238,17 +10094,17 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -11276,8 +10132,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "4.0.2", @@ -11290,77 +10145,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", "dev": true, "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" } }, "node_modules/pluralize": { @@ -11372,6 +10165,34 @@ "node": ">=4" } }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -11506,19 +10327,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -11623,22 +10431,6 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -11929,14 +10721,9 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, "node_modules/regexp-tree": { "version": "0.1.27", @@ -12023,27 +10810,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -12060,15 +10826,6 @@ "protocol-buffers-schema": "^3.3.1" } }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/response-time": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", @@ -12151,6 +10908,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", + "fsevents": "~2.3.2" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -12537,6 +11329,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -12574,12 +11372,6 @@ "node": ">= 10" } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -12640,6 +11432,15 @@ "node": ">= 8" } }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -12705,12 +11506,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/sql-formatter": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.0.tgz", @@ -12753,26 +11548,11 @@ "nan": "^2.17.0" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true }, "node_modules/standard-as-callback": { "version": "2.1.0", @@ -12787,6 +11567,12 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, "node_modules/stream-source": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz", @@ -12817,19 +11603,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -12880,24 +11653,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -12922,51 +11677,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "node_modules/strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", "dev": true, "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" + "js-tokens": "^9.0.0" }, - "engines": { - "node": ">=6.4.0 <13 || >=14" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", - "dev": true, - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.1.2" - }, - "engines": { - "node": ">=6.4.0" - } + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true }, "node_modules/supports-color": { "version": "7.2.0", @@ -13240,9 +11967,9 @@ } }, "node_modules/testcontainers": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.7.2.tgz", - "integrity": "sha512-7d+LVd/4YKp/cutiVMLL5cnj/8p8oYELAVRRyNUM4FyUDz1OLQuwW868nDl7Vd1ZAQxzGeCR+F86FlR9Yw9fMA==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.8.2.tgz", + "integrity": "sha512-9Ink7NUyYZwOjQhk0C6R6basWy2WADNly+md3D9YDap0pcDr3C+vrO8Ah1bkYco/9Zg8VoYTHO+blkLeebBYkA==", "dev": true, "dependencies": { "@balena/dockerignore": "^1.0.2", @@ -13480,6 +12207,30 @@ "resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz", "integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==" }, + "node_modules/tinybench": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.7.0.tgz", + "integrity": "sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -13491,12 +12242,6 @@ "node": ">=0.6.0" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -13556,85 +12301,23 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" } }, - "node_modules/ts-jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", - "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -13926,11 +12609,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typeorm/node_modules/reflect-metadata": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", - "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" - }, "node_modules/typeorm/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -13965,9 +12643,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -13999,6 +12677,12 @@ "node": "*" } }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -14044,6 +12728,35 @@ "node": ">= 0.8" } }, + "node_modules/unplugin": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.10.1.tgz", + "integrity": "sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "chokidar": "^3.6.0", + "webpack-sources": "^3.2.3", + "webpack-virtual-modules": "^0.6.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin-swc": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.4.5.tgz", + "integrity": "sha512-ltkJ70kjL53onJrypaMmKDiOvhghNUCbCxjxT6Ir0eAMIBsOfRhPt6vQtxB8R/6wYk/TfIJ2gCgdx2uKNPJRHA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "load-tsconfig": "^0.2.5", + "unplugin": "^1.10.1" + }, + "peerDependencies": { + "@swc/core": "^1.2.108" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -14137,27 +12850,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "optional": true, + "peer": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -14185,13 +12879,268 @@ "node": ">= 0.8" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/vite": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { - "makeerror": "1.0.12" + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.5.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/vitest/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/watchpack": { @@ -14285,6 +13234,12 @@ "node": ">=10.13.0" } }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz", + "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==", + "dev": true + }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -14330,6 +13285,22 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -14378,25 +13349,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", @@ -14433,12 +13385,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", @@ -14485,7 +13431,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -14692,210 +13639,18 @@ } }, "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", - "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", - "dev": true - }, - "@babel/core": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", - "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", - "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", - "dev": true, - "requires": { - "@babel/types": "^7.23.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "dev": true }, "@babel/helper-validator-identifier": { @@ -14903,31 +13658,15 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, - "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true - }, - "@babel/helpers": { - "version": "7.23.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", - "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.0", - "@babel/types": "^7.23.0" - } - }, "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "requires": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "dependencies": { "ansi-styles": { @@ -14982,189 +13721,18 @@ } }, "@babel/parser": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", - "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "dev": true }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", - "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.3", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.3", - "@babel/types": "^7.23.3", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, "@babel/types": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", - "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } @@ -15192,7 +13760,8 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -15201,7 +13770,8 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -15218,6 +13788,167 @@ "tslib": "^2.4.0" } }, + "@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "dev": true, + "optional": true + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -15228,9 +13959,9 @@ } }, "@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true }, "@eslint/eslintrc": { @@ -15285,18 +14016,18 @@ } }, "@grpc/grpc-js": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.2.tgz", - "integrity": "sha512-lSbgu8iayAod8O0YcoXK3+bMFGThY2svtN35Zlm9VepsB3jfyIcoupKknEht7Kh9Q8ITjsp0J4KpYo9l4+FhNg==", + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz", + "integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==", "requires": { "@grpc/proto-loader": "^0.7.10", "@js-sdsl/ordered-map": "^4.4.2" } }, "@grpc/proto-loader": { - "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", - "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz", + "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==", "requires": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -15645,244 +14376,12 @@ } } }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, - "@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - } - }, - "@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - } - }, - "@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, "@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", @@ -15892,87 +14391,15 @@ "@sinclair/typebox": "^0.27.8" } }, - "@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" } }, "@jridgewell/resolve-uri": { @@ -15982,9 +14409,9 @@ "devOptional": true }, "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true }, "@jridgewell/source-map": { @@ -16004,9 +14431,9 @@ "devOptional": true }, "@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.1.0", @@ -16230,9 +14657,9 @@ } }, "@nestjs/config": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.1.tgz", - "integrity": "sha512-tFZyLJKanSAu51ygQ6ZBSpx95pRcwS6qSpJDW6FFgRQzkOaOUXpL8qD8yMNoYoYxuJCxph+waiBaWKgFWxn3sw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-3.2.2.tgz", + "integrity": "sha512-vGICPOui5vE6kPz1iwQ7oCnp3qWgqxldPmBQ9onkVoKlBtyc83KJCr7CjuVtf4OdovMAVcux1d8Q6jglU2ZphA==", "requires": { "dotenv": "16.4.5", "dotenv-expand": "10.0.0", @@ -16397,135 +14824,135 @@ "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==" }, "@opentelemetry/api-logs": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.49.1.tgz", - "integrity": "sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.50.0.tgz", + "integrity": "sha512-JdZuKrhOYggqOpUljAq4WWNi5nB10PmgoF0y2CvedLGXd0kSawb/UBnWT8gg1ND3bHCNHStAIVT0ELlxJJRqrA==", "requires": { "@opentelemetry/api": "^1.0.0" } }, "@opentelemetry/auto-instrumentations-node": { - "version": "0.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.43.0.tgz", - "integrity": "sha512-2WvHUSi/QVeVG8ObPD0Ls6WevfIbQjspxIQRuHaQFWXhmEwy/MsEcoQUjbNKXwO5516aS04GTydKEoRKsMwhdA==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.44.0.tgz", + "integrity": "sha512-wd24SiEsxDLbKslvUvyzOesNXhjjxV52Co5parTYXuMixil3SNNgon1WosbQo65jFzuvbASAMZNmc1MjtVp+ng==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/instrumentation-amqplib": "^0.35.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.39.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.39.1", - "@opentelemetry/instrumentation-bunyan": "^0.36.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.36.0", - "@opentelemetry/instrumentation-connect": "^0.34.0", - "@opentelemetry/instrumentation-cucumber": "^0.4.0", - "@opentelemetry/instrumentation-dataloader": "^0.7.0", - "@opentelemetry/instrumentation-dns": "^0.34.0", - "@opentelemetry/instrumentation-express": "^0.36.1", - "@opentelemetry/instrumentation-fastify": "^0.34.0", - "@opentelemetry/instrumentation-fs": "^0.10.0", - "@opentelemetry/instrumentation-generic-pool": "^0.34.0", - "@opentelemetry/instrumentation-graphql": "^0.38.1", - "@opentelemetry/instrumentation-grpc": "^0.49.1", - "@opentelemetry/instrumentation-hapi": "^0.35.0", - "@opentelemetry/instrumentation-http": "^0.49.1", - "@opentelemetry/instrumentation-ioredis": "^0.38.0", - "@opentelemetry/instrumentation-knex": "^0.34.0", - "@opentelemetry/instrumentation-koa": "^0.38.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.35.0", - "@opentelemetry/instrumentation-memcached": "^0.34.0", - "@opentelemetry/instrumentation-mongodb": "^0.41.0", - "@opentelemetry/instrumentation-mongoose": "^0.36.0", - "@opentelemetry/instrumentation-mysql": "^0.36.0", - "@opentelemetry/instrumentation-mysql2": "^0.36.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.35.0", - "@opentelemetry/instrumentation-net": "^0.34.0", - "@opentelemetry/instrumentation-pg": "^0.39.1", - "@opentelemetry/instrumentation-pino": "^0.36.0", - "@opentelemetry/instrumentation-redis": "^0.37.0", - "@opentelemetry/instrumentation-redis-4": "^0.37.0", - "@opentelemetry/instrumentation-restify": "^0.36.0", - "@opentelemetry/instrumentation-router": "^0.35.0", - "@opentelemetry/instrumentation-socket.io": "^0.37.0", - "@opentelemetry/instrumentation-tedious": "^0.8.0", - "@opentelemetry/instrumentation-winston": "^0.35.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.28.7", - "@opentelemetry/resource-detector-aws": "^1.4.0", - "@opentelemetry/resource-detector-container": "^0.3.7", - "@opentelemetry/resource-detector-gcp": "^0.29.7", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/instrumentation-amqplib": "^0.36.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.40.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.40.0", + "@opentelemetry/instrumentation-bunyan": "^0.37.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.37.0", + "@opentelemetry/instrumentation-connect": "^0.35.0", + "@opentelemetry/instrumentation-cucumber": "^0.5.0", + "@opentelemetry/instrumentation-dataloader": "^0.8.0", + "@opentelemetry/instrumentation-dns": "^0.35.0", + "@opentelemetry/instrumentation-express": "^0.37.0", + "@opentelemetry/instrumentation-fastify": "^0.35.0", + "@opentelemetry/instrumentation-fs": "^0.11.0", + "@opentelemetry/instrumentation-generic-pool": "^0.35.0", + "@opentelemetry/instrumentation-graphql": "^0.39.0", + "@opentelemetry/instrumentation-grpc": "^0.50.0", + "@opentelemetry/instrumentation-hapi": "^0.36.0", + "@opentelemetry/instrumentation-http": "^0.50.0", + "@opentelemetry/instrumentation-ioredis": "^0.39.0", + "@opentelemetry/instrumentation-knex": "^0.35.0", + "@opentelemetry/instrumentation-koa": "^0.39.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.36.0", + "@opentelemetry/instrumentation-memcached": "^0.35.0", + "@opentelemetry/instrumentation-mongodb": "^0.42.0", + "@opentelemetry/instrumentation-mongoose": "^0.37.0", + "@opentelemetry/instrumentation-mysql": "^0.37.0", + "@opentelemetry/instrumentation-mysql2": "^0.37.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.36.0", + "@opentelemetry/instrumentation-net": "^0.35.0", + "@opentelemetry/instrumentation-pg": "^0.40.0", + "@opentelemetry/instrumentation-pino": "^0.37.0", + "@opentelemetry/instrumentation-redis": "^0.38.0", + "@opentelemetry/instrumentation-redis-4": "^0.38.0", + "@opentelemetry/instrumentation-restify": "^0.37.0", + "@opentelemetry/instrumentation-router": "^0.36.0", + "@opentelemetry/instrumentation-socket.io": "^0.38.0", + "@opentelemetry/instrumentation-tedious": "^0.9.0", + "@opentelemetry/instrumentation-winston": "^0.36.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.28.8", + "@opentelemetry/resource-detector-aws": "^1.4.1", + "@opentelemetry/resource-detector-container": "^0.3.8", + "@opentelemetry/resource-detector-gcp": "^0.29.8", "@opentelemetry/resources": "^1.12.0", - "@opentelemetry/sdk-node": "^0.49.1" + "@opentelemetry/sdk-node": "^0.50.0" } }, "@opentelemetry/context-async-hooks": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.22.0.tgz", - "integrity": "sha512-Nfdxyg8YtWqVWkyrCukkundAjPhUXi93JtVQmqDT1mZRVKqA7e2r7eJCrI+F651XUBMp0hsOJSGiFk3QSpaIJw==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.23.0.tgz", + "integrity": "sha512-wazGJZDRevibOJ+VgyrT+9+8sybZAxpZx2G7vy30OAtk92OpZCg7HgNxT11NUx0VBDWcRx1dOatMYGOVplQ7QA==", "requires": {} }, "@opentelemetry/core": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.22.0.tgz", - "integrity": "sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.23.0.tgz", + "integrity": "sha512-hdQ/a9TMzMQF/BO8Cz1juA43/L5YGtCSiKoOHmrTEf7VMDAZgy8ucpWx3eQTnQ3gBloRcWtzvcrMZABC3PTSKQ==", "requires": { - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/exporter-prometheus": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.49.1.tgz", - "integrity": "sha512-FgzGl6OH22f+Wb1dh/TnoQSnZE2SCADhHx06nMqxivSqRJ9t3AhUdMsEOFt2IMjZClE705pcsLHk10BCJ79vsA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.50.0.tgz", + "integrity": "sha512-6jBrGqzpU1b2gCPUWTSSW+G3ejbZRx9SYhhFg0MO6v8R51mcln9KH6oIdTDrA+3Ie3L18bpygKrIWA9VPWEifg==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-metrics": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-metrics": "1.23.0" } }, "@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.49.1.tgz", - "integrity": "sha512-Zbd7f3zF7fI2587MVhBizaW21cO/SordyrZGtMtvhoxU6n4Qb02Gx71X4+PzXH620e0+JX+Pcr9bYb1HTeVyJA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.50.0.tgz", + "integrity": "sha512-w/NF4TrwHxx+Uz1M0rCOSVr6KgcoQPv3zF9JRqcebY2euD7ddWnLP0hE8JavyA1uq4UchnMp9faAk9n7hTCePw==", "requires": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" } }, "@opentelemetry/exporter-trace-otlp-http": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.49.1.tgz", - "integrity": "sha512-KOLtZfZvIrpGZLVvblKsiVQT7gQUZNKcUUH24Zz6Xbi7LJb9Vt6xtUZFYdR5IIjvt47PIqBKDWUQlU0o1wAsRw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.50.0.tgz", + "integrity": "sha512-L7OtIMT7MsFqkmhbQlPBGRXt7152VN5esHpQEJYIBFedOEo3Da+yHpu5ojMZtPzpIvSpB5Xr5lnJUjJCbkttCA==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" } }, "@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.49.1.tgz", - "integrity": "sha512-n8ON/c9pdMyYAfSFWKkgsPwjYoxnki+6Olzo+klKfW7KqLWoyEkryNkbcMIYnGGNXwdkMIrjoaP0VxXB26Oxcg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.50.0.tgz", + "integrity": "sha512-vavD9Ow6yOLiD+ocuS/oeciCsXNdsN41aYUrEljNaLXogvnkfMhJ+JLAhOnRSpzlVtRp7Ciw2BYGdYSebR0OsA==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", - "@opentelemetry/otlp-proto-exporter-base": "0.49.1", - "@opentelemetry/otlp-transformer": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", + "@opentelemetry/otlp-proto-exporter-base": "0.50.0", + "@opentelemetry/otlp-transformer": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" } }, "@opentelemetry/exporter-zipkin": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.22.0.tgz", - "integrity": "sha512-XcFs6rGvcTz0qW5uY7JZDYD0yNEXdekXAb6sFtnZgY/cHY6BQ09HMzOjv9SX+iaXplRDcHr1Gta7VQKM1XXM6g==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.23.0.tgz", + "integrity": "sha512-2LOGvNUGONuIcWhynFaJorVyqv03uZkURScciLmOxvBf2lWTNPEj77br1dCpShIWBM+YlrH7Tc+JXAs+GC7DqA==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/host-metrics": { @@ -16538,11 +14965,11 @@ } }, "@opentelemetry/instrumentation": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.49.1.tgz", - "integrity": "sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.50.0.tgz", + "integrity": "sha512-bhGhbJiZKpuu7wTaSak4hyZcFPlnDeuSF/2vglze8B4w2LubcSbbOnkVTzTs5SXtzh4Xz8eRjaNnAm+u2GYufQ==", "requires": { - "@opentelemetry/api-logs": "0.49.1", + "@opentelemetry/api-logs": "0.50.0", "@types/shimmer": "^1.0.2", "import-in-the-middle": "1.7.1", "require-in-the-middle": "^7.1.1", @@ -16551,21 +14978,21 @@ } }, "@opentelemetry/instrumentation-amqplib": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.35.0.tgz", - "integrity": "sha512-rb3hIWA7f0HXpXpfElnGC6CukRxy58/OJ6XYlTzpZJtNJPao7BuobZjkQEscaRYhUzgi7X7R1aKkIUOTV5JFrg==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.36.0.tgz", + "integrity": "sha512-qIXHiYbU8hfFHtI/y5ntv2k4g1stlQBHXu383xCqFdg3XbkLuKIyQ+QnnP8Ybb12qh+hzuXstEpFiF0dSmuhZA==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-aws-lambda": { - "version": "0.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.39.0.tgz", - "integrity": "sha512-D+oG/hIBDdwCNq7Y6BEuddjcwDVD0C8NhBE7A85mRZ9RLG0bKoWrhIdVvbpqEoa0U5AWe9Y98RX4itNg7WTy4w==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.40.0.tgz", + "integrity": "sha512-DF1Ja1AQNV8K5tAW4STcI0KKuu1IDzCM1aa66UDbJwZfEMq0QedzKS7FjoIB+wV4Mk8+WJQRtzISFmjDiquxmw==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/propagator-aws-xray": "^1.3.1", "@opentelemetry/resources": "^1.8.0", "@opentelemetry/semantic-conventions": "^1.0.0", @@ -16573,266 +15000,266 @@ } }, "@opentelemetry/instrumentation-aws-sdk": { - "version": "0.39.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.39.1.tgz", - "integrity": "sha512-QnvIMVpzRYqQHSXydGUksbhBjPbMyHSUBwi6ocN7gEXoI711+tIY3R1cfRutl0u3M67A/fAvPI3IgACfJaFORg==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.40.0.tgz", + "integrity": "sha512-USjyLBbpYIp4YPcTqcvspSmckZ/i2r/LSBkcoiIfCGNsbWyEK3mBDUTV3ULL6cuA0zGvYAabNDFZ2qm5vCvAuw==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/propagation-utils": "^0.30.7", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/propagation-utils": "^0.30.8", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-bunyan": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.36.0.tgz", - "integrity": "sha512-sHD5BSiqSrgWow7VmugEFzV8vGdsz5m+w1v9tK6YwRzuAD7vbo57chluq+UBzIqStoCH+0yOzRzSALH7hrfffg==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.37.0.tgz", + "integrity": "sha512-NKHuTIn1JrxfKZ8sVZjwT/bD3wyijkipiWzBvvxGj8pJJJNP5shVHWcEJhk5tQsfKQc4LproTAPdklex1o5/ag==", "requires": { - "@opentelemetry/api-logs": "^0.49.1", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/api-logs": "^0.50.0", + "@opentelemetry/instrumentation": "^0.50.0", "@types/bunyan": "1.8.9" } }, "@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.36.0.tgz", - "integrity": "sha512-gMfxzryOIP/mvSLXBJp/QxSr2NvS+cC1dkIXn+aSOzYoU1U3apeF3nAyuikmY9dRCQDV7wHPslqbi+pCmd4pAQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.37.0.tgz", + "integrity": "sha512-3owM6+vqu+akYWnRY4hTmzDPoa3NrX++pmRMwJZPd6+zPlu7NTXPFpjxIhM0IzhGUbv3H5V9ULQw/CUUHTeh2g==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-connect": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.34.0.tgz", - "integrity": "sha512-PJO99nfyUp3JSoBMhwZsOQDm/XKfkb/QQ8YTsNX4ZJ28phoRcNLqe36mqIMp80DKmKAX4xkxCAyrSYtW8QqZxA==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.35.0.tgz", + "integrity": "sha512-COcQdFNVXyqaVGSuBgIyLu1qZUebjWyQhMAdMOOulCRznhquoyffeymQDZ0LB8XjD7kkG8if8PbykL4b8tjbNA==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/connect": "3.4.36" } }, "@opentelemetry/instrumentation-cucumber": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.4.0.tgz", - "integrity": "sha512-n53QvozzgMS9imEclow2nBYJ/jtZlZqiKIqDUi2/g0nDi08F555JhDS03d/Z+4NJxbu7bDLAg12giCV9KZN/Jw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.5.0.tgz", + "integrity": "sha512-81id1pd8/bLAJT5+kr0V9iMKNCImE+LAX1E02qFui0lyTP9IGk2gQXPf3U68fyHxxJOjUqr1VPkDL/Pcjrfrfg==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-dataloader": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.7.0.tgz", - "integrity": "sha512-sIaevxATJV5YaZzBTTcTaDEnI+/1vxYs+lVk1honnvrEAaP0FA9C/cFrQEN0kP2BDHkHRE/t6y5lGUqusi/h3A==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.8.0.tgz", + "integrity": "sha512-1LHr3W6BZm2VyoFo2boYD1wUlDD3V4dIFoCVg+PwQgHqampiJbl8raEMaa66RNl1LOq+vw4LMgbBqWIUog1Few==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" } }, "@opentelemetry/instrumentation-dns": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.34.0.tgz", - "integrity": "sha512-3tmXdvrzHQ7S3v82Cm36PTYLtgg2+hVm00K1xB3uzP08GEo9w/F8DW4me9z6rDroVGiLIg621RZ6dzjBcmmFCg==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.35.0.tgz", + "integrity": "sha512-ci7hIJcp8AWsJntRMt397VGYh2Isou/u8nDX/fPa9nlbIR6VssgcHTpAV6j98Q2+N3/kTPcdTs/WtLI2g8Wwgg==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "semver": "^7.5.4" } }, "@opentelemetry/instrumentation-express": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.36.1.tgz", - "integrity": "sha512-ltIE4kIMa+83QjW/p7oe7XCESF29w3FQ9/T1VgShdX7fzm56K2a0xfEX1vF8lnHRGERYxIWX9D086C6gJOjVGA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.37.0.tgz", + "integrity": "sha512-ts6yI7huK41clpcSHqP0NhZbikO4nWFHu/GF2+88IMMKTUBw8IJPnrvXeOUSg9B60uyGaftxfKvfKENtevBFig==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0" } }, "@opentelemetry/instrumentation-fastify": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.34.0.tgz", - "integrity": "sha512-2Qu66XBkfJ8tr6H+RHBTyw/EX73N9U7pvNa49aonDnT9/mK58k7AKOscpRnKXOvHqc2YIdEPRcBIWxhksPFZVA==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.35.0.tgz", + "integrity": "sha512-lUHj4lYmJswKHR0twg3KFSiIkfTlF4tyh+VwyrrNDYy7yGDqzftP3tP4coVdhT78RRp3DpcAGggbfrIJbdP9pA==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-fs": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.10.0.tgz", - "integrity": "sha512-XtMoNINVsIQTQHjtxe7A0Lng96wxA5DSD5CYVVvpquG6HJRdZ4xNe9DTU03YtoEFqlN9qTfvGb/6ILzhKhiG8g==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.11.0.tgz", + "integrity": "sha512-vVhrKNYc8S/tN5kRsuGDNaCdDVFoMSGFSrOKt9NaB53edWNB2psoUwR+aHUg0U+87zVnXMvRf2e0yDssg3WMcg==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-generic-pool": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.34.0.tgz", - "integrity": "sha512-jdI7tfVVwZJuTu4j2kAvJtx4wlEQKIXSZnZG4RdqRHc56KqQQDuVTBLvUgmDXvnSVclH9ayf4oaAV08R9fICtw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.35.0.tgz", + "integrity": "sha512-2Vpy4Vj3RaeAN+uxG9kI2SnKKoitgcOmpD/GR5xjNKQEv2av5X1Gor71ZH2Ec3NjB9HpOLp9QRJg+misFnyYZQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-graphql": { - "version": "0.38.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.38.1.tgz", - "integrity": "sha512-mSt4ztn3EVlLtZJ+tDEqq5GUEYdY8cbTT9SeVJFmXSfdSQkPZn0ovo/dRe6dUcplM60gg4w+llw8SZuQN0iZfQ==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.39.0.tgz", + "integrity": "sha512-TUe4DJJ2S89L45eO3esGrQKvIGVasj7gY4Nk7PecxkgeVnKO5gJ41mlbhUOF44vXxYQq53470FNnhe396+JlBQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" } }, "@opentelemetry/instrumentation-grpc": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.49.1.tgz", - "integrity": "sha512-f8mQjFi5/PiP4SK3VDU1/3sUUgs6exMtBgcnNycgCKgN40htiPT+MuDRwdRnRMNI/4vNQ7p1/5r4Q5oN0GuRBw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.50.0.tgz", + "integrity": "sha512-/ZFuvHtrHyxfRJX5CJ8yPKokAIcvTbIJAoR4AN+gBq1YqecWuCr4XG52p5YU5qDrbwOBtShOrC8d4GdFZOky6Q==", "requires": { - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/instrumentation-hapi": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.35.0.tgz", - "integrity": "sha512-j7q99aTLHfjNKW94qJnEaDatgz+q2psTKs7lxZO4QHRnoDltDk39a44/+AkI1qBJNw5xyLjrApqkglfbWJ2abg==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.36.0.tgz", + "integrity": "sha512-0NnXuRF89Windosn+iUpq5Fn/Foy8PMJxtLfe6CakDJIUGPj/g1+erz5irqSOc0P5mM3rEqKC/cYCoSIMKo/eA==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/hapi__hapi": "20.0.13" } }, "@opentelemetry/instrumentation-http": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.49.1.tgz", - "integrity": "sha512-Yib5zrW2s0V8wTeUK/B3ZtpyP4ldgXj9L3Ws/axXrW1dW0/mEFKifK50MxMQK9g5NNJQS9dWH7rvcEGZdWdQDA==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.50.0.tgz", + "integrity": "sha512-bsd6Nv0FtN9C6M6vX/kgPzvJY9UhJc4CZZNvqDbsfVQv3/MWvPrYgthf41AhrehqeDnpfn/QGzNKtdWUduGanQ==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/semantic-conventions": "1.22.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/semantic-conventions": "1.23.0", "semver": "^7.5.2" } }, "@opentelemetry/instrumentation-ioredis": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.38.0.tgz", - "integrity": "sha512-c9nQFhRjFAtpInTks7z5v9CiOCiR8U9GbIhIv0TLEJ/r0wqdKNLfLZzCrr9XQ9WasxeOmziLlPFhpRBAd9Q4oA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.39.0.tgz", + "integrity": "sha512-Xh0d6LajMXr4zQRxjub/eiRtqQcFBDfoP7mIwmkirA6wBxq+4fEe2QrEMMEyUCPl1MDMsLS8YKUu2r95W/0iUw==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/ioredis4": "npm:@types/ioredis@^4.28.10" } }, "@opentelemetry/instrumentation-knex": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.34.0.tgz", - "integrity": "sha512-6kZOEvNJOylTQunU5zSSi4iTuCkwIL9nwFnZg7719p61u3d6Qj3X4xi9su46VE3M0dH7vEoxUW+nb/0ilm+aZg==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.35.0.tgz", + "integrity": "sha512-pIfOn3HSPBFHWd55rS2YPLXq4pBGjLbKwrNnd0x8/nM5TPNy3GueSaruGvMZwkQ68Z1nBB42hLXRKozgErBlqw==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-koa": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.38.0.tgz", - "integrity": "sha512-lQujF4I3wdcrOF14miCV2pC72H+OJKb2LrrmTvTDAhELQDN/95v0doWgT9aHybUGkaAeB3QG4d09sved548TlA==", + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.39.0.tgz", + "integrity": "sha512-eSqPzDykJVF9AcOuQvYqYCA/TN8tnU9/RYgrdPclaQcH6nfp0ZbQqLsAMGOwatfwJ8p06FAj+koPBy5NQNFMow==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0", "@types/koa": "2.14.0", "@types/koa__router": "12.0.3" } }, "@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.35.0.tgz", - "integrity": "sha512-wCXe+iCF7JweMgY3blLM2Y1G0GSwLEeSA61z/y1UwzvBLEEXt7vL6qOl2mkNcUL9ZbLDS+EABatBH+vFO6DV5Q==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.36.0.tgz", + "integrity": "sha512-wnqHo4CJfMmXMGJonT4kPGwiEAFlO1a9IrWxVoc1+3B+yD9wKFRby+dVy0s5Ddarwl0SD5ljUZklXMgcEQjlQQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" } }, "@opentelemetry/instrumentation-memcached": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.34.0.tgz", - "integrity": "sha512-RleFfaag3Evg4pTzHwDBwo1KiFgnCtiT4V6MQRRHadytNGdpcL+Ynz32ydDdiOXeadt7xpRI7HSvBy0quGTXSw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.35.0.tgz", + "integrity": "sha512-UrdzoXShPEfJ2ppjCNHcioHF/Bcd3Jr6pEzayUuRfIXiurOhsHlTnUHPH1rHDunEOcR8FGUicuerEDdDDDU+dA==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/memcached": "^2.2.6" } }, "@opentelemetry/instrumentation-mongodb": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.41.0.tgz", - "integrity": "sha512-DlSH0oyEuTW5gprCUppb0Qe3pK3cpUUFW5eTmayWNyICI1LFunwtcrULTNv6UiThD/V5ykAf/GGGEa7KFAmkog==", + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.42.0.tgz", + "integrity": "sha512-pnUewN9r9Kw3on+6hM24COP4X4zJP5PafXCd1b/qka45Savyle7IaJyJODWVHkX6hN8ERsG853cNsYrsO/f/lQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/sdk-metrics": "^1.9.1", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-mongoose": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.36.0.tgz", - "integrity": "sha512-UelQ8dLQRLTdck3tPJdZ17b+Hk9usLf1cY2ou5THAaZpulUdpg62Q9Hx2RHRU71Rp2/YMDk25og7GJhuWScfEA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.37.0.tgz", + "integrity": "sha512-VBGfiERp2fxE35OjUFAlfAOKkPOwYx3FqqV8LG7mq6SGK6yhOaU7RvE1b//bhGWoVYohVyxaBIMRVZr4xpmOJQ==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-mysql": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.36.0.tgz", - "integrity": "sha512-2mt/032SLkiuddzMrq3YwM0bHksXRep69EzGRnBfF+bCbwYvKLpqmSFqJZ9T3yY/mBWj+tvdvc1+klXGrh2QnQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.37.0.tgz", + "integrity": "sha512-swp9B4oIOcduYfOslc7tbFDjOCgKlcSdVOO+emVnaOEvYG19NcGdc82WH6Zqmctl5oOEBVeKHS+wgjjbwBiMWA==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/mysql": "2.15.22" } }, "@opentelemetry/instrumentation-mysql2": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.36.0.tgz", - "integrity": "sha512-F63lKcl/R+if2j5Vz66c2/SLXQEtLlFkWTmYb8NQSgmcCaEKjML4RRRjZISIT4IBwdpanJ2qmNuXVM6MYqhBXw==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.37.0.tgz", + "integrity": "sha512-KaXZr8B13IovmSN5Xe5qblp34VPsLaooivHMnhOwj2so7ivB1PcGGkesWH5knXC/9iQryiIFXwSDdHrd4R5iXQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@opentelemetry/sql-common": "^0.40.0" } }, "@opentelemetry/instrumentation-nestjs-core": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.35.0.tgz", - "integrity": "sha512-INKA7CIOteTSRVxP7SQaFby11AYU3uezI93xDaDRGY4TloXNVoyw5n6UmcVJU4yDn6xY2r7zZ2SVHvblUc21/g==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.36.0.tgz", + "integrity": "sha512-ku1HdUWFwd6ajJh0pTPwDmcRZF8sbjLCTQXAjUTrJEfmiXavKHwFBZnyF9/5NWedK8FrfZjZ7+hH9heiDigMNQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-net": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.34.0.tgz", - "integrity": "sha512-gjybNOQQqbXmD1qVHNO2qBJI4V6p3QQ7xKg3pnC/x7wRdxn+siLQj7QIVxW85C3mymngoJJdRs6BwI3qPUfsPQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.35.0.tgz", + "integrity": "sha512-L5qw1G7vwxuePpuLKxpbIg49wH0brfgVhbMaMa09YX4HM6yj1KQRRGMMFxB00NQo2u42kuv/7F7AB/lWeG0yUA==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-pg": { - "version": "0.39.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.39.1.tgz", - "integrity": "sha512-pX5ujDOyGpPcrZlzaD3LJzmyaSMMMKAP+ffTHJp9vasvZJr+LifCk53TMPVUafcXKV/xX/IIkvADO+67M1Z25g==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.40.0.tgz", + "integrity": "sha512-z3v8OzfImnycykWgqIdS44aUZlRwq51yYIo8GfmiRBd8yyMl2ESQyv6z/IAWBWyT015IWGy3ZTijySe65P9J1w==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", - "@opentelemetry/semantic-conventions": "^1.0.0", + "@opentelemetry/instrumentation": "^0.50.0", + "@opentelemetry/semantic-conventions": "^1.22.0", "@opentelemetry/sql-common": "^0.40.0", "@types/pg": "8.6.1", "@types/pg-pool": "2.0.4" @@ -16851,125 +15278,125 @@ } }, "@opentelemetry/instrumentation-pino": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.36.0.tgz", - "integrity": "sha512-oEz+BJEYRBMAUu7MVJFJhhlsBuwLaUGjbJciKZRIeGX+fUtgcbQGV+a2Ris9jR3yFzWZrYg0aNBSCbGqvPCtMQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.37.0.tgz", + "integrity": "sha512-5V8Cz25pNbbiz0ewWuuKmSCygzoqzgDB3EeliixCnxOzu5Fp7nVKfHlQ775uxylLuODCYwwgVhU6L1Sif4j4AA==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" } }, "@opentelemetry/instrumentation-redis": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.37.0.tgz", - "integrity": "sha512-9G0T74kheu37k+UvyBnAcieB5iowxska3z2rhUcSTL8Cl0y/CvMn7sZ7txkUbXt0rdX6qeEUdMLmbsY2fPUM7Q==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.38.0.tgz", + "integrity": "sha512-PzcYLkPvClE7NyjsuqvjCWnCO8wt6ZaYHM6AB9Ga9EQKFh1oNEr2dXCnqVHMuxjzbTWF2+c86yPZAdCEfyKfmA==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-redis-4": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.37.0.tgz", - "integrity": "sha512-WNO+HALvPPvjbh7UEEIuay0Z0d2mIfSCkBZbPRwZttDGX6LYGc2WnRgJh3TnYqjp7/y9IryWIbajAFIebj1OBA==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.38.0.tgz", + "integrity": "sha512-ugDb2txE2o76zwasSzOpmlFlUUnQu4MT2WpO+UmmS5sN4pClU+zsZy0QxjHB03PUJMLCRfsbZf2ez0jkpm7yag==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/redis-common": "^0.36.1", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-restify": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.36.0.tgz", - "integrity": "sha512-QbOh8HpnnRn4xxFXX77Gdww6M78yx7dRiIKR6+H3j5LH5u6sYckTXw3TGPSsXsaM4DQHy0fOw15sAcJoWkC+aQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.37.0.tgz", + "integrity": "sha512-eJyfLsV243Jx4wL8FSZ08Q9Ih6ojOIh6csAJJSNqdFtSMBBQXKVcTw5g2nOi2I2d0AmPNYhaA5H0FLLz6ZIMXw==", "requires": { "@opentelemetry/core": "^1.8.0", - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-router": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.35.0.tgz", - "integrity": "sha512-MdxGJuNTIy/2qDI8yow6cRBQ87m6O//VuHIlawe8v0x1NsTOSwS72xm+BzTuY9D0iMqiJUiTlE3dBs8DA91MTw==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.36.0.tgz", + "integrity": "sha512-HhVw2OuhlHlbzq0X3rUf3ilyBcM1UsFABESKU6NF1MlVWm1OlZDuZLmlk5EvuU/u9cjduXp61jVj5s4OCW+knQ==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-socket.io": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.37.0.tgz", - "integrity": "sha512-aIztxmx/yis/goEndnoITrZvDDr1GdCtlsWo9ex7MhUIjqq5nJbTuyigf3GmU86XFFhSThxfQuJ9DpJyPxfBfA==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.38.0.tgz", + "integrity": "sha512-gls1SrTxTy2swwtrn2O+b8wK2MmnQTxEkmpZ6YPh+52mlcvOpW35L+Wfx/aM/hWOw8Za1Wlns710UBH4p88M2Q==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0" } }, "@opentelemetry/instrumentation-tedious": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.8.0.tgz", - "integrity": "sha512-BBRW8+Qm2PLNkVMynr3Q7L4xCAOCOs0J9BJIJ8ZGoatW42b2H4qhMhq35jfPDvEL5u5azxHDapmUVYrDJDjAfA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.9.0.tgz", + "integrity": "sha512-VmrKxXmfrzc0Sa5AtDMB96M/lr8cOiU61nYVpY0VtS4QPD7cqqwjeBzBvZpvSu1OZS/zbYg+B9ILOl2cHiG5+g==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1", + "@opentelemetry/instrumentation": "^0.50.0", "@opentelemetry/semantic-conventions": "^1.0.0", "@types/tedious": "^4.0.10" } }, "@opentelemetry/instrumentation-winston": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.35.0.tgz", - "integrity": "sha512-ymcuA3S2flnLmH1GS0105H91iDLap8cizOCaLMCp7Xz7r4L+wFf1zfix9M+iSkxcPFshHRt8LFA/ELXw51nk0g==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.36.0.tgz", + "integrity": "sha512-NTuGpUA9AGC7EmKQyMn3ABiqJ3XvHihRcxdvqhs8E72nHmIU3n6om+DCYYE3fpEQwbC9N14ir8fqA5Oxn0Ynag==", "requires": { - "@opentelemetry/instrumentation": "^0.49.1" + "@opentelemetry/instrumentation": "^0.50.0" } }, "@opentelemetry/otlp-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz", - "integrity": "sha512-z6sHliPqDgJU45kQatAettY9/eVF58qVPaTuejw9YWfSRqid9pXPYeegDCSdyS47KAUgAtm+nC28K3pfF27HWg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.50.0.tgz", + "integrity": "sha512-JUmjmrCmE1/fc4LjCQMqLfudgSl5OpUkzx7iA94b4jgeODM7zWxUoVXL7/CT7fWf47Cn+pmKjMvTCSESqZZ3mA==", "requires": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" } }, "@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.49.1.tgz", - "integrity": "sha512-DNDNUWmOqtKTFJAyOyHHKotVox0NQ/09ETX8fUOeEtyNVHoGekAVtBbvIA3AtK+JflP7LC0PTjlLfruPM3Wy6w==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.50.0.tgz", + "integrity": "sha512-J500AczSD7xEsjXpwNzSh5HQqxW73PT3CCNsi1VEWCE+8UPgVfkHYIGRHGoch35DV+CMe1svbi7gAk3e5eCSVA==", "requires": { "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", "protobufjs": "^7.2.3" } }, "@opentelemetry/otlp-proto-exporter-base": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.49.1.tgz", - "integrity": "sha512-x1qB4EUC7KikUl2iNuxCkV8yRzrSXSyj4itfpIO674H7dhI7Zv37SFaOJTDN+8Z/F50gF2ISFH9CWQ4KCtGm2A==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-proto-exporter-base/-/otlp-proto-exporter-base-0.50.0.tgz", + "integrity": "sha512-hlbn3eZbhxoK79Sq1ddj1f7qcx+PzsPQC/SFpJvaWgTaqacCbqJmpzWDKfRRCAC7iGX2Hj/sgpf8vysazqyMOw==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/otlp-exporter-base": "0.49.1", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/otlp-exporter-base": "0.50.0", "protobufjs": "^7.2.3" } }, "@opentelemetry/otlp-transformer": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.49.1.tgz", - "integrity": "sha512-Z+koA4wp9L9e3jkFacyXTGphSWTbOKjwwXMpb0CxNb0kjTHGUxhYRN8GnkLFsFo5NbZPjP07hwAqeEG/uCratQ==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.50.0.tgz", + "integrity": "sha512-s0sl1Yfqd5q1Kjrf6DqXPWzErL+XHhrXOfejh4Vc/SMTNqC902xDsC8JQxbjuramWt/+hibfguIvi7Ns8VLolA==", "requires": { - "@opentelemetry/api-logs": "0.49.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-logs": "0.49.1", - "@opentelemetry/sdk-metrics": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0" + "@opentelemetry/api-logs": "0.50.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-logs": "0.50.0", + "@opentelemetry/sdk-metrics": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0" } }, "@opentelemetry/propagation-utils": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.7.tgz", - "integrity": "sha512-QkxOkuCQdq8YgJstEMF4ntSyr0ivCrcQc49uvO2pyccrniu2DwA+JD071aM4BXfNVSCeOuhIyW/3QPiZYl4zdA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.8.tgz", + "integrity": "sha512-ZKjsUm//SvL8I9JS+bJpAXGpe0Fb+sO+AiWS0fb7EKKLEm3GoNAO7CDMs8GMZBZ91ElR3tBjdUKf/9MzUdYHBA==", "requires": {} }, "@opentelemetry/propagator-aws-xray": { @@ -16981,19 +15408,19 @@ } }, "@opentelemetry/propagator-b3": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.22.0.tgz", - "integrity": "sha512-qBItJm9ygg/jCB5rmivyGz1qmKZPsL/sX715JqPMFgq++Idm0x+N9sLQvWFHFt2+ZINnCSojw7FVBgFW6izcXA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.23.0.tgz", + "integrity": "sha512-cZ6rl8y2bdxYQ4e+zP2CQ+QmuPebaLBLO1skjFpj3eEu7zar+6hBzUP3llMOUupkQeQSwXz+4c8dZ26OhYfG/g==", "requires": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" } }, "@opentelemetry/propagator-jaeger": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.22.0.tgz", - "integrity": "sha512-pMLgst3QIwrUfepraH5WG7xfpJ8J3CrPKrtINK0t7kBkuu96rn+HDYQ8kt3+0FXvrZI8YJE77MCQwnJWXIrgpA==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.23.0.tgz", + "integrity": "sha512-6iArixfgIl3ZgzeltQ5jyiKbjZygM+MbM84pXi1HL0Qs4x4Ck5rM6wEtjhZffFnlDMWEkEqrnM0xF6bTfbiMAQ==", "requires": { - "@opentelemetry/core": "1.22.0" + "@opentelemetry/core": "1.23.0" } }, "@opentelemetry/redis-common": { @@ -17002,37 +15429,37 @@ "integrity": "sha512-YjfNEr7DK1Ymc5H0bzhmqVvMcCs+PUEUerzrpTFdHfZxj3HpnnjZTIFKx/gxiL/sajQ8dxycjlreoYTVYKBXlw==" }, "@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.7.tgz", - "integrity": "sha512-7o/waBJ08JrKED4blHGyBPIN1HMM1KEvhbO1HmdA+tsUqsGwZdTjsdMKFW7hc1TvAu4AQEnuvMy/Q5OByVr95A==", + "version": "0.28.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.28.8.tgz", + "integrity": "sha512-njdK7S90OX99H0nlDh45vmGU3Bn46JSNjciU18NefyU6R3Dq5ZUU13DWxSlrCBWAiH/+SFJyHQMFsXRnTOFp6w==", "requires": { "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" } }, "@opentelemetry/resource-detector-aws": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.0.tgz", - "integrity": "sha512-Cn8eQ/heLqrNrPuHGG7xUkk//VQt4hzVIPurmLlCI0wrDV6HR+yykBvRkJBuSdLzbjeQ/qNbGel9OvTmA6PBQA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.4.1.tgz", + "integrity": "sha512-QsPJwXDxlt+IWQazST7renk9KDOAK3hsywb0Mw6gEwgEoe12EvdVcT+mCknTc+hu5WzxiTmFMtnn1TWab7To1g==", "requires": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" } }, "@opentelemetry/resource-detector-container": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.7.tgz", - "integrity": "sha512-AYqwffGVuGLuzzVOQMLNHTztwyvsep9noxN9HTQ/grwmJSWZ6851kNx+W735K7v6GZEDmXeLpBn+J3TeqKQUJA==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.3.8.tgz", + "integrity": "sha512-YusLYSo8Rr1yKEhakaJ2XgbfbLses5t8YsSRHhO0b6SLliZOFq9ymxoWgtuwyabNCqMMJJYuvrE3Nq0AL6sbcQ==", "requires": { "@opentelemetry/resources": "^1.0.0", - "@opentelemetry/semantic-conventions": "^1.0.0" + "@opentelemetry/semantic-conventions": "^1.22.0" } }, "@opentelemetry/resource-detector-gcp": { - "version": "0.29.7", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.7.tgz", - "integrity": "sha512-uUHKfoOgBCZCEPCU6FWnRrbYuz1miaeIfos0Xe38YuR06vQvddhqZ0tewYunJpfECfKEcjSjY0eDe2QIRLMkXw==", + "version": "0.29.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.8.tgz", + "integrity": "sha512-jvFtsnrQI7CLDxehp6BxtudbZ3a0HZ+Dj/vFy251280efc15gQdlvFUqXbKBw6ODt/3o38ilw8e0qePJbm2eAg==", "requires": { "@opentelemetry/core": "^1.0.0", "@opentelemetry/resources": "^1.0.0", @@ -17041,80 +15468,80 @@ } }, "@opentelemetry/resources": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.22.0.tgz", - "integrity": "sha512-+vNeIFPH2hfcNL0AJk/ykJXoUCtR1YaDUZM+p3wZNU4Hq98gzq+7b43xbkXjadD9VhWIUQqEwXyY64q6msPj6A==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.23.0.tgz", + "integrity": "sha512-iPRLfVfcEQynYGo7e4Di+ti+YQTAY0h5mQEUJcHlU9JOqpb4x965O6PZ+wMcwYVY63G96KtdS86YCM1BF1vQZg==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/sdk-logs": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.49.1.tgz", - "integrity": "sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.50.0.tgz", + "integrity": "sha512-PeUEupBB29p9nlPNqXoa1PUWNLsZnxG0DCDj3sHqzae+8y76B/A5hvZjg03ulWdnvBLYpnJslqzylG9E0IL87g==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0" } }, "@opentelemetry/sdk-metrics": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.22.0.tgz", - "integrity": "sha512-k6iIx6H3TZ+BVMr2z8M16ri2OxWaljg5h8ihGJxi/KQWcjign6FEaEzuigXt5bK9wVEhqAcWLCfarSftaNWkkg==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.23.0.tgz", + "integrity": "sha512-4OkvW6+wST4h6LFG23rXSTf6nmTf201h9dzq7bE0z5R9ESEVLERZz6WXwE7PSgg1gdjlaznm1jLJf8GttypFDg==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", "lodash.merge": "^4.6.2" } }, "@opentelemetry/sdk-node": { - "version": "0.49.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.49.1.tgz", - "integrity": "sha512-feBIT85ndiSHXsQ2gfGpXC/sNeX4GCHLksC4A9s/bfpUbbgbCSl0RvzZlmEpCHarNrkZMwFRi4H0xFfgvJEjrg==", + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.50.0.tgz", + "integrity": "sha512-LhIXHnvcnhRYcPwG9VG4G6lJ7x4ElYF6UYHHmXA7e4ZWzSUEFmAPfR1IBWv358aD1KwffcEBu7J6zeAR7lPZag==", "requires": { - "@opentelemetry/api-logs": "0.49.1", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.49.1", - "@opentelemetry/exporter-trace-otlp-http": "0.49.1", - "@opentelemetry/exporter-trace-otlp-proto": "0.49.1", - "@opentelemetry/exporter-zipkin": "1.22.0", - "@opentelemetry/instrumentation": "0.49.1", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/sdk-logs": "0.49.1", - "@opentelemetry/sdk-metrics": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", - "@opentelemetry/sdk-trace-node": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/api-logs": "0.50.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.50.0", + "@opentelemetry/exporter-trace-otlp-http": "0.50.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.50.0", + "@opentelemetry/exporter-zipkin": "1.23.0", + "@opentelemetry/instrumentation": "0.50.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/sdk-logs": "0.50.0", + "@opentelemetry/sdk-metrics": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", + "@opentelemetry/sdk-trace-node": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/sdk-trace-base": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz", - "integrity": "sha512-pfTuSIpCKONC6vkTpv6VmACxD+P1woZf4q0K46nSUvXFvOFqjBYKFaAMkKD3M1mlKUUh0Oajwj35qNjMl80m1Q==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.23.0.tgz", + "integrity": "sha512-PzBmZM8hBomUqvCddF/5Olyyviayka44O5nDWq673np3ctnvwMOvNrsUORZjKja1zJbwEuD9niAGbnVrz3jwRQ==", "requires": { - "@opentelemetry/core": "1.22.0", - "@opentelemetry/resources": "1.22.0", - "@opentelemetry/semantic-conventions": "1.22.0" + "@opentelemetry/core": "1.23.0", + "@opentelemetry/resources": "1.23.0", + "@opentelemetry/semantic-conventions": "1.23.0" } }, "@opentelemetry/sdk-trace-node": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.22.0.tgz", - "integrity": "sha512-gTGquNz7ue8uMeiWPwp3CU321OstQ84r7PCDtOaCicjbJxzvO8RZMlEC4geOipTeiF88kss5n6w+//A0MhP1lQ==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.23.0.tgz", + "integrity": "sha512-dwnin5Go2r6VzJZkVc9JBPupssWp7j2EFto+S7qRkwQ00WDykWeq3x2Skk7I1Jr448FeBSvGCQVPgV5e6s6O3w==", "requires": { - "@opentelemetry/context-async-hooks": "1.22.0", - "@opentelemetry/core": "1.22.0", - "@opentelemetry/propagator-b3": "1.22.0", - "@opentelemetry/propagator-jaeger": "1.22.0", - "@opentelemetry/sdk-trace-base": "1.22.0", + "@opentelemetry/context-async-hooks": "1.23.0", + "@opentelemetry/core": "1.23.0", + "@opentelemetry/propagator-b3": "1.23.0", + "@opentelemetry/propagator-jaeger": "1.23.0", + "@opentelemetry/sdk-trace-base": "1.23.0", "semver": "^7.5.2" } }, "@opentelemetry/semantic-conventions": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.22.0.tgz", - "integrity": "sha512-CAOgFOKLybd02uj/GhCdEeeBjOS0yeoDeo/CA7ASBSmenpZHAKGB3iDm/rv3BQLcabb/OprDEsSQ1y0P8A7Siw==" + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz", + "integrity": "sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg==" }, "@opentelemetry/sql-common": { "version": "0.40.0", @@ -17200,6 +15627,143 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "dependencies": { + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + } + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "dev": true, + "optional": true + }, "@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -17224,24 +15788,6 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, "@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -17263,38 +15809,147 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, + "@swc/core": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.14.tgz", + "integrity": "sha512-tHXg6OxboUsqa/L7DpsCcFnxhLkqN/ht5pCwav1HnvfthbiNIJypr86rNx4cUnQDJepETviSqBTIjxa7pSpGDQ==", + "devOptional": true, + "requires": { + "@swc/core-darwin-arm64": "1.4.14", + "@swc/core-darwin-x64": "1.4.14", + "@swc/core-linux-arm-gnueabihf": "1.4.14", + "@swc/core-linux-arm64-gnu": "1.4.14", + "@swc/core-linux-arm64-musl": "1.4.14", + "@swc/core-linux-x64-gnu": "1.4.14", + "@swc/core-linux-x64-musl": "1.4.14", + "@swc/core-win32-arm64-msvc": "1.4.14", + "@swc/core-win32-ia32-msvc": "1.4.14", + "@swc/core-win32-x64-msvc": "1.4.14", + "@swc/counter": "^0.1.2", + "@swc/types": "^0.1.5" + } + }, + "@swc/core-darwin-arm64": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.14.tgz", + "integrity": "sha512-8iPfLhYNspBl836YYsfv6ErXwDUqJ7IMieddV3Ey/t/97JAEAdNDUdtTKDtbyP0j/Ebyqyn+fKcqwSq7rAof0g==", + "dev": true, + "optional": true + }, + "@swc/core-darwin-x64": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.14.tgz", + "integrity": "sha512-9CqSj8uRZ92cnlgAlVaWMaJJBdxtNvCzJxaGj5KuIseeG6Q0l1g+qk8JcU7h9dAsH9saHTNwNFBVGKQo0W0ujg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm-gnueabihf": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.14.tgz", + "integrity": "sha512-mfd5JArPITTzMjcezH4DwMw+BdjBV1y25Khp8itEIpdih9ei+fvxOOrDYTN08b466NuE2dF2XuhKtRLA7fXArQ==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-gnu": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.14.tgz", + "integrity": "sha512-3Lqlhlmy8MVRS9xTShMaPAp0oyUt0KFhDs4ixJsjdxKecE0NJSV/MInuDmrkij1C8/RQ2wySRlV9np5jK86oWw==", + "dev": true, + "optional": true + }, + "@swc/core-linux-arm64-musl": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.14.tgz", + "integrity": "sha512-n0YoCa64TUcJrbcXIHIHDWQjdUPdaXeMHNEu7yyBtOpm01oMGTKP3frsUXIABLBmAVWtKvqit4/W1KVKn5gJzg==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-gnu": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.14.tgz", + "integrity": "sha512-CGmlwLWbfG1dB4jZBJnp2IWlK5xBMNLjN7AR5kKA3sEpionoccEnChOEvfux1UdVJQjLRKuHNV9yGyqGBTpxfQ==", + "dev": true, + "optional": true + }, + "@swc/core-linux-x64-musl": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.14.tgz", + "integrity": "sha512-xq4npk8YKYmNwmr8fbvF2KP3kUVdZYfXZMQnW425gP3/sn+yFQO8Nd0bGH40vOVQn41kEesSe0Z5O/JDor2TgQ==", + "dev": true, + "optional": true + }, + "@swc/core-win32-arm64-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.14.tgz", + "integrity": "sha512-imq0X+gU9uUe6FqzOQot5gpKoaC00aCUiN58NOzwp0QXEupn8CDuZpdBN93HiZswfLruu5jA1tsc15x6v9p0Yg==", + "dev": true, + "optional": true + }, + "@swc/core-win32-ia32-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.14.tgz", + "integrity": "sha512-cH6QpXMw5D3t+lpx6SkErHrxN0yFzmQ0lgNAJxoDRiaAdDbqA6Col8UqUJwUS++Ul6aCWgNhCdiEYehPaoyDPA==", + "dev": true, + "optional": true + }, + "@swc/core-win32-x64-msvc": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.14.tgz", + "integrity": "sha512-FmZ4Tby4wW65K/36BKzmuu7mlq7cW5XOxzvufaSNVvQ5PN4OodAlqPjToe029oma4Av+ykJiif64scMttyNAzg==", + "dev": true, + "optional": true + }, + "@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "devOptional": true + }, + "@swc/types": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", + "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", + "devOptional": true, + "requires": { + "@swc/counter": "^0.1.3" + } + }, "@testcontainers/postgresql": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.7.2.tgz", - "integrity": "sha512-BB1C4SUDhWYY4X9afHtVYXDcYpvtRz7M4CtNEI3gA5A8Zm09msg5NUmhDSJiNAO8/xILv5LHcMJ0NZPBjgM9Jg==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-10.8.2.tgz", + "integrity": "sha512-UYbcHlK2XnP5DkwIOB8v+atz/ZfSpyEXR//FFhvAKNlLetMTv/8W73Hbhmvv50TCd2J3biFnatyo//uJm9e7ng==", "dev": true, "requires": { - "testcontainers": "^10.7.2" + "testcontainers": "^10.8.2" } }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "optional": true, + "peer": true }, "@turf/boolean-point-in-polygon": { "version": "6.5.0", @@ -17346,47 +16001,6 @@ "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.122.tgz", "integrity": "sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==" }, - "@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, "@types/bcrypt": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", @@ -17440,12 +16054,6 @@ "@types/express": "*" } }, - "@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true - }, "@types/cookies": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.0.tgz", @@ -17542,15 +16150,6 @@ "@types/node": "*" } }, - "@types/graceful-fs": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", - "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/hapi__catbox": { "version": "10.2.6", "resolved": "https://registry.npmjs.org/@types/hapi__catbox/-/hapi__catbox-10.2.6.tgz", @@ -17624,49 +16223,6 @@ "@types/node": "*" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/jest-when": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.5.tgz", - "integrity": "sha512-H9MDPIrz7NOu6IXP9OHExNN9LnJbGYAzRsGIDKxWr7Fth9vovemNV8yFbkUWLSEmuA8PREvAEvt9yK0PPLmFHA==", - "dev": true, - "requires": { - "@types/jest": "*" - } - }, "@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -17674,9 +16230,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/keygrip": { @@ -17734,12 +16290,6 @@ "@types/node": "*" } }, - "@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true - }, "@types/mime": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", @@ -17777,9 +16327,9 @@ } }, "@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "requires": { "undici-types": "~5.26.4" } @@ -17850,7 +16400,8 @@ "@types/picomatch": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.3.tgz", - "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==" + "integrity": "sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==", + "dev": true }, "@types/qs": { "version": "6.9.8", @@ -17920,33 +16471,6 @@ "@types/node": "*" } }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/superagent": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.1.tgz", - "integrity": "sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA==", - "dev": true, - "requires": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*" - } - }, - "@types/supertest": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", - "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", - "dev": true, - "requires": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, "@types/tedious": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", @@ -17975,95 +16499,80 @@ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" }, - "@types/yargs": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", - "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==", - "dev": true - }, "@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "requires": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" } }, "@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "requires": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "dependencies": { "brace-expansion": { @@ -18076,9 +16585,9 @@ } }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -18087,28 +16596,28 @@ } }, "@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" } }, "@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "requires": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" } }, "@ungap/structured-clone": { @@ -18117,6 +16626,111 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "@vitest/coverage-v8": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + } + } + } + }, + "@vitest/expect": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", + "dev": true, + "requires": { + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "chai": "^4.3.10" + } + }, + "@vitest/runner": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", + "dev": true, + "requires": { + "@vitest/utils": "1.5.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "dependencies": { + "p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "requires": { + "yocto-queue": "^1.0.0" + } + }, + "yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true + } + } + }, + "@vitest/snapshot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", + "dev": true, + "requires": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + } + }, + "@vitest/spy": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", + "dev": true, + "requires": { + "tinyspy": "^2.2.0" + } + }, + "@vitest/utils": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", + "dev": true, + "requires": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + } + }, "@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -18298,9 +16912,9 @@ } }, "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" }, "acorn-import-assertions": { "version": "1.9.0", @@ -18316,9 +16930,9 @@ "requires": {} }, "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "devOptional": true }, "agent-base": { @@ -18512,7 +17126,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "optional": true, + "peer": true }, "argparse": { "version": "2.0.1", @@ -18541,12 +17156,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -18556,6 +17165,12 @@ "safer-buffer": "~2.1.0" } }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -18566,108 +17181,11 @@ "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==" }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "b4a": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==" }, - "babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -18833,24 +17351,6 @@ "update-browserslist-db": "^1.0.13" } }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -18871,11 +17371,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, "buildcheck": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz", @@ -18954,6 +17449,12 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true + }, "call-bind": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", @@ -18970,18 +17471,27 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "caniuse-lite": { "version": "1.0.30001581", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==", "dev": true }, + "chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -18991,17 +17501,20 @@ "supports-color": "^7.1.0" } }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -19028,12 +17541,6 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, "cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -19144,18 +17651,6 @@ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, "color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -19192,15 +17687,6 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -19220,12 +17706,6 @@ "repeat-string": "^1.6.1" } }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "compress-commons": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", @@ -19300,12 +17780,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, "cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", @@ -19325,12 +17799,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true - }, "core-js-compat": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.1.tgz", @@ -19413,26 +17881,12 @@ } } }, - "create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - } - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "optional": true, + "peer": true }, "cron": { "version": "3.1.6", @@ -19474,12 +17928,14 @@ "ms": "2.1.2" } }, - "dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, - "requires": {} + "requires": { + "type-detect": "^4.0.0" + } }, "deep-is": { "version": "0.1.4", @@ -19512,12 +17968,6 @@ "has-property-descriptors": "^1.0.1" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -19543,22 +17993,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "diacritics": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", @@ -19568,7 +18002,8 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true + "optional": true, + "peer": true }, "diff-sequences": { "version": "29.6.3", @@ -19691,12 +18126,6 @@ "integrity": "sha512-sYSQhJCJa4aGA1wYol5cMQgekDBlbVfTRavlGZVr3WZpDdOPcp6a6xUnFfrt8TqZhsBYYbDxJZCjGfHuGupCRQ==", "dev": true }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -19767,6 +18196,37 @@ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", "dev": true }, + "esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -19876,9 +18336,9 @@ } }, "eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", @@ -19970,6 +18430,15 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -19996,31 +18465,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - } - } - }, "exiftool-vendored": { "version": "24.6.0", "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-24.6.0.tgz", @@ -20051,26 +18495,8 @@ "exiftool-vendored.pl": { "version": "12.78.0", "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.78.0.tgz", - "integrity": "sha512-K8j9NgxRpTFskFuXEl0AGsc692yYyThe4i3SXgx7xc0fu/vwD2c7tRGljkEtvaweYnMmfrF4DhCpuTu0aux6sg==" - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } + "integrity": "sha512-K8j9NgxRpTFskFuXEl0AGsc692yYyThe4i3SXgx7xc0fu/vwD2c7tRGljkEtvaweYnMmfrF4DhCpuTu0aux6sg==", + "optional": true }, "express": { "version": "4.19.2", @@ -20204,15 +18630,6 @@ "reusify": "^1.0.4" } }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -20382,29 +18799,6 @@ "tapable": "^2.2.1" } }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -20539,12 +18933,6 @@ "json-bigint": "^1.0.0" } }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, "geo-tz": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/geo-tz/-/geo-tz-8.0.2.tgz", @@ -20571,6 +18959,12 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, "get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -20583,12 +18977,6 @@ "hasown": "^2.0.0" } }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, "get-port": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", @@ -20601,12 +18989,6 @@ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, "glob": { "version": "10.3.12", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", @@ -20767,12 +19149,6 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, "highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -20811,12 +19187,6 @@ "debug": "4" } }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, "i18n-iso-countries": { "version": "7.11.0", "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.11.0.tgz", @@ -20839,9 +19209,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "import-fresh": { @@ -20864,16 +19234,6 @@ "module-details-from-path": "^1.0.3" } }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -20989,12 +19349,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -21040,24 +19394,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true }, - "istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - } - }, "istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -21080,25 +19421,6 @@ } } }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "istanbul-reports": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", @@ -21123,540 +19445,10 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - } - }, - "jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "dependencies": { - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - } - } - }, - "jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - } - }, - "jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - } - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - } - } - }, - "jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - } - }, - "jest-when": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jest-when/-/jest-when-3.6.0.tgz", - "integrity": "sha512-+cZWTy0ekAJo7M9Om0Scdor1jm3wDiYJWmXE8U22UVnkH54YCXAuaqz3P+up/FdtOg8g4wHOxV7Thd7nKhT6Dg==", - "dev": true, - "requires": {} - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "joi": { - "version": "17.12.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", - "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", + "version": "17.12.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.3.tgz", + "integrity": "sha512-2RRziagf555owrm9IRVtdKynOBeITiDpuZqIpgwqXShPncPKNiRQoiGsl/T8SQdq+8ugRzH2LqY67irr2y/d+g==", "requires": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -21683,12 +19475,6 @@ "argparse": "^2.0.1" } }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -21751,12 +19537,6 @@ "json-buffer": "3.0.1" } }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, "lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -21794,12 +19574,6 @@ } } }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -21820,12 +19594,28 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true + }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, + "local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "requires": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -21873,12 +19663,6 @@ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -21904,13 +19688,13 @@ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "requires": { - "yallist": "^3.0.2" + "get-func-name": "^2.0.1" } }, "luxon": { @@ -21927,6 +19711,17 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "requires": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -21946,16 +19741,8 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } + "optional": true, + "peer": true }, "media-typer": { "version": "0.3.0", @@ -22093,6 +19880,18 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "dev": true }, + "mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dev": true, + "requires": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, "mnemonist": { "version": "0.39.8", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", @@ -22227,6 +20026,12 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -22290,6 +20095,12 @@ } } }, + "nestjs-cls": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-4.3.0.tgz", + "integrity": "sha512-MVTun6tqCZih8AJXRj8uBuuFyJhQrIA9m9fStiQjbBXUkE3BrlMRvmLzyw8UcneB3xtFFTfwkAh5PYKRulyaOg==", + "requires": {} + }, "nestjs-otel": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/nestjs-otel/-/nestjs-otel-5.1.5.tgz", @@ -22328,12 +20139,6 @@ "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==", "optional": true }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, "node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -22373,15 +20178,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, "npmlog": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", @@ -22542,11 +20338,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -22647,6 +20438,18 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "pbf": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", @@ -22657,16 +20460,14 @@ } }, "pg": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", - "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", "pg-cloudflare": "^1.1.1", - "pg-connection-string": "^2.6.2", - "pg-pool": "^3.6.1", - "pg-protocol": "^1.6.0", + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", "pg-types": "^2.1.0", "pgpass": "1.x" } @@ -22678,9 +20479,9 @@ "optional": true }, "pg-connection-string": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" }, "pg-int8": { "version": "1.0.1", @@ -22693,15 +20494,15 @@ "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==" }, "pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", "requires": {} }, "pg-protocol": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", - "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" }, "pg-types": { "version": "2.2.0", @@ -22726,66 +20527,22 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==" }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", "dev": true, "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" } }, "pluralize": { @@ -22794,6 +20551,17 @@ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true }, + "postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + } + }, "postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -22878,16 +20646,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, "proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -22973,12 +20731,6 @@ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, - "pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true - }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -23194,14 +20946,9 @@ } }, "reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" - }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, "regexp-tree": { "version": "0.1.27", @@ -23263,23 +21010,6 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -23293,12 +21023,6 @@ "protocol-buffers-schema": "^3.3.1" } }, - "resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true - }, "response-time": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", @@ -23357,6 +21081,32 @@ "glob": "^10.3.7" } }, + "rollup": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -23655,6 +21405,12 @@ "object-inspect": "^1.9.0" } }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -23685,12 +21441,6 @@ "totalist": "^3.0.0" } }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -23739,6 +21489,12 @@ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true + }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -23800,12 +21556,6 @@ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "sql-formatter": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-15.3.0.tgz", @@ -23839,22 +21589,11 @@ "nan": "^2.17.0" } }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true }, "standard-as-callback": { "version": "2.1.0", @@ -23866,6 +21605,12 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, "stream-source": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz", @@ -23893,16 +21638,6 @@ "safe-buffer": "~5.2.0" } }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -23939,18 +21674,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -23966,42 +21689,23 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, - "superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "strip-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", "dev": true, "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" + "js-tokens": "^9.0.0" }, "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", "dev": true } } }, - "supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^8.1.2" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -24182,9 +21886,9 @@ } }, "testcontainers": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.7.2.tgz", - "integrity": "sha512-7d+LVd/4YKp/cutiVMLL5cnj/8p8oYELAVRRyNUM4FyUDz1OLQuwW868nDl7Vd1ZAQxzGeCR+F86FlR9Yw9fMA==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.8.2.tgz", + "integrity": "sha512-9Ink7NUyYZwOjQhk0C6R6basWy2WADNly+md3D9YDap0pcDr3C+vrO8Ah1bkYco/9Zg8VoYTHO+blkLeebBYkA==", "dev": true, "requires": { "@balena/dockerignore": "^1.0.2", @@ -24394,6 +22098,24 @@ "resolved": "https://registry.npmjs.org/thumbhash/-/thumbhash-0.1.1.tgz", "integrity": "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==" }, + "tinybench": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.7.0.tgz", + "integrity": "sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==", + "dev": true + }, + "tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true + }, + "tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -24402,12 +22124,6 @@ "os-tmpdir": "~1.0.2" } }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -24452,46 +22168,18 @@ } }, "ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "requires": {} }, - "ts-jest": { - "version": "29.1.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", - "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - } - }, - "ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - } - }, "ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -24630,11 +22318,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==" }, - "reflect-metadata": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", - "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==" - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -24662,9 +22345,9 @@ } }, "typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "devOptional": true }, "ua-parser-js": { @@ -24672,6 +22355,12 @@ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==" }, + "ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -24702,6 +22391,29 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, + "unplugin": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.10.1.tgz", + "integrity": "sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==", + "dev": true, + "requires": { + "acorn": "^8.11.3", + "chokidar": "^3.6.0", + "webpack-sources": "^3.2.3", + "webpack-virtual-modules": "^0.6.1" + } + }, + "unplugin-swc": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.4.5.tgz", + "integrity": "sha512-ltkJ70kjL53onJrypaMmKDiOvhghNUCbCxjxT6Ir0eAMIBsOfRhPt6vQtxB8R/6wYk/TfIJ2gCgdx2uKNPJRHA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.1.0", + "load-tsconfig": "^0.2.5", + "unplugin": "^1.10.1" + } + }, "update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -24763,26 +22475,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - } - } + "optional": true, + "peer": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -24804,13 +22498,130 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "vite": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "requires": { - "makeerror": "1.0.12" + "esbuild": "^0.20.1", + "fsevents": "~2.3.3", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + } + }, + "vite-node": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", + "dev": true, + "requires": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + } + }, + "vitest": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", + "dev": true, + "requires": { + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.5.0", + "why-is-node-running": "^2.2.2" + }, + "dependencies": { + "execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + } + }, + "get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true + }, + "human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } } }, "watchpack": { @@ -24898,6 +22709,12 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, + "webpack-virtual-modules": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz", + "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==", + "dev": true + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -24915,6 +22732,16 @@ "isexe": "^2.0.0" } }, + "why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -24953,24 +22780,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "dependencies": { - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - } - } - }, "ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", @@ -24987,12 +22796,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", @@ -25029,7 +22832,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true + "optional": true, + "peer": true }, "yocto-queue": { "version": "0.1.0", diff --git a/server/package.json b/server/package.json index 4d5c5e94cb..d5828822cd 100644 --- a/server/package.json +++ b/server/package.json @@ -18,11 +18,9 @@ "check": "tsc --noEmit", "check:code": "npm run format && npm run lint && npm run check", "check:all": "npm run check:code && npm run test:cov", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "e2e:jobs": "jest --config e2e/jobs/jest-e2e.json --runInBand", + "test": "vitest", + "test:watch": "vitest --watch", + "test:cov": "vitest --coverage", "typeorm": "typeorm", "typeorm:migrations:create": "typeorm migration:create", "typeorm:migrations:generate": "typeorm migration:generate -d ./dist/database.config.js", @@ -33,7 +31,6 @@ "sql:generate": "node ./dist/utils/sql.js" }, "dependencies": { - "@babel/runtime": "^7.22.11", "@nestjs/bullmq": "^10.0.1", "@nestjs/common": "^10.2.2", "@nestjs/config": "^3.0.0", @@ -45,11 +42,10 @@ "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", - "@opentelemetry/auto-instrumentations-node": "^0.43.0", - "@opentelemetry/exporter-prometheus": "^0.49.0", - "@opentelemetry/sdk-node": "^0.49.0", + "@opentelemetry/auto-instrumentations-node": "^0.44.0", + "@opentelemetry/exporter-prometheus": "^0.50.0", + "@opentelemetry/sdk-node": "^0.50.0", "@socket.io/postgres-adapter": "^0.3.1", - "@types/picomatch": "^2.3.3", "archiver": "^7.0.0", "async-lock": "^1.4.0", "bcrypt": "^5.1.1", @@ -59,7 +55,6 @@ "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", "exiftool-vendored": "~24.6.0", - "exiftool-vendored.pl": "12.78", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", @@ -73,11 +68,12 @@ "luxon": "^3.4.2", "mnemonist": "^0.39.8", "nest-commander": "^3.11.1", + "nestjs-cls": "^4.3.0", "nestjs-otel": "^5.1.5", "openid-client": "^5.4.3", "pg": "^8.11.3", "picomatch": "^4.0.0", - "reflect-metadata": "^0.1.13", + "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", "sharp": "^0.33.0", @@ -90,6 +86,7 @@ "@nestjs/cli": "^10.1.16", "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.2.2", + "@swc/core": "^1.4.14", "@testcontainers/postgresql": "^10.2.1", "@types/archiver": "^6.0.0", "@types/async-lock": "^1.4.2", @@ -98,76 +95,34 @@ "@types/express": "^4.17.17", "@types/fluent-ffmpeg": "^2.1.21", "@types/imagemin": "^8.0.1", - "@types/jest": "29.5.12", - "@types/jest-when": "^3.5.2", "@types/js-yaml": "^4.0.9", "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", "@types/node": "^20.5.7", - "@types/supertest": "^6.0.0", + "@types/picomatch": "^2.3.3", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", + "@vitest/coverage-v8": "^1.5.0", "dotenv": "^16.3.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-unicorn": "^51.0.0", - "jest": "^29.6.4", - "jest-when": "^3.6.0", + "eslint-plugin-unicorn": "^52.0.0", "mock-fs": "^5.2.0", "prettier": "^3.0.2", "prettier-plugin-organize-imports": "^3.2.3", "rimraf": "^5.0.1", "source-map-support": "^0.5.21", "sql-formatter": "^15.0.0", - "supertest": "^6.3.3", - "testcontainers": "^10.2.1", - "ts-jest": "^29.1.1", - "ts-loader": "^9.4.4", - "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^5.3.3", - "utimes": "^5.2.1" - }, - "jest": { - "clearMocks": true, - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": ".", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "collectCoverageFrom": [ - "/src/cores/*.(t|j)s", - "/src/dtos/*.(t|j)s", - "/src/interfaces/*.(t|j)s", - "/src/services/*.(t|j)s", - "/src/utils/*.(t|j)s", - "/src/*.t|j)s" - ], - "coverageDirectory": "./coverage", - "coverageThreshold": { - "./src/": { - "branches": 70, - "functions": 75, - "lines": 80, - "statements": 80 - } - }, - "testEnvironment": "node", - "moduleNameMapper": { - "^test(|/.*)$": "/test/$1", - "^src(|/.*)$": "/src/$1" - }, - "globalSetup": "/test/global-setup.js" + "unplugin-swc": "^1.4.5", + "utimes": "^5.2.1", + "vitest": "^1.5.0" }, "volta": { - "node": "20.12.0" + "node": "20.12.2" } } diff --git a/server/src/app.module.ts b/server/src/app.module.ts index ded08a96ab..40fa95aefc 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -5,28 +5,29 @@ import { APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ClsModule } from 'nestjs-cls'; import { OpenTelemetryModule } from 'nestjs-otel'; import { commands } from 'src/commands'; -import { bullConfig, bullQueues, immichAppConfig } from 'src/config'; +import { bullConfig, bullQueues, clsConfig, immichAppConfig } from 'src/config'; import { controllers } from 'src/controllers'; import { databaseConfig } from 'src/database.config'; import { entities } from 'src/entities'; import { AuthGuard } from 'src/middleware/auth.guard'; import { ErrorInterceptor } from 'src/middleware/error.interceptor'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; +import { LoggingInterceptor } from 'src/middleware/logging.interceptor'; import { repositories } from 'src/repositories'; import { services } from 'src/services'; import { ApiService } from 'src/services/api.service'; import { MicroservicesService } from 'src/services/microservices.service'; import { otelConfig } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; -const providers = [ImmichLogger]; -const common = [...services, ...providers, ...repositories]; +const common = [...services, ...repositories]; const middleware = [ FileUploadInterceptor, { provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) }, + { provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }, { provide: APP_INTERCEPTOR, useClass: ErrorInterceptor }, { provide: APP_GUARD, useClass: AuthGuard }, ]; @@ -34,6 +35,7 @@ const middleware = [ const imports = [ BullModule.forRoot(bullConfig), BullModule.registerQueue(...bullQueues), + ClsModule.forRoot(clsConfig), ConfigModule.forRoot(immichAppConfig), EventEmitterModule.forRoot(), OpenTelemetryModule.forRoot(otelConfig), diff --git a/server/src/commands/reset-admin-password.command.ts b/server/src/commands/reset-admin-password.command.ts index a186603a3f..f7c0775c8b 100644 --- a/server/src/commands/reset-admin-password.command.ts +++ b/server/src/commands/reset-admin-password.command.ts @@ -9,7 +9,7 @@ import { UserService } from 'src/services/user.service'; export class ResetAdminPasswordCommand extends CommandRunner { constructor( private userService: UserService, - private readonly inquirer: InquirerService, + private inquirer: InquirerService, ) { super(); } diff --git a/server/src/config.ts b/server/src/config.ts index c7d2302c1d..068d0b3a9f 100644 --- a/server/src/config.ts +++ b/server/src/config.ts @@ -1,8 +1,10 @@ import { RegisterQueueOptions } from '@nestjs/bullmq'; import { ConfigModuleOptions } from '@nestjs/config'; import { QueueOptions } from 'bullmq'; +import { Request, Response } from 'express'; import { RedisOptions } from 'ioredis'; import Joi from 'joi'; +import { CLS_ID, ClsModuleOptions } from 'nestjs-cls'; import { LogLevel } from 'src/entities/system-config.entity'; import { QueueName } from 'src/interfaces/job.interface'; @@ -69,3 +71,17 @@ export const bullConfig: QueueOptions = { }; export const bullQueues: RegisterQueueOptions[] = Object.values(QueueName).map((name) => ({ name })); + +export const clsConfig: ClsModuleOptions = { + middleware: { + mount: true, + generateId: true, + setup: (cls, req: Request, res: Response) => { + const headerValues = req.headers['x-immich-cid']; + const headerValue = Array.isArray(headerValues) ? headerValues[0] : headerValues; + const cid = headerValue || cls.get(CLS_ID); + cls.set(CLS_ID, cid); + res.header('x-immich-cid', cid); + }, + }, +}; diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index ce51aa4c01..d136a52b04 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -17,6 +17,7 @@ import { PersonController } from 'src/controllers/person.controller'; import { SearchController } from 'src/controllers/search.controller'; import { ServerInfoController } from 'src/controllers/server-info.controller'; import { SharedLinkController } from 'src/controllers/shared-link.controller'; +import { SyncController } from 'src/controllers/sync.controller'; import { SystemConfigController } from 'src/controllers/system-config.controller'; import { TagController } from 'src/controllers/tag.controller'; import { TimelineController } from 'src/controllers/timeline.controller'; @@ -43,6 +44,7 @@ export const controllers = [ SearchController, ServerInfoController, SharedLinkController, + SyncController, SystemConfigController, TagController, TimelineController, diff --git a/server/src/controllers/shared-link.controller.ts b/server/src/controllers/shared-link.controller.ts index 990f4e3225..a7a8e3a1c6 100644 --- a/server/src/controllers/shared-link.controller.ts +++ b/server/src/controllers/shared-link.controller.ts @@ -19,7 +19,7 @@ import { UUIDParamDto } from 'src/validation'; @Controller('shared-link') @Authenticated() export class SharedLinkController { - constructor(private readonly service: SharedLinkService) {} + constructor(private service: SharedLinkService) {} @Get() getAllSharedLinks(@Auth() auth: AuthDto): Promise { diff --git a/server/src/controllers/sync.controller.ts b/server/src/controllers/sync.controller.ts new file mode 100644 index 0000000000..c12d42df23 --- /dev/null +++ b/server/src/controllers/sync.controller.ts @@ -0,0 +1,24 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { AssetDeltaSyncDto, AssetDeltaSyncResponseDto, AssetFullSyncDto } from 'src/dtos/sync.dto'; +import { Auth, Authenticated } from 'src/middleware/auth.guard'; +import { SyncService } from 'src/services/sync.service'; + +@ApiTags('Sync') +@Controller('sync') +@Authenticated() +export class SyncController { + constructor(private service: SyncService) {} + + @Get('full-sync') + getAllForUserFullSync(@Auth() auth: AuthDto, @Query() dto: AssetFullSyncDto): Promise { + return this.service.getAllAssetsForUserFullSync(auth, dto); + } + + @Get('delta-sync') + getDeltaSync(@Auth() auth: AuthDto, @Query() dto: AssetDeltaSyncDto): Promise { + return this.service.getChangesForDeltaSync(auth, dto); + } +} diff --git a/server/src/controllers/system-config.controller.ts b/server/src/controllers/system-config.controller.ts index 0b46b82a51..08da743191 100644 --- a/server/src/controllers/system-config.controller.ts +++ b/server/src/controllers/system-config.controller.ts @@ -8,7 +8,7 @@ import { SystemConfigService } from 'src/services/system-config.service'; @Controller('system-config') @Authenticated({ admin: true }) export class SystemConfigController { - constructor(private readonly service: SystemConfigService) {} + constructor(private service: SystemConfigService) {} @Get() getConfig(): Promise { diff --git a/server/src/cores/storage.core.spec.ts b/server/src/cores/storage.core.spec.ts index 16258f095e..6ff6ca61bf 100644 --- a/server/src/cores/storage.core.spec.ts +++ b/server/src/cores/storage.core.spec.ts @@ -1,6 +1,7 @@ import { StorageCore } from 'src/cores/storage.core'; +import { vitest } from 'vitest'; -jest.mock('src/constants', () => ({ +vitest.mock('src/constants', () => ({ APP_MEDIA_LOCATION: '/photos', })); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 035f90c911..f1c16e5698 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -7,11 +7,11 @@ import { PersonEntity } from 'src/entities/person.entity'; import { ImageFormat } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; export enum StorageFolder { ENCODED_VIDEO = 'encoded-video', @@ -41,35 +41,37 @@ export type GeneratedAssetType = GeneratedImageType | AssetPathType.ENCODED_VIDE let instance: StorageCore | null; export class StorageCore { - private logger = new ImmichLogger(StorageCore.name); private configCore; private constructor( private assetRepository: IAssetRepository, + private cryptoRepository: ICryptoRepository, private moveRepository: IMoveRepository, private personRepository: IPersonRepository, - private cryptoRepository: ICryptoRepository, - private repository: IStorageRepository, + private storageRepository: IStorageRepository, systemConfigRepository: ISystemConfigRepository, + private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(systemConfigRepository); + this.configCore = SystemConfigCore.create(systemConfigRepository, this.logger); } static create( assetRepository: IAssetRepository, + cryptoRepository: ICryptoRepository, moveRepository: IMoveRepository, personRepository: IPersonRepository, - cryptoRepository: ICryptoRepository, - configRepository: ISystemConfigRepository, - repository: IStorageRepository, + storageRepository: IStorageRepository, + systemConfigRepository: ISystemConfigRepository, + logger: ILoggerRepository, ) { if (!instance) { instance = new StorageCore( assetRepository, + cryptoRepository, moveRepository, personRepository, - cryptoRepository, - repository, - configRepository, + storageRepository, + systemConfigRepository, + logger, ); } @@ -170,8 +172,8 @@ export class StorageCore { let move = await this.moveRepository.getByEntity(entityId, pathType); if (move) { this.logger.log(`Attempting to finish incomplete move: ${move.oldPath} => ${move.newPath}`); - const oldPathExists = await this.repository.checkFileExists(move.oldPath); - const newPathExists = await this.repository.checkFileExists(move.newPath); + const oldPathExists = await this.storageRepository.checkFileExists(move.oldPath); + const newPathExists = await this.storageRepository.checkFileExists(move.newPath); const newPathCheck = newPathExists ? move.newPath : null; const actualPath = oldPathExists ? move.oldPath : newPathCheck; if (!actualPath) { @@ -205,7 +207,7 @@ export class StorageCore { if (move.oldPath !== newPath) { try { this.logger.debug(`Attempting to rename file: ${move.oldPath} => ${newPath}`); - await this.repository.rename(move.oldPath, newPath); + await this.storageRepository.rename(move.oldPath, newPath); } catch (error: any) { if (error.code !== 'EXDEV') { this.logger.warn( @@ -214,19 +216,19 @@ export class StorageCore { return; } this.logger.debug(`Unable to rename file. Falling back to copy, verify and delete`); - await this.repository.copyFile(move.oldPath, newPath); + await this.storageRepository.copyFile(move.oldPath, newPath); if (!(await this.verifyNewPathContentsMatchesExpected(move.oldPath, newPath, assetInfo))) { this.logger.warn(`Skipping move due to file size mismatch`); - await this.repository.unlink(newPath); + await this.storageRepository.unlink(newPath); return; } - const { atime, mtime } = await this.repository.stat(move.oldPath); - await this.repository.utimes(newPath, atime, mtime); + const { atime, mtime } = await this.storageRepository.stat(move.oldPath); + await this.storageRepository.utimes(newPath, atime, mtime); try { - await this.repository.unlink(move.oldPath); + await this.storageRepository.unlink(move.oldPath); } catch (error: any) { this.logger.warn(`Unable to delete old file, it will now no longer be tracked by Immich: ${error.message}`); } @@ -242,8 +244,8 @@ export class StorageCore { newPath: string, assetInfo?: { sizeInBytes: number; checksum: Buffer }, ) { - const oldStat = await this.repository.stat(oldPath); - const newStat = await this.repository.stat(newPath); + const oldStat = await this.storageRepository.stat(oldPath); + const newStat = await this.storageRepository.stat(newPath); const oldPathSize = assetInfo ? assetInfo.sizeInBytes : oldStat.size; const newPathSize = newStat.size; this.logger.debug(`File size check: ${newPathSize} === ${oldPathSize}`); @@ -269,11 +271,11 @@ export class StorageCore { } ensureFolders(input: string) { - this.repository.mkdirSync(dirname(input)); + this.storageRepository.mkdirSync(dirname(input)); } removeEmptyDirs(folder: StorageFolder) { - return this.repository.removeEmptyDirs(StorageCore.getBaseFolder(folder)); + return this.storageRepository.removeEmptyDirs(StorageCore.getBaseFolder(folder)); } private savePath(pathType: PathType, id: string, newPath: string) { diff --git a/server/src/cores/system-config.core.ts b/server/src/cores/system-config.core.ts index 3a1ea47bbe..9cbe3b8414 100644 --- a/server/src/cores/system-config.core.ts +++ b/server/src/cores/system-config.core.ts @@ -22,8 +22,8 @@ import { VideoCodec, } from 'src/entities/system-config.entity'; import { QueueName } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; export type SystemConfigValidator = (config: SystemConfig, newConfig: SystemConfig) => void | Promise; @@ -169,16 +169,18 @@ let instance: SystemConfigCore | null; @Injectable() export class SystemConfigCore { - private logger = new ImmichLogger(SystemConfigCore.name); private configCache: SystemConfigEntity[] | null = null; public config$ = new Subject(); - private constructor(private repository: ISystemConfigRepository) {} + private constructor( + private repository: ISystemConfigRepository, + private logger: ILoggerRepository, + ) {} - static create(repository: ISystemConfigRepository) { + static create(repository: ISystemConfigRepository, logger: ILoggerRepository) { if (!instance) { - instance = new SystemConfigCore(repository); + instance = new SystemConfigCore(repository, logger); } return instance; } diff --git a/server/src/dtos/library.dto.ts b/server/src/dtos/library.dto.ts index 951012a852..b693d35adf 100644 --- a/server/src/dtos/library.dto.ts +++ b/server/src/dtos/library.dto.ts @@ -32,9 +32,6 @@ export class CreateLibraryDto { @ArrayUnique() @ArrayMaxSize(128) exclusionPatterns?: string[]; - - @ValidateBoolean({ optional: true }) - isWatched?: boolean; } export class UpdateLibraryDto { diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts new file mode 100644 index 0000000000..a69062ec2d --- /dev/null +++ b/server/src/dtos/sync.dto.ts @@ -0,0 +1,38 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsInt, IsPositive } from 'class-validator'; +import { AssetResponseDto } from 'src/dtos/asset-response.dto'; +import { ValidateDate, ValidateUUID } from 'src/validation'; + +export class AssetFullSyncDto { + @ValidateUUID({ optional: true }) + lastId?: string; + + @ValidateDate({ optional: true }) + lastCreationDate?: Date; + + @ValidateDate() + updatedUntil!: Date; + + @IsInt() + @IsPositive() + @Type(() => Number) + @ApiProperty({ type: 'integer' }) + limit!: number; + + @ValidateUUID({ optional: true }) + userId?: string; +} + +export class AssetDeltaSyncDto { + @ValidateDate() + updatedAfter!: Date; + @ValidateUUID({ each: true }) + userIds!: string[]; +} + +export class AssetDeltaSyncResponseDto { + needsFullSync!: boolean; + upserted!: AssetResponseDto[]; + deleted!: string[]; +} diff --git a/server/src/entities/system-config.entity.ts b/server/src/entities/system-config.entity.ts index 1ddd6baff3..a8a550fd6d 100644 --- a/server/src/entities/system-config.entity.ts +++ b/server/src/entities/system-config.entity.ts @@ -153,6 +153,7 @@ export enum VideoCodec { H264 = 'h264', HEVC = 'hevc', VP9 = 'vp9', + AV1 = 'av1', } export enum AudioCodec { diff --git a/server/src/interfaces/asset.interface.ts b/server/src/interfaces/asset.interface.ts index 008931566b..9c2ebe3e73 100644 --- a/server/src/interfaces/asset.interface.ts +++ b/server/src/interfaces/asset.interface.ts @@ -133,6 +133,20 @@ export interface MetadataSearchOptions { numResults: number; } +export interface AssetFullSyncOptions { + ownerId: string; + lastCreationDate?: Date; + lastId?: string; + updatedUntil: Date; + limit: number; +} + +export interface AssetDeltaSyncOptions { + userIds: string[]; + updatedAfter: Date; + limit: number; +} + export type AssetPathEntity = Pick; export const IAssetRepository = 'IAssetRepository'; @@ -146,7 +160,7 @@ export interface IAssetRepository { ): Promise; getByIdsWithAllRelations(ids: string[]): Promise; getByDayOfYear(ownerIds: string[], monthDay: MonthDay): Promise; - getByChecksum(userId: string, checksum: Buffer): Promise; + getByChecksum(libraryId: string, checksum: Buffer): Promise; getByAlbumId(pagination: PaginationOptions, albumId: string): Paginated; getByUserId(pagination: PaginationOptions, userId: string, options?: AssetSearchOptions): Paginated; getById(id: string, relations?: FindOptionsRelations): Promise; @@ -155,7 +169,7 @@ export interface IAssetRepository { getRandom(userId: string, count: number): Promise; getFirstAssetForAlbumId(albumId: string): Promise; getLastUpdatedAssetForAlbumId(albumId: string): Promise; - getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated; + getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated; getByLibraryIdAndOriginalPath(libraryId: string, originalPath: string): Promise; deleteAll(ownerId: string): Promise; getAll(pagination: PaginationOptions, options?: AssetSearchOptions): Paginated; @@ -175,4 +189,6 @@ export interface IAssetRepository { getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise>; getAssetIdByTag(userId: string, options: AssetExploreFieldOptions): Promise>; searchMetadata(query: string, userIds: string[], options: MetadataSearchOptions): Promise; + getAllForUserFullSync(options: AssetFullSyncOptions): Promise; + getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise; } diff --git a/server/src/interfaces/audit.interface.ts b/server/src/interfaces/audit.interface.ts index 767a4bc2f6..b023d00d56 100644 --- a/server/src/interfaces/audit.interface.ts +++ b/server/src/interfaces/audit.interface.ts @@ -1,14 +1,14 @@ -import { AuditEntity, DatabaseAction, EntityType } from 'src/entities/audit.entity'; +import { DatabaseAction, EntityType } from 'src/entities/audit.entity'; export const IAuditRepository = 'IAuditRepository'; export interface AuditSearch { action?: DatabaseAction; entityType?: EntityType; - ownerId?: string; + userIds: string[]; } export interface IAuditRepository { - getAfter(since: Date, options: AuditSearch): Promise; + getAfter(since: Date, options: AuditSearch): Promise; removeBefore(before: Date): Promise; } diff --git a/server/src/interfaces/logger.interface.ts b/server/src/interfaces/logger.interface.ts new file mode 100644 index 0000000000..d8e9a7d2ab --- /dev/null +++ b/server/src/interfaces/logger.interface.ts @@ -0,0 +1,15 @@ +import { LogLevel } from 'src/entities/system-config.entity'; + +export const ILoggerRepository = 'ILoggerRepository'; + +export interface ILoggerRepository { + setContext(message: string): void; + setLogLevel(level: LogLevel): void; + + verbose(message: any, ...args: any): void; + debug(message: any, ...args: any): void; + log(message: any, ...args: any): void; + warn(message: any, ...args: any): void; + error(message: any, ...args: any): void; + fatal(message: any, ...args: any): void; +} diff --git a/server/src/interfaces/storage.interface.ts b/server/src/interfaces/storage.interface.ts index e78bb0195d..1bd49a3f20 100644 --- a/server/src/interfaces/storage.interface.ts +++ b/server/src/interfaces/storage.interface.ts @@ -31,14 +31,6 @@ export interface WatchEvents { onError(error: Error): void; } -export enum StorageEventType { - READY = 'ready', - ADD = 'add', - CHANGE = 'change', - UNLINK = 'unlink', - ERROR = 'error', -} - export interface IStorageRepository { createZipStream(): ImmichZipStream; createReadStream(filepath: string, mimeType?: string | null): Promise; diff --git a/server/src/main.ts b/server/src/main.ts index 3a93038683..54e6a97acc 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -8,34 +8,38 @@ import sirv from 'sirv'; import { ApiModule, ImmichAdminModule, MicroservicesModule } from 'src/app.module'; import { WEB_ROOT, envName, excludePaths, isDev, serverVersion } from 'src/constants'; import { LogLevel } from 'src/entities/system-config.entity'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { WebSocketAdapter } from 'src/middleware/websocket.adapter'; import { ApiService } from 'src/services/api.service'; import { otelSDK } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { useSwagger } from 'src/utils/misc'; async function bootstrapMicroservices() { - const logger = new ImmichLogger('ImmichMicroservice'); - const port = Number(process.env.MICROSERVICES_PORT) || 3002; - otelSDK.start(); + + const host = String(process.env.HOST || '0.0.0.0'); + const port = Number(process.env.MICROSERVICES_PORT) || 3002; const app = await NestFactory.create(MicroservicesModule, { bufferLogs: true }); - app.useLogger(app.get(ImmichLogger)); + const logger = await app.resolve(ILoggerRepository); + logger.setContext('ImmichMicroservice'); + app.useLogger(logger); app.useWebSocketAdapter(new WebSocketAdapter(app)); - await app.listen(port); + await app.listen(port, host); logger.log(`Immich Microservices is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `); } async function bootstrapApi() { - const logger = new ImmichLogger('ImmichServer'); - const port = Number(process.env.SERVER_PORT) || 3001; - otelSDK.start(); - const app = await NestFactory.create(ApiModule, { bufferLogs: true }); - app.useLogger(app.get(ImmichLogger)); + const host = String(process.env.HOST || '0.0.0.0'); + const port = Number(process.env.SERVER_PORT) || 3001; + const app = await NestFactory.create(ApiModule, { bufferLogs: true }); + const logger = await app.resolve(ILoggerRepository); + + logger.setContext('ImmichServer'); + app.useLogger(logger); app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); app.set('etag', 'strong'); app.use(cookieParser()); @@ -65,7 +69,7 @@ async function bootstrapApi() { } app.use(app.get(ApiService).ssr(excludePaths)); - const server = await app.listen(port); + const server = await app.listen(port, host); server.requestTimeout = 30 * 60 * 1000; logger.log(`Immich Server is listening on ${await app.getUrl()} [v${serverVersion}] [${envName}] `); diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index eaa47d013b..8b3abe6693 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -1,6 +1,7 @@ import { CanActivate, ExecutionContext, + Inject, Injectable, SetMetadata, applyDecorators, @@ -11,8 +12,8 @@ import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } fr import { Request } from 'express'; import { IMMICH_API_KEY_NAME } from 'src/constants'; import { AuthDto } from 'src/dtos/auth.dto'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthService, LoginDetails } from 'src/services/auth.service'; -import { ImmichLogger } from 'src/utils/logger'; import { UAParser } from 'ua-parser-js'; export enum Metadata { @@ -79,12 +80,13 @@ export interface AuthRequest extends Request { @Injectable() export class AuthGuard implements CanActivate { - private logger = new ImmichLogger(AuthGuard.name); - constructor( + @Inject(ILoggerRepository) private logger: ILoggerRepository, private reflector: Reflector, private authService: AuthService, - ) {} + ) { + this.logger.setContext(AuthGuard.name); + } async canActivate(context: ExecutionContext): Promise { const targets = [context.getHandler(), context.getClass()]; diff --git a/server/src/middleware/error.interceptor.ts b/server/src/middleware/error.interceptor.ts index 9e2273b976..8d1a25d44c 100644 --- a/server/src/middleware/error.interceptor.ts +++ b/server/src/middleware/error.interceptor.ts @@ -2,17 +2,20 @@ import { CallHandler, ExecutionContext, HttpException, + Inject, Injectable, InternalServerErrorException, NestInterceptor, } from '@nestjs/common'; import { Observable, catchError, throwError } from 'rxjs'; -import { ImmichLogger } from 'src/utils/logger'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { isConnectionAborted, routeToErrorMessage } from 'src/utils/misc'; @Injectable() export class ErrorInterceptor implements NestInterceptor { - private logger = new ImmichLogger(ErrorInterceptor.name); + constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { + this.logger.setContext(ErrorInterceptor.name); + } intercept(context: ExecutionContext, next: CallHandler): Observable { return next.handle().pipe( diff --git a/server/src/middleware/file-upload.interceptor.ts b/server/src/middleware/file-upload.interceptor.ts index 53acbefa88..1b8405fe6e 100644 --- a/server/src/middleware/file-upload.interceptor.ts +++ b/server/src/middleware/file-upload.interceptor.ts @@ -1,4 +1,4 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from '@nestjs/common'; import { PATH_METADATA } from '@nestjs/common/constants'; import { Reflector } from '@nestjs/core'; import { transformException } from '@nestjs/platform-express/multer/multer/multer.utils'; @@ -7,9 +7,9 @@ import multer, { StorageEngine, diskStorage } from 'multer'; import { createHash, randomUUID } from 'node:crypto'; import { Observable } from 'rxjs'; import { UploadFieldName } from 'src/dtos/asset.dto'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthRequest } from 'src/middleware/auth.guard'; import { AssetService, UploadFile } from 'src/services/asset.service'; -import { ImmichLogger } from 'src/utils/logger'; export enum Route { ASSET = 'asset', @@ -59,8 +59,6 @@ const asRequest = (request: AuthRequest, file: Express.Multer.File) => { @Injectable() export class FileUploadInterceptor implements NestInterceptor { - private logger = new ImmichLogger(FileUploadInterceptor.name); - private handlers: { userProfile: RequestHandler; assetUpload: RequestHandler; @@ -70,7 +68,10 @@ export class FileUploadInterceptor implements NestInterceptor { constructor( private reflect: Reflector, private assetService: AssetService, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { + this.logger.setContext(FileUploadInterceptor.name); + this.defaultStorage = diskStorage({ filename: this.filename.bind(this), destination: this.destination.bind(this), diff --git a/server/src/middleware/logging.interceptor.ts b/server/src/middleware/logging.interceptor.ts new file mode 100644 index 0000000000..ee8161d9c7 --- /dev/null +++ b/server/src/middleware/logging.interceptor.ts @@ -0,0 +1,28 @@ +import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from '@nestjs/common'; +import { Observable, finalize } from 'rxjs'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; + +@Injectable() +export class LoggingInterceptor implements NestInterceptor { + constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { + this.logger.setContext(LoggingInterceptor.name); + } + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const handler = context.switchToHttp(); + const req = handler.getRequest(); + const res = handler.getResponse(); + + const { method, ip, path } = req; + + const start = performance.now(); + return next.handle().pipe( + finalize(() => { + const finish = performance.now(); + const duration = (finish - start).toFixed(2); + const { statusCode } = res; + this.logger.verbose(`${method} ${path} ${statusCode} ${duration}ms ${ip}`); + }), + ); + } +} diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index 6f0e8cd5e3..86e9796faa 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -253,7 +253,7 @@ DELETE FROM "assets" WHERE "ownerId" = $1 --- AssetRepository.getLibraryAssetPaths +-- AssetRepository.getExternalLibraryAssetPaths SELECT DISTINCT "distinctAlias"."AssetEntity_id" AS "ids_AssetEntity_id" FROM @@ -272,6 +272,7 @@ FROM ( ( ((("AssetEntity__AssetEntity_library"."id" = $1))) + AND ("AssetEntity"."isExternal" = $2) ) ) AND ("AssetEntity"."deletedAt" IS NULL) @@ -464,7 +465,7 @@ FROM WHERE ( ( - ("AssetEntity"."ownerId" = $1) + ("AssetEntity"."libraryId" = $1) AND ("AssetEntity"."checksum" = $2) ) ) @@ -767,3 +768,151 @@ ORDER BY "asset"."fileCreatedAt" DESC LIMIT 250 + +-- AssetRepository.getAllForUserFullSync +SELECT + "asset"."id" AS "asset_id", + "asset"."deviceAssetId" AS "asset_deviceAssetId", + "asset"."ownerId" AS "asset_ownerId", + "asset"."libraryId" AS "asset_libraryId", + "asset"."deviceId" AS "asset_deviceId", + "asset"."type" AS "asset_type", + "asset"."originalPath" AS "asset_originalPath", + "asset"."previewPath" AS "asset_previewPath", + "asset"."thumbnailPath" AS "asset_thumbnailPath", + "asset"."thumbhash" AS "asset_thumbhash", + "asset"."encodedVideoPath" AS "asset_encodedVideoPath", + "asset"."createdAt" AS "asset_createdAt", + "asset"."updatedAt" AS "asset_updatedAt", + "asset"."deletedAt" AS "asset_deletedAt", + "asset"."fileCreatedAt" AS "asset_fileCreatedAt", + "asset"."localDateTime" AS "asset_localDateTime", + "asset"."fileModifiedAt" AS "asset_fileModifiedAt", + "asset"."isFavorite" AS "asset_isFavorite", + "asset"."isArchived" AS "asset_isArchived", + "asset"."isExternal" AS "asset_isExternal", + "asset"."isReadOnly" AS "asset_isReadOnly", + "asset"."isOffline" AS "asset_isOffline", + "asset"."checksum" AS "asset_checksum", + "asset"."duration" AS "asset_duration", + "asset"."isVisible" AS "asset_isVisible", + "asset"."livePhotoVideoId" AS "asset_livePhotoVideoId", + "asset"."originalFileName" AS "asset_originalFileName", + "asset"."sidecarPath" AS "asset_sidecarPath", + "asset"."stackId" AS "asset_stackId", + "exifInfo"."assetId" AS "exifInfo_assetId", + "exifInfo"."description" AS "exifInfo_description", + "exifInfo"."exifImageWidth" AS "exifInfo_exifImageWidth", + "exifInfo"."exifImageHeight" AS "exifInfo_exifImageHeight", + "exifInfo"."fileSizeInByte" AS "exifInfo_fileSizeInByte", + "exifInfo"."orientation" AS "exifInfo_orientation", + "exifInfo"."dateTimeOriginal" AS "exifInfo_dateTimeOriginal", + "exifInfo"."modifyDate" AS "exifInfo_modifyDate", + "exifInfo"."timeZone" AS "exifInfo_timeZone", + "exifInfo"."latitude" AS "exifInfo_latitude", + "exifInfo"."longitude" AS "exifInfo_longitude", + "exifInfo"."projectionType" AS "exifInfo_projectionType", + "exifInfo"."city" AS "exifInfo_city", + "exifInfo"."livePhotoCID" AS "exifInfo_livePhotoCID", + "exifInfo"."autoStackId" AS "exifInfo_autoStackId", + "exifInfo"."state" AS "exifInfo_state", + "exifInfo"."country" AS "exifInfo_country", + "exifInfo"."make" AS "exifInfo_make", + "exifInfo"."model" AS "exifInfo_model", + "exifInfo"."lensModel" AS "exifInfo_lensModel", + "exifInfo"."fNumber" AS "exifInfo_fNumber", + "exifInfo"."focalLength" AS "exifInfo_focalLength", + "exifInfo"."iso" AS "exifInfo_iso", + "exifInfo"."exposureTime" AS "exifInfo_exposureTime", + "exifInfo"."profileDescription" AS "exifInfo_profileDescription", + "exifInfo"."colorspace" AS "exifInfo_colorspace", + "exifInfo"."bitsPerSample" AS "exifInfo_bitsPerSample", + "exifInfo"."fps" AS "exifInfo_fps", + "stack"."id" AS "stack_id", + "stack"."primaryAssetId" AS "stack_primaryAssetId" +FROM + "assets" "asset" + LEFT JOIN "exif" "exifInfo" ON "exifInfo"."assetId" = "asset"."id" + LEFT JOIN "asset_stack" "stack" ON "stack"."id" = "asset"."stackId" +WHERE + "asset"."ownerId" = $1 + AND ("asset"."fileCreatedAt", "asset"."id") < ($2, $3) + AND "asset"."updatedAt" <= $4 + AND "asset"."isVisible" = true +ORDER BY + "asset"."fileCreatedAt" DESC, + "asset"."id" DESC +LIMIT + 10 + +-- AssetRepository.getChangedDeltaSync +SELECT + "AssetEntity"."id" AS "AssetEntity_id", + "AssetEntity"."deviceAssetId" AS "AssetEntity_deviceAssetId", + "AssetEntity"."ownerId" AS "AssetEntity_ownerId", + "AssetEntity"."libraryId" AS "AssetEntity_libraryId", + "AssetEntity"."deviceId" AS "AssetEntity_deviceId", + "AssetEntity"."type" AS "AssetEntity_type", + "AssetEntity"."originalPath" AS "AssetEntity_originalPath", + "AssetEntity"."previewPath" AS "AssetEntity_previewPath", + "AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath", + "AssetEntity"."thumbhash" AS "AssetEntity_thumbhash", + "AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath", + "AssetEntity"."createdAt" AS "AssetEntity_createdAt", + "AssetEntity"."updatedAt" AS "AssetEntity_updatedAt", + "AssetEntity"."deletedAt" AS "AssetEntity_deletedAt", + "AssetEntity"."fileCreatedAt" AS "AssetEntity_fileCreatedAt", + "AssetEntity"."localDateTime" AS "AssetEntity_localDateTime", + "AssetEntity"."fileModifiedAt" AS "AssetEntity_fileModifiedAt", + "AssetEntity"."isFavorite" AS "AssetEntity_isFavorite", + "AssetEntity"."isArchived" AS "AssetEntity_isArchived", + "AssetEntity"."isExternal" AS "AssetEntity_isExternal", + "AssetEntity"."isReadOnly" AS "AssetEntity_isReadOnly", + "AssetEntity"."isOffline" AS "AssetEntity_isOffline", + "AssetEntity"."checksum" AS "AssetEntity_checksum", + "AssetEntity"."duration" AS "AssetEntity_duration", + "AssetEntity"."isVisible" AS "AssetEntity_isVisible", + "AssetEntity"."livePhotoVideoId" AS "AssetEntity_livePhotoVideoId", + "AssetEntity"."originalFileName" AS "AssetEntity_originalFileName", + "AssetEntity"."sidecarPath" AS "AssetEntity_sidecarPath", + "AssetEntity"."stackId" AS "AssetEntity_stackId", + "AssetEntity__AssetEntity_exifInfo"."assetId" AS "AssetEntity__AssetEntity_exifInfo_assetId", + "AssetEntity__AssetEntity_exifInfo"."description" AS "AssetEntity__AssetEntity_exifInfo_description", + "AssetEntity__AssetEntity_exifInfo"."exifImageWidth" AS "AssetEntity__AssetEntity_exifInfo_exifImageWidth", + "AssetEntity__AssetEntity_exifInfo"."exifImageHeight" AS "AssetEntity__AssetEntity_exifInfo_exifImageHeight", + "AssetEntity__AssetEntity_exifInfo"."fileSizeInByte" AS "AssetEntity__AssetEntity_exifInfo_fileSizeInByte", + "AssetEntity__AssetEntity_exifInfo"."orientation" AS "AssetEntity__AssetEntity_exifInfo_orientation", + "AssetEntity__AssetEntity_exifInfo"."dateTimeOriginal" AS "AssetEntity__AssetEntity_exifInfo_dateTimeOriginal", + "AssetEntity__AssetEntity_exifInfo"."modifyDate" AS "AssetEntity__AssetEntity_exifInfo_modifyDate", + "AssetEntity__AssetEntity_exifInfo"."timeZone" AS "AssetEntity__AssetEntity_exifInfo_timeZone", + "AssetEntity__AssetEntity_exifInfo"."latitude" AS "AssetEntity__AssetEntity_exifInfo_latitude", + "AssetEntity__AssetEntity_exifInfo"."longitude" AS "AssetEntity__AssetEntity_exifInfo_longitude", + "AssetEntity__AssetEntity_exifInfo"."projectionType" AS "AssetEntity__AssetEntity_exifInfo_projectionType", + "AssetEntity__AssetEntity_exifInfo"."city" AS "AssetEntity__AssetEntity_exifInfo_city", + "AssetEntity__AssetEntity_exifInfo"."livePhotoCID" AS "AssetEntity__AssetEntity_exifInfo_livePhotoCID", + "AssetEntity__AssetEntity_exifInfo"."autoStackId" AS "AssetEntity__AssetEntity_exifInfo_autoStackId", + "AssetEntity__AssetEntity_exifInfo"."state" AS "AssetEntity__AssetEntity_exifInfo_state", + "AssetEntity__AssetEntity_exifInfo"."country" AS "AssetEntity__AssetEntity_exifInfo_country", + "AssetEntity__AssetEntity_exifInfo"."make" AS "AssetEntity__AssetEntity_exifInfo_make", + "AssetEntity__AssetEntity_exifInfo"."model" AS "AssetEntity__AssetEntity_exifInfo_model", + "AssetEntity__AssetEntity_exifInfo"."lensModel" AS "AssetEntity__AssetEntity_exifInfo_lensModel", + "AssetEntity__AssetEntity_exifInfo"."fNumber" AS "AssetEntity__AssetEntity_exifInfo_fNumber", + "AssetEntity__AssetEntity_exifInfo"."focalLength" AS "AssetEntity__AssetEntity_exifInfo_focalLength", + "AssetEntity__AssetEntity_exifInfo"."iso" AS "AssetEntity__AssetEntity_exifInfo_iso", + "AssetEntity__AssetEntity_exifInfo"."exposureTime" AS "AssetEntity__AssetEntity_exifInfo_exposureTime", + "AssetEntity__AssetEntity_exifInfo"."profileDescription" AS "AssetEntity__AssetEntity_exifInfo_profileDescription", + "AssetEntity__AssetEntity_exifInfo"."colorspace" AS "AssetEntity__AssetEntity_exifInfo_colorspace", + "AssetEntity__AssetEntity_exifInfo"."bitsPerSample" AS "AssetEntity__AssetEntity_exifInfo_bitsPerSample", + "AssetEntity__AssetEntity_exifInfo"."fps" AS "AssetEntity__AssetEntity_exifInfo_fps", + "AssetEntity__AssetEntity_stack"."id" AS "AssetEntity__AssetEntity_stack_id", + "AssetEntity__AssetEntity_stack"."primaryAssetId" AS "AssetEntity__AssetEntity_stack_primaryAssetId" +FROM + "assets" "AssetEntity" + LEFT JOIN "exif" "AssetEntity__AssetEntity_exifInfo" ON "AssetEntity__AssetEntity_exifInfo"."assetId" = "AssetEntity"."id" + LEFT JOIN "asset_stack" "AssetEntity__AssetEntity_stack" ON "AssetEntity__AssetEntity_stack"."id" = "AssetEntity"."stackId" +WHERE + ( + ("AssetEntity"."ownerId" IN ($1)) + AND ("AssetEntity"."isVisible" = $2) + AND ("AssetEntity"."updatedAt" > $3) + ) diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index b3741bcf75..581ebe2277 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -159,10 +159,12 @@ SET COALESCE(SUM(exif."fileSizeInByte"), 0) FROM "assets" "assets" + LEFT JOIN "libraries" "library" ON "library"."id" = "assets"."libraryId" + AND ("library"."deletedAt" IS NULL) LEFT JOIN "exif" "exif" ON "exif"."assetId" = "assets"."id" WHERE "assets"."ownerId" = users.id - AND NOT "assets"."isExternal" + AND "library"."type" = 'UPLOAD' ), "updatedAt" = CURRENT_TIMESTAMP WHERE diff --git a/server/src/repositories/access.repository.ts b/server/src/repositories/access.repository.ts index fd74eb2ec9..469de11be6 100644 --- a/server/src/repositories/access.repository.ts +++ b/server/src/repositories/access.repository.ts @@ -1,3 +1,4 @@ +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ChunkedSet, DummyValue, GenerateSql } from 'src/decorators'; import { ActivityEntity } from 'src/entities/activity.entity'; @@ -25,6 +26,7 @@ type IPersonAccess = IAccessRepository['person']; type IPartnerAccess = IAccessRepository['partner']; @Instrumentation() +@Injectable() class ActivityAccess implements IActivityAccess { constructor( private activityRepository: Repository, diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index e6389c2e56..ddc666edd3 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -2,15 +2,18 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import path from 'node:path'; import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; -import { AssetOrder } from 'src/entities/album.entity'; +import { AlbumEntity, AssetOrder } from 'src/entities/album.entity'; import { AssetJobStatusEntity } from 'src/entities/asset-job-status.entity'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { ExifEntity } from 'src/entities/exif.entity'; +import { PartnerEntity } from 'src/entities/partner.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { AssetBuilderOptions, AssetCreate, + AssetDeltaSyncOptions, AssetExploreFieldOptions, + AssetFullSyncOptions, AssetPathEntity, AssetStats, AssetStatsOptions, @@ -39,6 +42,7 @@ import { FindOptionsWhere, In, IsNull, + MoreThan, Not, Repository, } from 'typeorm'; @@ -61,6 +65,8 @@ export class AssetRepository implements IAssetRepository { @InjectRepository(ExifEntity) private exifRepository: Repository, @InjectRepository(AssetJobStatusEntity) private jobStatusRepository: Repository, @InjectRepository(SmartInfoEntity) private smartInfoRepository: Repository, + @InjectRepository(PartnerEntity) private partnerRepository: Repository, + @InjectRepository(AlbumEntity) private albumRepository: Repository, ) {} async upsertExif(exif: Partial): Promise { @@ -160,10 +166,10 @@ export class AssetRepository implements IAssetRepository { } @GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] }) - getLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated { + getExternalLibraryAssetPaths(pagination: PaginationOptions, libraryId: string): Paginated { return paginate(this.repository, pagination, { select: { id: true, originalPath: true, isOffline: true }, - where: { library: { id: libraryId } }, + where: { library: { id: libraryId }, isExternal: true }, }); } @@ -265,8 +271,8 @@ export class AssetRepository implements IAssetRepository { } @GenerateSql({ params: [DummyValue.UUID, DummyValue.BUFFER] }) - getByChecksum(userId: string, checksum: Buffer): Promise { - return this.repository.findOne({ where: { ownerId: userId, checksum } }); + getByChecksum(libraryId: string, checksum: Buffer): Promise { + return this.repository.findOne({ where: { libraryId, checksum } }); } findLivePhotoMatch(options: LivePhotoSearchOptions): Promise { @@ -781,4 +787,55 @@ export class AssetRepository implements IAssetRepository { }) as AssetEntity, ); } + + @GenerateSql({ + params: [ + { + ownerId: DummyValue.UUID, + lastCreationDate: DummyValue.DATE, + lastId: DummyValue.STRING, + updatedUntil: DummyValue.DATE, + limit: 10, + }, + ], + }) + getAllForUserFullSync(options: AssetFullSyncOptions): Promise { + const { ownerId, lastCreationDate, lastId, updatedUntil, limit } = options; + let builder = this.repository + .createQueryBuilder('asset') + .leftJoinAndSelect('asset.exifInfo', 'exifInfo') + .leftJoinAndSelect('asset.stack', 'stack') + .where('asset.ownerId = :ownerId', { ownerId }); + if (lastCreationDate !== undefined && lastId !== undefined) { + builder = builder.andWhere('(asset.fileCreatedAt, asset.id) < (:lastCreationDate, :lastId)', { + lastCreationDate, + lastId, + }); + } + return builder + .andWhere('asset.updatedAt <= :updatedUntil', { updatedUntil }) + .andWhere('asset.isVisible = true') + .orderBy('asset.fileCreatedAt', 'DESC') + .addOrderBy('asset.id', 'DESC') + .limit(limit) + .withDeleted() + .getMany(); + } + + @GenerateSql({ params: [{ userIds: [DummyValue.UUID], updatedAfter: DummyValue.DATE }] }) + getChangedDeltaSync(options: AssetDeltaSyncOptions): Promise { + return this.repository.find({ + where: { + ownerId: In(options.userIds), + isVisible: true, + updatedAt: MoreThan(options.updatedAfter), + }, + relations: { + exifInfo: true, + stack: true, + }, + take: options.limit, + withDeleted: true, + }); + } } diff --git a/server/src/repositories/audit.repository.ts b/server/src/repositories/audit.repository.ts index 50f5631f3a..6cf5b76e6e 100644 --- a/server/src/repositories/audit.repository.ts +++ b/server/src/repositories/audit.repository.ts @@ -1,25 +1,28 @@ +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { AuditEntity } from 'src/entities/audit.entity'; import { AuditSearch, IAuditRepository } from 'src/interfaces/audit.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { LessThan, MoreThan, Repository } from 'typeorm'; +import { In, LessThan, MoreThan, Repository } from 'typeorm'; @Instrumentation() +@Injectable() export class AuditRepository implements IAuditRepository { constructor(@InjectRepository(AuditEntity) private repository: Repository) {} - getAfter(since: Date, options: AuditSearch): Promise { + getAfter(since: Date, options: AuditSearch): Promise { return this.repository .createQueryBuilder('audit') .where({ createdAt: MoreThan(since), action: options.action, entityType: options.entityType, - ownerId: options.ownerId, + ownerId: In(options.userIds), }) .distinctOn(['audit.entityId', 'audit.entityType']) .orderBy('audit.entityId, audit.entityType, audit.createdAt', 'DESC') - .getMany(); + .select('audit.entityId') + .getRawMany(); } async removeBefore(before: Date): Promise { diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 4ff24eeaa4..b9a04bffc8 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import AsyncLock from 'async-lock'; import { vectorExt } from 'src/database.config'; @@ -11,8 +11,8 @@ import { VectorUpdateResult, extName, } from 'src/interfaces/database.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { Version, VersionType } from 'src/utils/version'; import { isValidInteger } from 'src/validation'; import { DataSource, EntityManager, QueryRunner } from 'typeorm'; @@ -20,10 +20,14 @@ import { DataSource, EntityManager, QueryRunner } from 'typeorm'; @Instrumentation() @Injectable() export class DatabaseRepository implements IDatabaseRepository { - private logger = new ImmichLogger(DatabaseRepository.name); readonly asyncLock = new AsyncLock(); - constructor(@InjectDataSource() private dataSource: DataSource) {} + constructor( + @InjectDataSource() private dataSource: DataSource, + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(DatabaseRepository.name); + } async getExtensionVersion(extension: DatabaseExtension): Promise { const res = await this.dataSource.query(`SELECT extversion FROM pg_extension WHERE extname = $1`, [extension]); diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index be1de76c2d..33dfbfadc5 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -1,3 +1,4 @@ +import { Inject, Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { OnGatewayConnection, @@ -14,9 +15,9 @@ import { ServerEvent, ServerEventMap, } from 'src/interfaces/event.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthService } from 'src/services/auth.service'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; @Instrumentation() @WebSocketGateway({ @@ -24,16 +25,18 @@ import { ImmichLogger } from 'src/utils/logger'; path: '/api/socket.io', transports: ['websocket'], }) +@Injectable() export class EventRepository implements OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, IEventRepository { - private logger = new ImmichLogger(EventRepository.name); - @WebSocketServer() private server?: Server; constructor( private authService: AuthService, private eventEmitter: EventEmitter2, - ) {} + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(EventRepository.name); + } afterInit(server: Server) { this.logger.log('Initialized websocket server'); diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 336d5df0f0..e6466ee6b5 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -11,6 +11,7 @@ import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; @@ -41,6 +42,7 @@ import { DatabaseRepository } from 'src/repositories/database.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LibraryRepository } from 'src/repositories/library.repository'; +import { LoggerRepository } from 'src/repositories/logger.repository'; import { MachineLearningRepository } from 'src/repositories/machine-learning.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; @@ -71,6 +73,7 @@ export const repositories = [ { provide: IDatabaseRepository, useClass: DatabaseRepository }, { provide: IEventRepository, useClass: EventRepository }, { provide: IJobRepository, useClass: JobRepository }, + { provide: ILoggerRepository, useClass: LoggerRepository }, { provide: ILibraryRepository, useClass: LibraryRepository }, { provide: IKeyRepository, useClass: ApiKeyRepository }, { provide: IMachineLearningRepository, useClass: MachineLearningRepository }, diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index a7c99f93cb..858798b88d 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -1,5 +1,5 @@ import { getQueueToken } from '@nestjs/bullmq'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { SchedulerRegistry } from '@nestjs/schedule'; import { Job, JobsOptions, Processor, Queue, Worker, WorkerOptions } from 'bullmq'; @@ -15,8 +15,8 @@ import { QueueName, QueueStatus, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; export const JOBS_TO_QUEUE: Record = { // misc @@ -83,12 +83,14 @@ export const JOBS_TO_QUEUE: Record = { @Injectable() export class JobRepository implements IJobRepository { private workers: Partial> = {}; - private logger = new ImmichLogger(JobRepository.name); constructor( private moduleReference: ModuleRef, private schedulerReqistry: SchedulerRegistry, - ) {} + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(JobRepository.name); + } addHandler(queueName: QueueName, concurrency: number, handler: (item: JobItem) => Promise) { const workerHandler: Processor = async (job: Job) => handler(job as JobItem); diff --git a/server/src/repositories/logger.repository.ts b/server/src/repositories/logger.repository.ts new file mode 100644 index 0000000000..65ccd8ea47 --- /dev/null +++ b/server/src/repositories/logger.repository.ts @@ -0,0 +1,27 @@ +import { Injectable, Scope } from '@nestjs/common'; +import { ClsService } from 'nestjs-cls'; +import { LogLevel } from 'src/entities/system-config.entity'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { ImmichLogger } from 'src/utils/logger'; + +@Injectable({ scope: Scope.TRANSIENT }) +export class LoggerRepository extends ImmichLogger implements ILoggerRepository { + constructor(private cls: ClsService) { + super(LoggerRepository.name); + } + + protected formatContext(context: string): string { + let formattedContext = super.formatContext(context); + + const correlationId = this.cls?.getId(); + if (correlationId && this.isLevelEnabled(LogLevel.VERBOSE)) { + formattedContext += `[${correlationId}] `; + } + + return formattedContext; + } + + setLogLevel(level: LogLevel): void { + ImmichLogger.setLogLevel(level); + } +} diff --git a/server/src/repositories/media.repository.ts b/server/src/repositories/media.repository.ts index 52a538909f..3936ad7e42 100644 --- a/server/src/repositories/media.repository.ts +++ b/server/src/repositories/media.repository.ts @@ -1,9 +1,11 @@ +import { Inject, Injectable } from '@nestjs/common'; import ffmpeg, { FfprobeData } from 'fluent-ffmpeg'; import fs from 'node:fs/promises'; import { Writable } from 'node:stream'; import { promisify } from 'node:util'; import sharp from 'sharp'; import { Colorspace } from 'src/entities/system-config.entity'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { CropOptions, IMediaRepository, @@ -12,7 +14,6 @@ import { VideoInfo, } from 'src/interfaces/media.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { handlePromiseError } from 'src/utils/misc'; const probe = promisify(ffmpeg.ffprobe); @@ -20,9 +21,11 @@ sharp.concurrency(0); sharp.cache({ files: 0 }); @Instrumentation() +@Injectable() export class MediaRepository implements IMediaRepository { - private logger = new ImmichLogger(MediaRepository.name); - + constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { + this.logger.setContext(MediaRepository.name); + } crop(input: string | Buffer, options: CropOptions): Promise { return sharp(input, { failOn: 'none' }) .pipelineColorspace('rgb16') diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index 511023a8ea..8eeb0064ac 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -1,4 +1,4 @@ -import { Inject } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; import { DefaultReadTaskOptions, Tags, exiftool } from 'exiftool-vendored'; import geotz from 'geo-tz'; @@ -11,24 +11,26 @@ import { DummyValue, GenerateSql } from 'src/decorators'; import { ExifEntity } from 'src/entities/exif.entity'; import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { GeoPoint, IMetadataRepository, ImmichTags, ReverseGeocodeResult } from 'src/interfaces/metadata.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { DataSource, QueryRunner, Repository } from 'typeorm'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; @Instrumentation() +@Injectable() export class MetadataRepository implements IMetadataRepository { constructor( @InjectRepository(ExifEntity) private exifRepository: Repository, - @InjectRepository(GeodataPlacesEntity) private readonly geodataPlacesRepository: Repository, + @InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository, @Inject(ISystemMetadataRepository) - private readonly systemMetadataRepository: ISystemMetadataRepository, + private systemMetadataRepository: ISystemMetadataRepository, @InjectDataSource() private dataSource: DataSource, - ) {} - - private logger = new ImmichLogger(MetadataRepository.name); + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(MetadataRepository.name); + } async init(): Promise { this.logger.log('Initializing metadata repository'); diff --git a/server/src/repositories/metric.repository.ts b/server/src/repositories/metric.repository.ts index c6eb953acf..5948e92fa6 100644 --- a/server/src/repositories/metric.repository.ts +++ b/server/src/repositories/metric.repository.ts @@ -6,7 +6,7 @@ import { apiMetrics, hostMetrics, jobMetrics, repoMetrics } from 'src/utils/inst class MetricGroupRepository implements IMetricGroupRepository { private enabled = false; - constructor(private readonly metricService: MetricService) {} + constructor(private metricService: MetricService) {} addToCounter(name: string, value: number, options?: MetricOptions): void { if (this.enabled) { diff --git a/server/src/repositories/partner.repository.ts b/server/src/repositories/partner.repository.ts index 8465493b54..e0c8998dbf 100644 --- a/server/src/repositories/partner.repository.ts +++ b/server/src/repositories/partner.repository.ts @@ -8,7 +8,7 @@ import { DeepPartial, Repository } from 'typeorm'; @Instrumentation() @Injectable() export class PartnerRepository implements IPartnerRepository { - constructor(@InjectRepository(PartnerEntity) private readonly repository: Repository) {} + constructor(@InjectRepository(PartnerEntity) private repository: Repository) {} getAll(userId: string): Promise { return this.repository.find({ where: [{ sharedWithId: userId }, { sharedById: userId }] }); diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 0acbafe699..7ffc6bf2b7 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -1,3 +1,4 @@ +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import _ from 'lodash'; import { ChunkedArray, DummyValue, GenerateSql } from 'src/decorators'; @@ -19,6 +20,7 @@ import { Paginated, PaginationOptions, paginate } from 'src/utils/pagination'; import { FindManyOptions, FindOptionsRelations, FindOptionsSelect, In, Repository } from 'typeorm'; @Instrumentation() +@Injectable() export class PersonRepository implements IPersonRepository { constructor( @InjectRepository(AssetEntity) private assetRepository: Repository, diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 4530d2295f..6ac49a3190 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { vectorExt } from 'src/database.config'; import { DummyValue, GenerateSql } from 'src/decorators'; @@ -8,6 +8,7 @@ import { GeodataPlacesEntity } from 'src/entities/geodata-places.entity'; import { SmartInfoEntity } from 'src/entities/smart-info.entity'; import { SmartSearchEntity } from 'src/entities/smart-search.entity'; import { DatabaseExtension } from 'src/interfaces/database.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AssetSearchOptions, FaceEmbeddingSearch, @@ -18,7 +19,6 @@ import { } from 'src/interfaces/search.interface'; import { asVector, searchAssetBuilder } from 'src/utils/database'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { getCLIPModelInfo } from 'src/utils/misc'; import { Paginated, PaginationMode, PaginationResult, paginatedBuilder } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -27,7 +27,6 @@ import { Repository, SelectQueryBuilder } from 'typeorm'; @Instrumentation() @Injectable() export class SearchRepository implements ISearchRepository { - private logger = new ImmichLogger(SearchRepository.name); private faceColumns: string[]; private assetsByCityQuery: string; @@ -36,8 +35,10 @@ export class SearchRepository implements ISearchRepository { @InjectRepository(AssetEntity) private assetRepository: Repository, @InjectRepository(AssetFaceEntity) private assetFaceRepository: Repository, @InjectRepository(SmartSearchEntity) private smartSearchRepository: Repository, - @InjectRepository(GeodataPlacesEntity) private readonly geodataPlacesRepository: Repository, + @InjectRepository(GeodataPlacesEntity) private geodataPlacesRepository: Repository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { + this.logger.setContext(SearchRepository.name); this.faceColumns = this.assetFaceRepository.manager.connection .getMetadata(AssetFaceEntity) .ownColumns.map((column) => column.propertyName) diff --git a/server/src/repositories/storage.repository.spec.ts b/server/src/repositories/storage.repository.spec.ts index b92a26904f..44c81d76a6 100644 --- a/server/src/repositories/storage.repository.spec.ts +++ b/server/src/repositories/storage.repository.spec.ts @@ -1,6 +1,8 @@ import mockfs from 'mock-fs'; import { CrawlOptionsDto } from 'src/dtos/library.dto'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { StorageRepository } from 'src/repositories/storage.repository'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; interface Test { test: string; @@ -181,9 +183,11 @@ const tests: Test[] = [ describe(StorageRepository.name, () => { let sut: StorageRepository; + let logger: ILoggerRepository; beforeEach(() => { - sut = new StorageRepository(); + logger = newLoggerRepositoryMock(); + sut = new StorageRepository(logger); }); afterEach(() => { diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 7e1c8d59e2..ae374ac812 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -1,3 +1,4 @@ +import { Inject, Injectable } from '@nestjs/common'; import archiver from 'archiver'; import chokidar, { WatchOptions } from 'chokidar'; import { glob, globStream } from 'fast-glob'; @@ -5,21 +6,23 @@ import { constants, createReadStream, existsSync, mkdirSync } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; import { CrawlOptionsDto } from 'src/dtos/library.dto'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { DiskUsage, IStorageRepository, ImmichReadStream, ImmichZipStream, - StorageEventType, WatchEvents, } from 'src/interfaces/storage.interface'; import { Instrumentation } from 'src/utils/instrumentation'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; @Instrumentation() +@Injectable() export class StorageRepository implements IStorageRepository { - private logger = new ImmichLogger(StorageRepository.name); + constructor(@Inject(ILoggerRepository) private logger: ILoggerRepository) { + this.logger.setContext(StorageRepository.name); + } readdir(folder: string): Promise { return fs.readdir(folder); @@ -173,11 +176,11 @@ export class StorageRepository implements IStorageRepository { watch(paths: string[], options: WatchOptions, events: Partial) { const watcher = chokidar.watch(paths, options); - watcher.on(StorageEventType.READY, () => events.onReady?.()); - watcher.on(StorageEventType.ADD, (path) => events.onAdd?.(path)); - watcher.on(StorageEventType.CHANGE, (path) => events.onChange?.(path)); - watcher.on(StorageEventType.UNLINK, (path) => events.onUnlink?.(path)); - watcher.on(StorageEventType.ERROR, (error) => events.onError?.(error)); + watcher.on('ready', () => events.onReady?.()); + watcher.on('add', (path) => events.onAdd?.(path)); + watcher.on('change', (path) => events.onChange?.(path)); + watcher.on('unlink', (path) => events.onUnlink?.(path)); + watcher.on('error', (error) => events.onError?.(error)); return () => watcher.close(); } diff --git a/server/src/repositories/system-config.repository.ts b/server/src/repositories/system-config.repository.ts index baa3218b00..3d2dbecbc8 100644 --- a/server/src/repositories/system-config.repository.ts +++ b/server/src/repositories/system-config.repository.ts @@ -1,3 +1,4 @@ +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { readFile } from 'node:fs/promises'; import { Chunked, DummyValue, GenerateSql } from 'src/decorators'; @@ -7,6 +8,7 @@ import { Instrumentation } from 'src/utils/instrumentation'; import { In, Repository } from 'typeorm'; @Instrumentation() +@Injectable() export class SystemConfigRepository implements ISystemConfigRepository { constructor( @InjectRepository(SystemConfigEntity) diff --git a/server/src/repositories/system-metadata.repository.ts b/server/src/repositories/system-metadata.repository.ts index 80936e46f6..91b887a176 100644 --- a/server/src/repositories/system-metadata.repository.ts +++ b/server/src/repositories/system-metadata.repository.ts @@ -1,3 +1,4 @@ +import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { SystemMetadata, SystemMetadataEntity } from 'src/entities/system-metadata.entity'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; @@ -5,6 +6,7 @@ import { Instrumentation } from 'src/utils/instrumentation'; import { Repository } from 'typeorm'; @Instrumentation() +@Injectable() export class SystemMetadataRepository implements ISystemMetadataRepository { constructor( @InjectRepository(SystemMetadataEntity) diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index f0e00d0496..0435d76d19 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetEntity } from 'src/entities/asset.entity'; +import { LibraryType } from 'src/entities/library.entity'; import { UserEntity } from 'src/entities/user.entity'; import { IUserRepository, @@ -117,11 +118,14 @@ export class UserRepository implements IUserRepository { @GenerateSql({ params: [DummyValue.UUID] }) async syncUsage(id?: string) { + // we can't use parameters with getQuery, hence the template string const subQuery = this.assetRepository .createQueryBuilder('assets') .select('COALESCE(SUM(exif."fileSizeInByte"), 0)') + .leftJoin('assets.library', 'library') .leftJoin('assets.exifInfo', 'exif') - .where('assets.ownerId = users.id AND NOT assets.isExternal') + .where('assets.ownerId = users.id') + .andWhere(`library.type = '${LibraryType.UPLOAD}'`) .withDeleted(); const query = this.userRepository diff --git a/server/src/services/activity.service.spec.ts b/server/src/services/activity.service.spec.ts index e7049ea6c9..30720b6c1f 100644 --- a/server/src/services/activity.service.spec.ts +++ b/server/src/services/activity.service.spec.ts @@ -6,11 +6,12 @@ import { activityStub } from 'test/fixtures/activity.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newActivityRepositoryMock } from 'test/repositories/activity.repository.mock'; +import { Mocked } from 'vitest'; describe(ActivityService.name, () => { let sut: ActivityService; let accessMock: IAccessRepositoryMock; - let activityMock: jest.Mocked; + let activityMock: Mocked; beforeEach(() => { accessMock = newAccessRepositoryMock(); diff --git a/server/src/services/album.service.spec.ts b/server/src/services/album.service.spec.ts index 02bb607b52..78ee92395d 100644 --- a/server/src/services/album.service.spec.ts +++ b/server/src/services/album.service.spec.ts @@ -12,13 +12,14 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked } from 'vitest'; describe(AlbumService.name, () => { let sut: AlbumService; let accessMock: IAccessRepositoryMock; - let albumMock: jest.Mocked; - let assetMock: jest.Mocked; - let userMock: jest.Mocked; + let albumMock: Mocked; + let assetMock: Mocked; + let userMock: Mocked; beforeEach(() => { accessMock = newAccessRepositoryMock(); @@ -175,6 +176,7 @@ describe(AlbumService.name, () => { it('creates album', async () => { albumMock.create.mockResolvedValue(albumStub.empty); userMock.get.mockResolvedValue(userStub.user1); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['123'])); await sut.create(authStub.admin, { albumName: 'Empty album', @@ -193,6 +195,7 @@ describe(AlbumService.name, () => { }); expect(userMock.get).toHaveBeenCalledWith('user-id', {}); + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['123'])); }); it('should require valid userIds', async () => { @@ -206,6 +209,31 @@ describe(AlbumService.name, () => { expect(userMock.get).toHaveBeenCalledWith('user-3', {}); expect(albumMock.create).not.toHaveBeenCalled(); }); + + it('should only add assets the user is allowed to access', async () => { + userMock.get.mockResolvedValue(userStub.user1); + albumMock.create.mockResolvedValue(albumStub.oneAsset); + accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); + + await sut.create(authStub.admin, { + albumName: 'Test album', + description: '', + assetIds: ['asset-1', 'asset-2'], + }); + + expect(albumMock.create).toHaveBeenCalledWith({ + ownerId: authStub.admin.user.id, + albumName: 'Test album', + description: '', + sharedUsers: [], + assets: [{ id: 'asset-1' }], + albumThumbnailAssetId: 'asset-1', + }); + expect(accessMock.asset.checkOwnerAccess).toHaveBeenCalledWith( + authStub.admin.user.id, + new Set(['asset-1', 'asset-2']), + ); + }); }); describe('update', () => { diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index df6c6b814c..b3b7f6d08a 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -119,13 +119,16 @@ export class AlbumService { } } + const allowedAssetIdsSet = await this.access.checkAccess(auth, Permission.ASSET_SHARE, new Set(dto.assetIds)); + const assets = [...allowedAssetIdsSet].map((id) => ({ id }) as AssetEntity); + const album = await this.albumRepository.create({ ownerId: auth.user.id, albumName: dto.albumName, description: dto.description, sharedUsers: dto.sharedWithUserIds?.map((value) => ({ id: value }) as UserEntity) ?? [], - assets: (dto.assetIds || []).map((id) => ({ id }) as AssetEntity), - albumThumbnailAssetId: dto.assetIds?.[0] || null, + assets, + albumThumbnailAssetId: assets[0]?.id || null, }); return mapAlbumWithAssets(album); diff --git a/server/src/services/api-key.service.spec.ts b/server/src/services/api-key.service.spec.ts index 47fd0f5159..2b5efc674f 100644 --- a/server/src/services/api-key.service.spec.ts +++ b/server/src/services/api-key.service.spec.ts @@ -6,11 +6,12 @@ import { keyStub } from 'test/fixtures/api-key.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; +import { Mocked } from 'vitest'; describe(APIKeyService.name, () => { let sut: APIKeyService; - let keyMock: jest.Mocked; - let cryptoMock: jest.Mocked; + let keyMock: Mocked; + let cryptoMock: Mocked; beforeEach(() => { cryptoMock = newCryptoRepositoryMock(); diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index 87107b23f4..fb9912da95 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -1,9 +1,10 @@ -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Cron, CronExpression, Interval } from '@nestjs/schedule'; import { NextFunction, Request, Response } from 'express'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { ONE_HOUR, WEB_ROOT } from 'src/constants'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AuthService } from 'src/services/auth.service'; import { DatabaseService } from 'src/services/database.service'; import { JobService } from 'src/services/job.service'; @@ -11,7 +12,6 @@ import { ServerInfoService } from 'src/services/server-info.service'; import { SharedLinkService } from 'src/services/shared-link.service'; import { StorageService } from 'src/services/storage.service'; import { SystemConfigService } from 'src/services/system-config.service'; -import { ImmichLogger } from 'src/utils/logger'; import { OpenGraphTags } from 'src/utils/misc'; const render = (index: string, meta: OpenGraphTags) => { @@ -36,8 +36,6 @@ const render = (index: string, meta: OpenGraphTags) => { @Injectable() export class ApiService { - private logger = new ImmichLogger(ApiService.name); - constructor( private authService: AuthService, private configService: SystemConfigService, @@ -46,7 +44,10 @@ export class ApiService { private sharedLinkService: SharedLinkService, private storageService: StorageService, private databaseService: DatabaseService, - ) {} + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(ApiService.name); + } @Interval(ONE_HOUR.as('milliseconds')) async onVersionCheck() { diff --git a/server/src/services/asset-v1.service.spec.ts b/server/src/services/asset-v1.service.spec.ts index 735ac8322a..bc088cb30b 100644 --- a/server/src/services/asset-v1.service.spec.ts +++ b/server/src/services/asset-v1.service.spec.ts @@ -1,4 +1,3 @@ -import { when } from 'jest-when'; import { AssetRejectReason, AssetUploadAction } from 'src/dtos/asset-v1-response.dto'; import { CreateAssetDto } from 'src/dtos/asset-v1.dto'; import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity'; @@ -7,6 +6,7 @@ import { IAssetRepositoryV1 } from 'src/interfaces/asset-v1.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { AssetServiceV1 } from 'src/services/asset-v1.service'; @@ -17,9 +17,11 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { QueryFailedError } from 'typeorm'; +import { Mocked, vitest } from 'vitest'; const _getCreateAssetDto = (): CreateAssetDto => { const createAssetDto = new CreateAssetDto(); @@ -62,40 +64,50 @@ const _getAsset_1 = () => { describe('AssetService', () => { let sut: AssetServiceV1; let accessMock: IAccessRepositoryMock; - let assetRepositoryMockV1: jest.Mocked; - let assetMock: jest.Mocked; - let jobMock: jest.Mocked; - let libraryMock: jest.Mocked; - let storageMock: jest.Mocked; - let userMock: jest.Mocked; + let assetRepositoryMockV1: Mocked; + let assetMock: Mocked; + let jobMock: Mocked; + let libraryMock: Mocked; + let loggerMock: Mocked; + let storageMock: Mocked; + let userMock: Mocked; beforeEach(() => { assetRepositoryMockV1 = { - get: jest.fn(), - getAllByUserId: jest.fn(), - getDetectedObjectsByUserId: jest.fn(), - getLocationsByUserId: jest.fn(), - getSearchPropertiesByUserId: jest.fn(), - getAssetsByChecksums: jest.fn(), - getExistingAssets: jest.fn(), - getByOriginalPath: jest.fn(), + get: vitest.fn(), + getAllByUserId: vitest.fn(), + getDetectedObjectsByUserId: vitest.fn(), + getLocationsByUserId: vitest.fn(), + getSearchPropertiesByUserId: vitest.fn(), + getAssetsByChecksums: vitest.fn(), + getExistingAssets: vitest.fn(), + getByOriginalPath: vitest.fn(), }; accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); jobMock = newJobRepositoryMock(); libraryMock = newLibraryRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); - sut = new AssetServiceV1(accessMock, assetRepositoryMockV1, assetMock, jobMock, libraryMock, storageMock, userMock); + sut = new AssetServiceV1( + accessMock, + assetRepositoryMockV1, + assetMock, + jobMock, + libraryMock, + storageMock, + userMock, + loggerMock, + ); - when(assetRepositoryMockV1.get) - .calledWith(assetStub.livePhotoStillAsset.id) - .mockResolvedValue(assetStub.livePhotoStillAsset); - when(assetRepositoryMockV1.get) - .calledWith(assetStub.livePhotoMotionAsset.id) - .mockResolvedValue(assetStub.livePhotoMotionAsset); + assetRepositoryMockV1.get.mockImplementation((assetId) => + Promise.resolve( + [assetStub.livePhotoMotionAsset, assetStub.livePhotoMotionAsset].find((asset) => asset.id === assetId) ?? null, + ), + ); }); describe('uploadFile', () => { diff --git a/server/src/services/asset-v1.service.ts b/server/src/services/asset-v1.service.ts index 97aa99d91d..61fe5bd80b 100644 --- a/server/src/services/asset-v1.service.ts +++ b/server/src/services/asset-v1.service.ts @@ -33,18 +33,17 @@ import { IAssetRepositoryV1 } from 'src/interfaces/asset-v1.interface'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { UploadFile } from 'src/services/asset.service'; import { CacheControl, ImmichFileResponse, getLivePhotoMotionFilename } from 'src/utils/file'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; import { QueryFailedError } from 'typeorm'; @Injectable() /** @deprecated */ export class AssetServiceV1 { - readonly logger = new ImmichLogger(AssetServiceV1.name); private access: AccessCore; constructor( @@ -55,8 +54,10 @@ export class AssetServiceV1 { @Inject(ILibraryRepository) private libraryRepository: ILibraryRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IUserRepository) private userRepository: IUserRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { this.access = AccessCore.create(accessRepository); + this.logger.setContext(AssetServiceV1.name); } public async uploadFile( diff --git a/server/src/services/asset.service.spec.ts b/server/src/services/asset.service.spec.ts index b90c194aa0..1516ef8961 100755 --- a/server/src/services/asset.service.spec.ts +++ b/server/src/services/asset.service.spec.ts @@ -1,12 +1,12 @@ import { BadRequestException, UnauthorizedException } from '@nestjs/common'; -import { when } from 'jest-when'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AssetJobName, AssetStatsResponseDto, UploadFieldName } from 'src/dtos/asset.dto'; import { AssetEntity, AssetType } from 'src/entities/asset.entity'; import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; import { AssetStats, IAssetRepository } from 'src/interfaces/asset.interface'; import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; -import { IJobRepository, JobItem, JobName } from 'src/interfaces/job.interface'; +import { IJobRepository, JobName } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; @@ -22,10 +22,12 @@ import { newAssetStackRepositoryMock } from 'test/repositories/asset-stack.repos import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked, vitest } from 'vitest'; const stats: AssetStats = { [AssetType.IMAGE]: 10, @@ -148,19 +150,26 @@ const uploadTests = [ describe(AssetService.name, () => { let sut: AssetService; let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let jobMock: jest.Mocked; - let storageMock: jest.Mocked; - let userMock: jest.Mocked; - let eventMock: jest.Mocked; - let configMock: jest.Mocked; - let partnerMock: jest.Mocked; - let assetStackMock: jest.Mocked; + let assetMock: Mocked; + let jobMock: Mocked; + let storageMock: Mocked; + let userMock: Mocked; + let eventMock: Mocked; + let configMock: Mocked; + let partnerMock: Mocked; + let assetStackMock: Mocked; + let loggerMock: Mocked; it('should work', () => { expect(sut).toBeDefined(); }); + const mockGetById = (assets: AssetEntity[]) => { + assetMock.getById.mockImplementation((assetId) => + Promise.resolve(assets.find((asset) => asset.id === assetId) ?? null), + ); + }; + beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); @@ -171,6 +180,7 @@ describe(AssetService.name, () => { configMock = newSystemConfigRepositoryMock(); partnerMock = newPartnerRepositoryMock(); assetStackMock = newAssetStackRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); sut = new AssetService( accessMock, @@ -182,14 +192,10 @@ describe(AssetService.name, () => { eventMock, partnerMock, assetStackMock, + loggerMock, ); - when(assetMock.getById) - .calledWith(assetStub.livePhotoStillAsset.id) - .mockResolvedValue(assetStub.livePhotoStillAsset as AssetEntity); - when(assetMock.getById) - .calledWith(assetStub.livePhotoMotionAsset.id) - .mockResolvedValue(assetStub.livePhotoMotionAsset as AssetEntity); + mockGetById([assetStub.livePhotoStillAsset, assetStub.livePhotoMotionAsset]); }); describe('canUpload', () => { @@ -299,12 +305,12 @@ describe(AssetService.name, () => { describe('getMemoryLane', () => { beforeAll(() => { - jest.useFakeTimers(); - jest.setSystemTime(new Date('2024-01-15')); + vitest.useFakeTimers(); + vitest.setSystemTime(new Date('2024-01-15')); }); afterAll(() => { - jest.useRealTimers(); + vitest.useRealTimers(); }); it('should group the assets correctly', async () => { @@ -469,9 +475,7 @@ describe(AssetService.name, () => { it('should update parent asset updatedAt when children are added', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['parent'])); - when(assetMock.getById) - .calledWith('parent', { stack: { assets: true } }) - .mockResolvedValue(assetStub.image); + mockGetById([{ ...assetStub.image, id: 'parent' }]); await sut.updateAll(authStub.user1, { ids: [], stackParentId: 'parent', @@ -488,9 +492,7 @@ describe(AssetService.name, () => { stack: assetStackStub('stack-1', [{ id: 'parent' } as AssetEntity, { id: 'child-1' } as AssetEntity]), } as AssetEntity, ]); - when(assetStackMock.getById) - .calledWith('stack-1') - .mockResolvedValue(assetStackStub('stack-1', [{ id: 'parent' } as AssetEntity])); + assetStackMock.getById.mockResolvedValue(assetStackStub('stack-1', [{ id: 'parent' } as AssetEntity])); await sut.updateAll(authStub.user1, { ids: ['child-1'], @@ -511,12 +513,10 @@ describe(AssetService.name, () => { { id: 'child-1' } as AssetEntity, { id: 'child-2' } as AssetEntity, ]); - when(assetMock.getById) - .calledWith('parent', { stack: { assets: true } }) - .mockResolvedValue({ - id: 'child-1', - stack, - } as AssetEntity); + assetMock.getById.mockResolvedValue({ + id: 'child-1', + stack, + } as AssetEntity); await sut.updateAll(authStub.user1, { stackParentId: 'parent', @@ -547,9 +547,7 @@ describe(AssetService.name, () => { it('merge stacks if new child has children', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['child-1'])); accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['parent'])); - when(assetMock.getById) - .calledWith('parent', { stack: { assets: true } }) - .mockResolvedValue({ ...assetStub.image, id: 'parent' }); + assetMock.getById.mockResolvedValue({ ...assetStub.image, id: 'parent' }); assetMock.getByIds.mockResolvedValue([ { id: 'child-1', @@ -557,9 +555,7 @@ describe(AssetService.name, () => { stack: assetStackStub('stack-1', [{ id: 'child-1' } as AssetEntity, { id: 'child-2' } as AssetEntity]), } as AssetEntity, ]); - when(assetStackMock.getById) - .calledWith('stack-1') - .mockResolvedValue(assetStackStub('stack-1', [{ id: 'parent' } as AssetEntity])); + assetStackMock.getById.mockResolvedValue(assetStackStub('stack-1', [{ id: 'parent' } as AssetEntity])); await sut.updateAll(authStub.user1, { ids: ['child-1'], @@ -579,9 +575,7 @@ describe(AssetService.name, () => { it('should send ws asset update event', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['asset-1'])); accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['parent'])); - when(assetMock.getById) - .calledWith('parent', { stack: { assets: true } }) - .mockResolvedValue(assetStub.image); + assetMock.getById.mockResolvedValue(assetStub.image); await sut.updateAll(authStub.user1, { ids: ['asset-1'], @@ -626,32 +620,10 @@ describe(AssetService.name, () => { }); describe('handleAssetDeletion', () => { - beforeEach(() => { - when(jobMock.queue) - .calledWith( - expect.objectContaining({ - name: JobName.ASSET_DELETION, - }), - ) - .mockImplementation(async (item: JobItem) => { - const jobData = (item as { data?: any })?.data || {}; - await sut.handleAssetDeletion(jobData); - }); - }); - it('should remove faces', async () => { const assetWithFace = { ...assetStub.image, faces: [faceStub.face1, faceStub.mergeFace1] }; - when(assetMock.getById) - .calledWith(assetWithFace.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetWithFace); + assetMock.getById.mockResolvedValue(assetWithFace); await sut.handleAssetDeletion({ id: assetWithFace.id }); @@ -676,16 +648,7 @@ describe(AssetService.name, () => { }); it('should update stack primary asset if deleted asset was primary asset in a stack', async () => { - when(assetMock.getById) - .calledWith(assetStub.primaryImage.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.primaryImage as AssetEntity); + assetMock.getById.mockResolvedValue(assetStub.primaryImage as AssetEntity); await sut.handleAssetDeletion({ id: assetStub.primaryImage.id }); @@ -695,27 +658,31 @@ describe(AssetService.name, () => { }); }); - it('should not schedule delete-files job for readonly assets', async () => { - when(assetMock.getById) - .calledWith(assetStub.readOnly.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.readOnly); + it('should only delete generated files for readonly assets', async () => { + assetMock.getById.mockResolvedValue(assetStub.readOnly); await sut.handleAssetDeletion({ id: assetStub.readOnly.id }); - expect(jobMock.queue.mock.calls).toEqual([]); + expect(jobMock.queue.mock.calls).toEqual([ + [ + { + name: JobName.DELETE_FILES, + data: { + files: [ + assetStub.readOnly.thumbnailPath, + assetStub.readOnly.previewPath, + assetStub.readOnly.encodedVideoPath, + ], + }, + }, + ], + ]); expect(assetMock.remove).toHaveBeenCalledWith(assetStub.readOnly); }); it('should not process assets from external library without fromExternal flag', async () => { - when(assetMock.getById).calledWith(assetStub.external.id).mockResolvedValue(assetStub.external); + assetMock.getById.mockResolvedValue(assetStub.external); await sut.handleAssetDeletion({ id: assetStub.external.id }); @@ -725,16 +692,7 @@ describe(AssetService.name, () => { }); it('should process assets from external library with fromExternal flag', async () => { - when(assetMock.getById) - .calledWith(assetStub.external.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.external); + assetMock.getById.mockResolvedValue(assetStub.external); await sut.handleAssetDeletion({ id: assetStub.external.id, fromExternal: true }); @@ -748,7 +706,6 @@ describe(AssetService.name, () => { assetStub.external.thumbnailPath, assetStub.external.previewPath, assetStub.external.encodedVideoPath, - assetStub.external.sidecarPath, ], }, }, @@ -757,39 +714,12 @@ describe(AssetService.name, () => { }); it('should delete a live photo', async () => { - when(assetMock.getById) - .calledWith(assetStub.livePhotoStillAsset.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.livePhotoStillAsset); - when(assetMock.getById) - .calledWith(assetStub.livePhotoMotionAsset.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.livePhotoMotionAsset); + assetMock.getById.mockResolvedValue(assetStub.livePhotoStillAsset); await sut.handleAssetDeletion({ id: assetStub.livePhotoStillAsset.id }); expect(jobMock.queue.mock.calls).toEqual([ [{ name: JobName.ASSET_DELETION, data: { id: assetStub.livePhotoMotionAsset.id } }], - [ - { - name: JobName.DELETE_FILES, - data: { - files: [undefined, undefined, undefined, undefined, 'fake_path/asset_1.mp4'], - }, - }, - ], [ { name: JobName.DELETE_FILES, @@ -802,18 +732,8 @@ describe(AssetService.name, () => { }); it('should update usage', async () => { - when(assetMock.getById) - .calledWith(assetStub.image.id, { - faces: { - person: true, - }, - library: true, - stack: { assets: true }, - exifInfo: true, - }) - .mockResolvedValue(assetStub.image); + assetMock.getById.mockResolvedValue(assetStub.image); await sut.handleAssetDeletion({ id: assetStub.image.id }); - expect(userMock.updateUsage).toHaveBeenCalledWith(assetStub.image.ownerId, -5000); }); }); @@ -862,18 +782,7 @@ describe(AssetService.name, () => { it('make old parent the child of new parent', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set([assetStub.image.id])); accessMock.asset.checkOwnerAccess.mockResolvedValueOnce(new Set(['new'])); - - when(assetMock.getById) - .calledWith(assetStub.image.id, { - faces: { - person: true, - }, - library: true, - stack: { - assets: true, - }, - }) - .mockResolvedValue({ ...assetStub.image, stackId: 'stack-1' }); + assetMock.getById.mockResolvedValue({ ...assetStub.image, stackId: 'stack-1' }); await sut.updateStackParent(authStub.user1, { oldParentId: assetStub.image.id, diff --git a/server/src/services/asset.service.ts b/server/src/services/asset.service.ts index 135377e0bd..a26b7f5a73 100644 --- a/server/src/services/asset.service.ts +++ b/server/src/services/asset.service.ts @@ -40,11 +40,11 @@ import { JobName, JobStatus, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; import { usePagination } from 'src/utils/pagination'; @@ -63,7 +63,6 @@ export interface UploadFile { } export class AssetService { - private logger = new ImmichLogger(AssetService.name); private access: AccessCore; private configCore: SystemConfigCore; @@ -77,9 +76,11 @@ export class AssetService { @Inject(IEventRepository) private eventRepository: IEventRepository, @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, @Inject(IAssetStackRepository) private assetStackRepository: IAssetStackRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { + this.logger.setContext(AssetService.name); this.access = AccessCore.create(accessRepository); - this.configCore = SystemConfigCore.create(configRepository); + this.configCore = SystemConfigCore.create(configRepository, this.logger); } canUploadFile({ auth, fieldName, file }: UploadRequest): true { @@ -391,22 +392,25 @@ export class AssetService { } await this.assetRepository.remove(asset); - await this.userRepository.updateUsage(asset.ownerId, -(asset.exifInfo?.fileSizeInByte || 0)); + if (asset.library.type === LibraryType.UPLOAD) { + await this.userRepository.updateUsage(asset.ownerId, -(asset.exifInfo?.fileSizeInByte || 0)); + } this.eventRepository.clientSend(ClientEvent.ASSET_DELETE, asset.ownerId, id); // TODO refactor this to use cascades if (asset.livePhotoVideoId) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } }); + await this.jobRepository.queue({ + name: JobName.ASSET_DELETION, + data: { id: asset.livePhotoVideoId, fromExternal }, + }); } - const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath, asset.sidecarPath]; - if (!fromExternal) { - files.push(asset.originalPath); + const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath]; + if (!(asset.isExternal || asset.isReadOnly)) { + files.push(asset.sidecarPath, asset.originalPath); } - if (!asset.isReadOnly) { - await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files } }); - } + await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files } }); return JobStatus.SUCCESS; } diff --git a/server/src/services/audit.service.spec.ts b/server/src/services/audit.service.spec.ts index 4af5c1f94d..8557677f92 100644 --- a/server/src/services/audit.service.spec.ts +++ b/server/src/services/audit.service.spec.ts @@ -3,6 +3,7 @@ import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAuditRepository } from 'src/interfaces/audit.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; @@ -13,19 +14,22 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newAuditRepositoryMock } from 'test/repositories/audit.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked } from 'vitest'; describe(AuditService.name, () => { let sut: AuditService; let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let auditMock: jest.Mocked; - let cryptoMock: jest.Mocked; - let personMock: jest.Mocked; - let storageMock: jest.Mocked; - let userMock: jest.Mocked; + let assetMock: Mocked; + let auditMock: Mocked; + let cryptoMock: Mocked; + let personMock: Mocked; + let storageMock: Mocked; + let userMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { accessMock = newAccessRepositoryMock(); @@ -35,7 +39,8 @@ describe(AuditService.name, () => { personMock = newPersonRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); - sut = new AuditService(accessMock, assetMock, cryptoMock, personMock, auditMock, storageMock, userMock); + loggerMock = newLoggerRepositoryMock(); + sut = new AuditService(accessMock, assetMock, cryptoMock, personMock, auditMock, storageMock, userMock, loggerMock); }); it('should work', () => { @@ -61,13 +66,13 @@ describe(AuditService.name, () => { expect(auditMock.getAfter).toHaveBeenCalledWith(date, { action: DatabaseAction.DELETE, - ownerId: authStub.admin.user.id, + userIds: [authStub.admin.user.id], entityType: EntityType.ASSET, }); }); it('should get any new or updated assets and deleted ids', async () => { - auditMock.getAfter.mockResolvedValue([auditStub.delete]); + auditMock.getAfter.mockResolvedValue([auditStub.delete.entityId]); const date = new Date(); await expect(sut.getDeletes(authStub.admin, { after: date, entityType: EntityType.ASSET })).resolves.toEqual({ @@ -77,7 +82,7 @@ describe(AuditService.name, () => { expect(auditMock.getAfter).toHaveBeenCalledWith(date, { action: DatabaseAction.DELETE, - ownerId: authStub.admin.user.id, + userIds: [authStub.admin.user.id], entityType: EntityType.ASSET, }); }); diff --git a/server/src/services/audit.service.ts b/server/src/services/audit.service.ts index d40167429f..bfff09c0bc 100644 --- a/server/src/services/audit.service.ts +++ b/server/src/services/audit.service.ts @@ -20,16 +20,15 @@ import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IAuditRepository } from 'src/interfaces/audit.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -import { ImmichLogger } from 'src/utils/logger'; import { usePagination } from 'src/utils/pagination'; @Injectable() export class AuditService { private access: AccessCore; - private logger = new ImmichLogger(AuditService.name); constructor( @Inject(IAccessRepository) accessRepository: IAccessRepository, @@ -39,8 +38,10 @@ export class AuditService { @Inject(IAuditRepository) private repository: IAuditRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IUserRepository) private userRepository: IUserRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { this.access = AccessCore.create(accessRepository); + this.logger.setContext(AuditService.name); } async handleCleanup(): Promise { @@ -53,7 +54,7 @@ export class AuditService { await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId); const audits = await this.repository.getAfter(dto.after, { - ownerId: userId, + userIds: [userId], entityType: dto.entityType, action: DatabaseAction.DELETE, }); @@ -62,7 +63,7 @@ export class AuditService { return { needsFullSync: duration > AUDIT_LOG_MAX_DURATION, - ids: audits.map(({ entityId }) => entityId), + ids: audits, }; } diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index 30773f3f16..d53f319661 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -8,6 +8,7 @@ import { UserEntity } from 'src/entities/user.entity'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserTokenRepository } from 'src/interfaces/user-token.interface'; @@ -23,10 +24,12 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newKeyRepositoryMock } from 'test/repositories/api-key.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newSharedLinkRepositoryMock } from 'test/repositories/shared-link.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newUserTokenRepositoryMock } from 'test/repositories/user-token.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mock, Mocked, vitest } from 'vitest'; // const token = Buffer.from('my-api-key', 'utf8').toString('base64'); @@ -56,33 +59,34 @@ const oauthUserWithDefaultQuota = { describe('AuthService', () => { let sut: AuthService; - let accessMock: jest.Mocked; - let cryptoMock: jest.Mocked; - let userMock: jest.Mocked; - let libraryMock: jest.Mocked; - let configMock: jest.Mocked; - let userTokenMock: jest.Mocked; - let shareMock: jest.Mocked; - let keyMock: jest.Mocked; + let accessMock: Mocked; + let cryptoMock: Mocked; + let userMock: Mocked; + let libraryMock: Mocked; + let loggerMock: Mocked; + let configMock: Mocked; + let userTokenMock: Mocked; + let shareMock: Mocked; + let keyMock: Mocked; - let callbackMock: jest.Mock; - let userinfoMock: jest.Mock; + let callbackMock: Mock; + let userinfoMock: Mock; beforeEach(() => { - callbackMock = jest.fn().mockReturnValue({ access_token: 'access-token' }); - userinfoMock = jest.fn().mockResolvedValue({ sub, email }); + callbackMock = vitest.fn().mockReturnValue({ access_token: 'access-token' }); + userinfoMock = vitest.fn().mockResolvedValue({ sub, email }); - jest.spyOn(generators, 'state').mockReturnValue('state'); - jest.spyOn(Issuer, 'discover').mockResolvedValue({ + vitest.spyOn(generators, 'state').mockReturnValue('state'); + vitest.spyOn(Issuer, 'discover').mockResolvedValue({ id_token_signing_alg_values_supported: ['RS256'], - Client: jest.fn().mockResolvedValue({ + Client: vitest.fn().mockResolvedValue({ issuer: { metadata: { end_session_endpoint: 'http://end-session-endpoint', }, }, - authorizationUrl: jest.fn().mockReturnValue('http://authorization-url'), - callbackParams: jest.fn().mockReturnValue({ state: 'state' }), + authorizationUrl: vitest.fn().mockReturnValue('http://authorization-url'), + callbackParams: vitest.fn().mockReturnValue({ state: 'state' }), callback: callbackMock, userinfo: userinfoMock, }), @@ -92,12 +96,23 @@ describe('AuthService', () => { cryptoMock = newCryptoRepositoryMock(); userMock = newUserRepositoryMock(); libraryMock = newLibraryRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); configMock = newSystemConfigRepositoryMock(); userTokenMock = newUserTokenRepositoryMock(); shareMock = newSharedLinkRepositoryMock(); keyMock = newKeyRepositoryMock(); - sut = new AuthService(accessMock, cryptoMock, configMock, libraryMock, userMock, userTokenMock, shareMock, keyMock); + sut = new AuthService( + accessMock, + cryptoMock, + configMock, + libraryMock, + loggerMock, + userMock, + userTokenMock, + shareMock, + keyMock, + ); }); it('should be defined', () => { diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 19476667c1..7bebca5989 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -43,12 +43,12 @@ import { IAccessRepository } from 'src/interfaces/access.interface'; import { IKeyRepository } from 'src/interfaces/api-key.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserTokenRepository } from 'src/interfaces/user-token.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { HumanReadableSize } from 'src/utils/bytes'; -import { ImmichLogger } from 'src/utils/logger'; export interface LoginDetails { isSecure: boolean; @@ -76,7 +76,6 @@ interface ClaimOptions { export class AuthService { private access: AccessCore; private configCore: SystemConfigCore; - private logger = new ImmichLogger(AuthService.name); private userCore: UserCore; constructor( @@ -84,13 +83,15 @@ export class AuthService { @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(ILibraryRepository) libraryRepository: ILibraryRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IUserTokenRepository) private userTokenRepository: IUserTokenRepository, @Inject(ISharedLinkRepository) private sharedLinkRepository: ISharedLinkRepository, @Inject(IKeyRepository) private keyRepository: IKeyRepository, ) { + this.logger.setContext(AuthService.name); this.access = AccessCore.create(accessRepository); - this.configCore = SystemConfigCore.create(configRepository); + this.configCore = SystemConfigCore.create(configRepository, logger); this.userCore = UserCore.create(cryptoRepository, libraryRepository, userRepository); custom.setHttpOptionsDefaults({ timeout: 30_000 }); diff --git a/server/src/services/database.service.spec.ts b/server/src/services/database.service.spec.ts index 6fa5e7fd81..28ed7d7df2 100644 --- a/server/src/services/database.service.spec.ts +++ b/server/src/services/database.service.spec.ts @@ -1,17 +1,20 @@ import { DatabaseExtension, IDatabaseRepository, VectorIndex } from 'src/interfaces/database.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { DatabaseService } from 'src/services/database.service'; -import { ImmichLogger } from 'src/utils/logger'; import { Version, VersionType } from 'src/utils/version'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; +import { Mocked } from 'vitest'; describe(DatabaseService.name, () => { let sut: DatabaseService; - let databaseMock: jest.Mocked; + let databaseMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { databaseMock = newDatabaseRepositoryMock(); - - sut = new DatabaseService(databaseMock); + loggerMock = newLoggerRepositoryMock(); + sut = new DatabaseService(databaseMock, loggerMock); }); it('should work', () => { @@ -22,18 +25,11 @@ describe(DatabaseService.name, () => { [{ vectorExt: DatabaseExtension.VECTORS, extName: 'pgvecto.rs', minVersion: new Version(0, 1, 1) }], [{ vectorExt: DatabaseExtension.VECTOR, extName: 'pgvector', minVersion: new Version(0, 5, 0) }], ] as const)('init', ({ vectorExt, extName, minVersion }) => { - let fatalLog: jest.SpyInstance; - let errorLog: jest.SpyInstance; - let warnLog: jest.SpyInstance; - beforeEach(() => { - fatalLog = jest.spyOn(ImmichLogger.prototype, 'fatal'); - errorLog = jest.spyOn(ImmichLogger.prototype, 'error'); - warnLog = jest.spyOn(ImmichLogger.prototype, 'warn'); databaseMock.getPreferredVectorExtension.mockReturnValue(vectorExt); databaseMock.getExtensionVersion.mockResolvedValue(minVersion); - sut = new DatabaseService(databaseMock); + sut = new DatabaseService(databaseMock, loggerMock); sut.minVectorVersion = minVersion; sut.minVectorsVersion = minVersion; @@ -41,11 +37,6 @@ describe(DatabaseService.name, () => { sut.vectorsVersionPin = VersionType.MINOR; }); - afterEach(() => { - fatalLog.mockRestore(); - warnLog.mockRestore(); - }); - it(`should resolve successfully if minimum supported PostgreSQL and ${extName} version are installed`, async () => { databaseMock.getPostgresVersion.mockResolvedValueOnce(new Version(14, 0, 0)); @@ -56,7 +47,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.createExtension).toHaveBeenCalledTimes(1); expect(databaseMock.getExtensionVersion).toHaveBeenCalled(); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }); it('should throw an error if PostgreSQL version is below minimum supported version', async () => { @@ -73,7 +64,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.createExtension).toHaveBeenCalledWith(vectorExt); expect(databaseMock.createExtension).toHaveBeenCalledTimes(1); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }); it(`should throw an error if ${extName} version is not installed even after createVectorExtension`, async () => { @@ -133,7 +124,7 @@ describe(DatabaseService.name, () => { await expect(sut.init()).rejects.toThrow('Failed to create extension'); - expect(fatalLog).toHaveBeenCalledTimes(1); + expect(loggerMock.fatal).toHaveBeenCalledTimes(1); expect(databaseMock.createExtension).toHaveBeenCalledTimes(1); expect(databaseMock.runMigrations).not.toHaveBeenCalled(); }); @@ -147,7 +138,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.updateVectorExtension).toHaveBeenCalledWith(vectorExt, version); expect(databaseMock.updateVectorExtension).toHaveBeenCalledTimes(1); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }); it(`should not update ${extName} if a newer version is higher than the maximum`, async () => { @@ -158,7 +149,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.updateVectorExtension).not.toHaveBeenCalled(); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }); it(`should warn if attempted to update ${extName} and failed`, async () => { @@ -168,10 +159,10 @@ describe(DatabaseService.name, () => { await expect(sut.init()).resolves.toBeUndefined(); - expect(warnLog).toHaveBeenCalledTimes(1); - expect(warnLog.mock.calls[0][0]).toContain(extName); - expect(errorLog).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.warn).toHaveBeenCalledTimes(1); + expect(loggerMock.warn.mock.calls[0][0]).toContain(extName); + expect(loggerMock.error).toHaveBeenCalledTimes(1); + expect(loggerMock.fatal).not.toHaveBeenCalled(); expect(databaseMock.updateVectorExtension).toHaveBeenCalledWith(vectorExt, version); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); }); @@ -183,11 +174,11 @@ describe(DatabaseService.name, () => { await expect(sut.init()).resolves.toBeUndefined(); - expect(warnLog).toHaveBeenCalledTimes(1); - expect(warnLog.mock.calls[0][0]).toContain(extName); + expect(loggerMock.warn).toHaveBeenCalledTimes(1); + expect(loggerMock.warn.mock.calls[0][0]).toContain(extName); expect(databaseMock.updateVectorExtension).toHaveBeenCalledWith(vectorExt, version); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }); it.each([{ index: VectorIndex.CLIP }, { index: VectorIndex.FACE }])( @@ -202,7 +193,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.reindex).toHaveBeenCalledWith(index); expect(databaseMock.reindex).toHaveBeenCalledTimes(1); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }, ); @@ -216,7 +207,7 @@ describe(DatabaseService.name, () => { expect(databaseMock.shouldReindex).toHaveBeenCalledTimes(2); expect(databaseMock.reindex).not.toHaveBeenCalled(); expect(databaseMock.runMigrations).toHaveBeenCalledTimes(1); - expect(fatalLog).not.toHaveBeenCalled(); + expect(loggerMock.fatal).not.toHaveBeenCalled(); }, ); }); diff --git a/server/src/services/database.service.ts b/server/src/services/database.service.ts index a333c0053a..8da48eb152 100644 --- a/server/src/services/database.service.ts +++ b/server/src/services/database.service.ts @@ -7,12 +7,11 @@ import { VectorIndex, extName, } from 'src/interfaces/database.interface'; -import { ImmichLogger } from 'src/utils/logger'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { Version, VersionType } from 'src/utils/version'; @Injectable() export class DatabaseService { - private logger = new ImmichLogger(DatabaseService.name); private vectorExt: VectorExtension; minPostgresVersion = 14; minVectorsVersion = new Version(0, 2, 0); @@ -20,7 +19,11 @@ export class DatabaseService { minVectorVersion = new Version(0, 5, 0); vectorVersionPin = VersionType.MAJOR; - constructor(@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository) { + constructor( + @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(DatabaseService.name); this.vectorExt = this.databaseRepository.getPreferredVectorExtension(); } diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index babc21fa8a..331ddcaaa7 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,6 +1,6 @@ import { BadRequestException } from '@nestjs/common'; -import { when } from 'jest-when'; import { DownloadResponseDto } from 'src/dtos/download.dto'; +import { AssetEntity } from 'src/entities/asset.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { DownloadService } from 'src/services/download.service'; @@ -11,6 +11,7 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { Readable } from 'typeorm/platform/PlatformTools.js'; +import { Mocked, vitest } from 'vitest'; const downloadResponse: DownloadResponseDto = { totalSize: 105_000, @@ -25,8 +26,8 @@ const downloadResponse: DownloadResponseDto = { describe(DownloadService.name, () => { let sut: DownloadService; let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let storageMock: jest.Mocked; + let assetMock: Mocked; + let storageMock: Mocked; it('should work', () => { expect(sut).toBeDefined(); @@ -82,8 +83,8 @@ describe(DownloadService.name, () => { it('should download an archive', async () => { const archiveMock = { - addFile: jest.fn(), - finalize: jest.fn(), + addFile: vitest.fn(), + finalize: vitest.fn(), stream: new Readable(), }; @@ -105,8 +106,8 @@ describe(DownloadService.name, () => { it('should handle duplicate file names', async () => { const archiveMock = { - addFile: jest.fn(), - finalize: jest.fn(), + addFile: vitest.fn(), + finalize: vitest.fn(), stream: new Readable(), }; @@ -128,8 +129,8 @@ describe(DownloadService.name, () => { it('should be deterministic', async () => { const archiveMock = { - addFile: jest.fn(), - finalize: jest.fn(), + addFile: vitest.fn(), + finalize: vitest.fn(), stream: new Readable(), }; @@ -223,14 +224,15 @@ describe(DownloadService.name, () => { it('should include the video portion of a live photo', async () => { const assetIds = [assetStub.livePhotoStillAsset.id]; + const assets = [assetStub.livePhotoStillAsset, assetStub.livePhotoMotionAsset]; accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(assetIds)); - when(assetMock.getByIds) - .calledWith([assetStub.livePhotoStillAsset.id], { exifInfo: true }) - .mockResolvedValue([assetStub.livePhotoStillAsset]); - when(assetMock.getByIds) - .calledWith([assetStub.livePhotoMotionAsset.id], { exifInfo: true }) - .mockResolvedValue([assetStub.livePhotoMotionAsset]); + assetMock.getByIds.mockImplementation( + (ids) => + Promise.resolve( + ids.map((id) => assets.find((asset) => asset.id === id)).filter((asset) => !!asset), + ) as Promise, + ); await expect(sut.getDownloadInfo(authStub.admin, { assetIds })).resolves.toEqual({ totalSize: 125_000, diff --git a/server/src/services/index.ts b/server/src/services/index.ts index 3c903c927d..6c40f8420a 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -22,6 +22,7 @@ import { SharedLinkService } from 'src/services/shared-link.service'; import { SmartInfoService } from 'src/services/smart-info.service'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { StorageService } from 'src/services/storage.service'; +import { SyncService } from 'src/services/sync.service'; import { SystemConfigService } from 'src/services/system-config.service'; import { TagService } from 'src/services/tag.service'; import { TimelineService } from 'src/services/timeline.service'; @@ -53,6 +54,7 @@ export const services = [ SmartInfoService, StorageService, StorageTemplateService, + SyncService, SystemConfigService, TagService, TimelineService, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index c141586dbe..ce7d2c00e4 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -12,6 +12,7 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; @@ -20,12 +21,14 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMetricRepositoryMock } from 'test/repositories/metric.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked, vitest } from 'vitest'; const makeMockHandlers = (status: JobStatus) => { - const mock = jest.fn().mockResolvedValue(status); + const mock = vitest.fn().mockResolvedValue(status); return Object.fromEntries(Object.values(JobName).map((jobName) => [jobName, mock])) as unknown as Record< JobName, JobHandler @@ -34,12 +37,13 @@ const makeMockHandlers = (status: JobStatus) => { describe(JobService.name, () => { let sut: JobService; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let eventMock: jest.Mocked; - let jobMock: jest.Mocked; - let personMock: jest.Mocked; - let metricMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let eventMock: Mocked; + let jobMock: Mocked; + let personMock: Mocked; + let metricMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); @@ -48,7 +52,8 @@ describe(JobService.name, () => { jobMock = newJobRepositoryMock(); personMock = newPersonRepositoryMock(); metricMock = newMetricRepositoryMock(); - sut = new JobService(assetMock, eventMock, jobMock, configMock, personMock, metricMock); + loggerMock = newLoggerRepositoryMock(); + sut = new JobService(assetMock, eventMock, jobMock, configMock, personMock, metricMock, loggerMock); }); it('should work', () => { @@ -234,7 +239,7 @@ describe(JobService.name, () => { it('should subscribe to config changes', async () => { await sut.init(makeMockHandlers(JobStatus.FAILED)); - SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({ + SystemConfigCore.create(newSystemConfigRepositoryMock(false), newLoggerRepositoryMock()).config$.next({ job: { [QueueName.BACKGROUND_TASK]: { concurrency: 10 }, [QueueName.SMART_SEARCH]: { concurrency: 10 }, diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 3f9cd8a221..13d367994b 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -17,14 +17,13 @@ import { QueueCleanType, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class JobService { - private logger = new ImmichLogger(JobService.name); private configCore: SystemConfigCore; constructor( @@ -34,8 +33,10 @@ export class JobService { @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IPersonRepository) private personRepository: IPersonRepository, @Inject(IMetricRepository) private metricRepository: IMetricRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(JobService.name); + this.configCore = SystemConfigCore.create(configRepository, logger); } async handleCommand(queueName: QueueName, dto: JobCommandDto): Promise { diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index df59d0c150..2122b03208 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,6 +1,4 @@ import { BadRequestException } from '@nestjs/common'; -import { when } from 'jest-when'; -import { R_OK } from 'node:constants'; import { Stats } from 'node:fs'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { mapLibrary } from 'src/dtos/library.dto'; @@ -13,7 +11,8 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IJobRepository, ILibraryFileJob, ILibraryRefreshJob, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; -import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { LibraryService } from 'src/services/library.service'; import { assetStub } from 'test/fixtures/asset.stub'; @@ -26,19 +25,22 @@ import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.moc import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { makeMockWatcher, newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked, vitest } from 'vitest'; describe(LibraryService.name, () => { let sut: LibraryService; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let cryptoMock: jest.Mocked; - let jobMock: jest.Mocked; - let libraryMock: jest.Mocked; - let storageMock: jest.Mocked; - let databaseMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let cryptoMock: Mocked; + let jobMock: Mocked; + let libraryMock: Mocked; + let storageMock: Mocked; + let databaseMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { configMock = newSystemConfigRepositoryMock(); @@ -48,8 +50,18 @@ describe(LibraryService.name, () => { cryptoMock = newCryptoRepositoryMock(); storageMock = newStorageRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); - sut = new LibraryService(assetMock, configMock, cryptoMock, jobMock, libraryMock, storageMock, databaseMock); + sut = new LibraryService( + assetMock, + configMock, + cryptoMock, + jobMock, + libraryMock, + storageMock, + databaseMock, + loggerMock, + ); databaseMock.tryLock.mockResolvedValue(true); }); @@ -69,7 +81,7 @@ describe(LibraryService.name, () => { expect(configMock.load).toHaveBeenCalled(); expect(jobMock.addCronJob).toHaveBeenCalled(); - SystemConfigCore.create(newSystemConfigRepositoryMock(false)).config$.next({ + SystemConfigCore.create(newSystemConfigRepositoryMock(false), newLoggerRepositoryMock()).config$.next({ library: { scan: { enabled: true, @@ -89,15 +101,13 @@ describe(LibraryService.name, () => { ]); configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled); - libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - - when(libraryMock.get) - .calledWith(libraryStub.externalLibraryWithImportPaths1.id) - .mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - - when(libraryMock.get) - .calledWith(libraryStub.externalLibraryWithImportPaths2.id) - .mockResolvedValue(libraryStub.externalLibraryWithImportPaths2); + libraryMock.get.mockImplementation((id) => + Promise.resolve( + [libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find( + (library) => library.id === id, + ) || null, + ), + ); await sut.init(); @@ -160,7 +170,7 @@ describe(LibraryService.name, () => { storageMock.walk.mockImplementation(async function* generator() { yield '/data/user1/photo.jpg'; }); - assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); + assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -189,7 +199,7 @@ describe(LibraryService.name, () => { storageMock.walk.mockImplementation(async function* generator() { yield '/data/user1/photo.jpg'; }); - assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); + assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -238,7 +248,7 @@ describe(LibraryService.name, () => { }; libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - assetMock.getLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); + assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ items: [], hasNextPage: false }); await sut.handleQueueAssetRefresh(mockLibraryJob); @@ -256,8 +266,8 @@ describe(LibraryService.name, () => { }; libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - assetMock.getLibraryAssetPaths.mockResolvedValue({ - items: [assetStub.image], + assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ + items: [assetStub.external], hasNextPage: false, }); @@ -278,16 +288,16 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); // eslint-disable-next-line @typescript-eslint/require-await storageMock.walk.mockImplementation(async function* generator() { - yield assetStub.offline.originalPath; + yield assetStub.externalOffline.originalPath; }); - assetMock.getLibraryAssetPaths.mockResolvedValue({ - items: [assetStub.offline], + assetMock.getExternalLibraryAssetPaths.mockResolvedValue({ + items: [assetStub.externalOffline], hasNextPage: false, }); await sut.handleQueueAssetRefresh(mockLibraryJob); - expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.offline.id], { isOffline: false }); + expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.externalOffline.id], { isOffline: false }); expect(assetMock.updateAll).not.toHaveBeenCalledWith(expect.anything(), { isOffline: true }); expect(jobMock.queueAll).not.toHaveBeenCalled(); }); @@ -751,7 +761,7 @@ describe(LibraryService.name, () => { configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled); - const mockClose = jest.fn(); + const mockClose = vitest.fn(); storageMock.watch.mockImplementation(makeMockWatcher({ close: mockClose })); await sut.init(); @@ -933,12 +943,6 @@ describe(LibraryService.name, () => { type: LibraryType.EXTERNAL, importPaths: libraryStub.externalLibraryWithImportPaths1.importPaths, }); - - expect(storageMock.watch).toHaveBeenCalledWith( - libraryStub.externalLibraryWithImportPaths1.importPaths, - expect.anything(), - expect.anything(), - ); }); it('should create with exclusion patterns', async () => { @@ -1058,14 +1062,6 @@ describe(LibraryService.name, () => { expect(libraryMock.create).not.toHaveBeenCalled(); }); - - it('should not create watched', async () => { - await expect( - sut.create({ ownerId: authStub.admin.user.id, type: LibraryType.UPLOAD, isWatched: true }), - ).rejects.toBeInstanceOf(BadRequestException); - - expect(storageMock.watch).not.toHaveBeenCalled(); - }); }); }); @@ -1095,45 +1091,6 @@ describe(LibraryService.name, () => { await expect(sut.update('library-id', {})).resolves.toEqual(mapLibrary(libraryStub.uploadLibrary1)); expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' })); }); - - it('should re-watch library when updating import paths', async () => { - libraryMock.update.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - - storageMock.stat.mockResolvedValue({ - isDirectory: () => true, - } as Stats); - - storageMock.checkFileExists.mockResolvedValue(true); - - await expect(sut.update('library-id', { importPaths: ['/data/user1/foo'] })).resolves.toEqual( - mapLibrary(libraryStub.externalLibraryWithImportPaths1), - ); - - expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' })); - expect(storageMock.watch).toHaveBeenCalledWith( - libraryStub.externalLibraryWithImportPaths1.importPaths, - expect.anything(), - expect.anything(), - ); - }); - - it('should re-watch library when updating exclusion patterns', async () => { - libraryMock.update.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled); - libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - - await expect(sut.update('library-id', { exclusionPatterns: ['bar'] })).resolves.toEqual( - mapLibrary(libraryStub.externalLibraryWithImportPaths1), - ); - - expect(libraryMock.update).toHaveBeenCalledWith(expect.objectContaining({ id: 'library-id' })); - expect(storageMock.watch).toHaveBeenCalledWith( - expect.arrayContaining([expect.any(String)]), - expect.anything(), - expect.anything(), - ); - }); }); describe('watchAll', () => { @@ -1176,7 +1133,7 @@ describe(LibraryService.name, () => { it('should watch and unwatch library', async () => { libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); - const mockClose = jest.fn(); + const mockClose = vitest.fn(); storageMock.watch.mockImplementation(makeMockWatcher({ close: mockClose })); await sut.watchAll(); @@ -1206,9 +1163,7 @@ describe(LibraryService.name, () => { it('should handle a new file event', async () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); - storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/foo/photo.jpg' }] }), - ); + storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/foo/photo.jpg' }] })); await sut.watchAll(); @@ -1229,7 +1184,7 @@ describe(LibraryService.name, () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.CHANGE, value: '/foo/photo.jpg' }] }), + makeMockWatcher({ items: [{ event: 'change', value: '/foo/photo.jpg' }] }), ); await sut.watchAll(); @@ -1252,7 +1207,7 @@ describe(LibraryService.name, () => { libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); assetMock.getByLibraryIdAndOriginalPath.mockResolvedValue(assetStub.external); storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.UNLINK, value: '/foo/photo.jpg' }] }), + makeMockWatcher({ items: [{ event: 'unlink', value: '/foo/photo.jpg' }] }), ); await sut.watchAll(); @@ -1266,19 +1221,17 @@ describe(LibraryService.name, () => { libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); storageMock.watch.mockImplementation( makeMockWatcher({ - items: [{ event: StorageEventType.ERROR, value: 'Error!' }], + items: [{ event: 'error', value: 'Error!' }], }), ); - await expect(sut.watchAll()).rejects.toThrow('Error!'); + await expect(sut.watchAll()).resolves.toBeUndefined(); }); it('should ignore unknown extensions', async () => { libraryMock.get.mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); libraryMock.getAll.mockResolvedValue([libraryStub.externalLibraryWithImportPaths1]); - storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/foo/photo.jpg' }] }), - ); + storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/foo/photo.jpg' }] })); await sut.watchAll(); @@ -1288,9 +1241,7 @@ describe(LibraryService.name, () => { it('should ignore excluded paths', async () => { libraryMock.get.mockResolvedValue(libraryStub.patternPath); libraryMock.getAll.mockResolvedValue([libraryStub.patternPath]); - storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/dir1/photo.txt' }] }), - ); + storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/dir1/photo.txt' }] })); await sut.watchAll(); @@ -1300,9 +1251,7 @@ describe(LibraryService.name, () => { it('should ignore excluded paths without case sensitivity', async () => { libraryMock.get.mockResolvedValue(libraryStub.patternPath); libraryMock.getAll.mockResolvedValue([libraryStub.patternPath]); - storageMock.watch.mockImplementation( - makeMockWatcher({ items: [{ event: StorageEventType.ADD, value: '/DIR1/photo.txt' }] }), - ); + storageMock.watch.mockImplementation(makeMockWatcher({ items: [{ event: 'add', value: '/DIR1/photo.txt' }] })); await sut.watchAll(); @@ -1321,15 +1270,15 @@ describe(LibraryService.name, () => { configMock.load.mockResolvedValue(systemConfigStub.libraryWatchEnabled); libraryMock.get.mockResolvedValue(libraryStub.externalLibrary1); - when(libraryMock.get) - .calledWith(libraryStub.externalLibraryWithImportPaths1.id) - .mockResolvedValue(libraryStub.externalLibraryWithImportPaths1); + libraryMock.get.mockImplementation((id) => + Promise.resolve( + [libraryStub.externalLibraryWithImportPaths1, libraryStub.externalLibraryWithImportPaths2].find( + (library) => library.id === id, + ) || null, + ), + ); - when(libraryMock.get) - .calledWith(libraryStub.externalLibraryWithImportPaths2.id) - .mockResolvedValue(libraryStub.externalLibraryWithImportPaths2); - - const mockClose = jest.fn(); + const mockClose = vitest.fn(); storageMock.watch.mockImplementation(makeMockWatcher({ close: mockClose })); await sut.init(); @@ -1606,7 +1555,7 @@ describe(LibraryService.name, () => { it('should detect when import path is in immich media folder', async () => { storageMock.stat.mockResolvedValue({ isDirectory: () => true } as Stats); const validImport = libraryStub.hasImmichPaths.importPaths[1]; - when(storageMock.checkFileExists).calledWith(validImport, R_OK).mockResolvedValue(true); + storageMock.checkFileExists.mockImplementation((importPath) => Promise.resolve(importPath === validImport)); await expect( sut.validate('library-id', { importPaths: libraryStub.hasImmichPaths.importPaths }), diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index d0e41da172..00909e9837 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -1,7 +1,6 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common'; import { Trie } from 'mnemonist'; import { R_OK } from 'node:constants'; -import { EventEmitter } from 'node:events'; import { Stats } from 'node:fs'; import path, { basename, parse } from 'node:path'; import picomatch from 'picomatch'; @@ -37,9 +36,9 @@ import { JobStatus, } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; -import { IStorageRepository, StorageEventType } from 'src/interfaces/storage.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; import { handlePromiseError } from 'src/utils/misc'; import { usePagination } from 'src/utils/pagination'; @@ -48,8 +47,7 @@ import { validateCronExpression } from 'src/validation'; const LIBRARY_SCAN_BATCH_SIZE = 5000; @Injectable() -export class LibraryService extends EventEmitter { - readonly logger = new ImmichLogger(LibraryService.name); +export class LibraryService { private configCore: SystemConfigCore; private watchLibraries = false; private watchLock = false; @@ -63,9 +61,10 @@ export class LibraryService extends EventEmitter { @Inject(ILibraryRepository) private repository: ILibraryRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - super(); - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(LibraryService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); } async init() { @@ -152,7 +151,6 @@ export class LibraryService extends EventEmitter { if (matcher(path)) { await this.scanAssets(library.id, [path], library.ownerId, false); } - this.emit(StorageEventType.ADD, path); }; return handlePromiseError(handler(), this.logger); }, @@ -163,7 +161,6 @@ export class LibraryService extends EventEmitter { // Note: if the changed file was not previously imported, it will be imported now. await this.scanAssets(library.id, [path], library.ownerId, false); } - this.emit(StorageEventType.CHANGE, path); }; return handlePromiseError(handler(), this.logger); }, @@ -174,13 +171,11 @@ export class LibraryService extends EventEmitter { if (asset && matcher(path)) { await this.assetRepository.update({ id: asset.id, isOffline: true }); } - this.emit(StorageEventType.UNLINK, path); }; return handlePromiseError(handler(), this.logger); }, onError: (error) => { this.logger.error(`Library watcher for library ${library.id} encountered error: ${error}`); - this.emit(StorageEventType.ERROR, error); }, }, ); @@ -266,9 +261,6 @@ export class LibraryService extends EventEmitter { if (dto.exclusionPatterns && dto.exclusionPatterns.length > 0) { throw new BadRequestException('Upload libraries cannot have exclusion patterns'); } - if (dto.isWatched) { - throw new BadRequestException('Upload libraries cannot be watched'); - } break; } } @@ -284,10 +276,6 @@ export class LibraryService extends EventEmitter { this.logger.log(`Creating ${dto.type} library for ${dto.ownerId}}`); - if (dto.type === LibraryType.EXTERNAL) { - await this.watch(library.id); - } - return mapLibrary(library); } @@ -371,11 +359,6 @@ export class LibraryService extends EventEmitter { } } - if (dto.importPaths || dto.exclusionPatterns) { - // Re-watch library to use new paths and/or exclusion patterns - await this.watch(id); - } - return mapLibrary(library); } @@ -619,7 +602,7 @@ export class LibraryService extends EventEmitter { const assetIdsToMarkOffline = []; const assetIdsToMarkOnline = []; const pagination = usePagination(LIBRARY_SCAN_BATCH_SIZE, (pagination) => - this.assetRepository.getLibraryAssetPaths(pagination, library.id), + this.assetRepository.getExternalLibraryAssetPaths(pagination, library.id), ); this.logger.verbose(`Crawled asset paths paginated`); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index 3a650430ef..1b1adcd573 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -14,6 +14,7 @@ import { import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; @@ -27,22 +28,25 @@ import { personStub } from 'test/fixtures/person.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked } from 'vitest'; describe(MediaService.name, () => { let sut: MediaService; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let jobMock: jest.Mocked; - let mediaMock: jest.Mocked; - let moveMock: jest.Mocked; - let personMock: jest.Mocked; - let storageMock: jest.Mocked; - let cryptoMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let jobMock: Mocked; + let mediaMock: Mocked; + let moveMock: Mocked; + let personMock: Mocked; + let storageMock: Mocked; + let cryptoMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); @@ -53,8 +57,19 @@ describe(MediaService.name, () => { personMock = newPersonRepositoryMock(); storageMock = newStorageRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); - sut = new MediaService(assetMock, personMock, jobMock, mediaMock, storageMock, configMock, moveMock, cryptoMock); + sut = new MediaService( + assetMock, + personMock, + jobMock, + mediaMock, + storageMock, + configMock, + moveMock, + cryptoMock, + loggerMock, + ); }); it('should be defined', () => { @@ -210,25 +225,21 @@ describe(MediaService.name, () => { expect(assetMock.update).not.toHaveBeenCalledWith(); }); - it('should generate a thumbnail for an image', async () => { + it.each(Object.values(ImageFormat))('should generate a %s preview for an image when specified', async (format) => { + configMock.load.mockResolvedValue([{ key: SystemConfigKey.IMAGE_PREVIEW_FORMAT, value: format }]); assetMock.getByIds.mockResolvedValue([assetStub.image]); + const previewPath = `upload/thumbs/user-id/as/se/asset-id-preview.${format}`; + await sut.handleGeneratePreview({ id: assetStub.image.id }); expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); - expect(mediaMock.resize).toHaveBeenCalledWith( - '/original/path.jpg', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', - { - size: 1440, - format: ImageFormat.JPEG, - quality: 80, - colorspace: Colorspace.SRGB, - }, - ); - expect(assetMock.update).toHaveBeenCalledWith({ - id: 'asset-id', - previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', previewPath, { + size: 1440, + format, + quality: 80, + colorspace: Colorspace.SRGB, }); + expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', previewPath }); }); it('should generate a P3 thumbnail for a wide gamut image', async () => { @@ -342,25 +353,25 @@ describe(MediaService.name, () => { expect(assetMock.update).not.toHaveBeenCalledWith(); }); - it('should generate a thumbnail', async () => { - assetMock.getByIds.mockResolvedValue([assetStub.image]); - await sut.handleGenerateThumbnail({ id: assetStub.image.id }); + it.each(Object.values(ImageFormat))( + 'should generate a %s thumbnail for an image when specified', + async (format) => { + configMock.load.mockResolvedValue([{ key: SystemConfigKey.IMAGE_THUMBNAIL_FORMAT, value: format }]); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + const thumbnailPath = `upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`; - expect(mediaMock.resize).toHaveBeenCalledWith( - '/original/path.jpg', - 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', - { - format: ImageFormat.WEBP, + await sut.handleGenerateThumbnail({ id: assetStub.image.id }); + + expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', thumbnailPath, { size: 250, + format, quality: 80, colorspace: Colorspace.SRGB, - }, - ); - expect(assetMock.update).toHaveBeenCalledWith({ - id: 'asset-id', - thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', - }); - }); + }); + expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', thumbnailPath }); + }, + ); }); it('should generate a P3 thumbnail for a wide gamut image', async () => { @@ -747,6 +758,67 @@ describe(MediaService.name, () => { ); }); + it('should not include hevc tag when target is hevc and video stream is copied from a different codec', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamH264); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, + { key: SystemConfigKey.FFMPEG_ACCEPTED_VIDEO_CODECS, value: [VideoCodec.H264, VideoCodec.HEVC] }, + { key: SystemConfigKey.FFMPEG_ACCEPTED_AUDIO_CODECS, value: [AudioCodec.AAC] }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v copy', + '-c:a aac', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-preset ultrafast', + '-crf 23', + ], + twoPass: false, + }, + ); + }); + + it('should include hevc tag when target is hevc and copying hevc video stream', async () => { + mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, + { key: SystemConfigKey.FFMPEG_ACCEPTED_VIDEO_CODECS, value: [VideoCodec.H264, VideoCodec.HEVC] }, + { key: SystemConfigKey.FFMPEG_ACCEPTED_AUDIO_CODECS, value: [AudioCodec.AAC] }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v copy', + '-c:a aac', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-tag:v hvc1', + '-v verbose', + '-preset ultrafast', + '-crf 23', + ], + twoPass: false, + }, + ); + }); + it('should copy audio stream when audio matches target', async () => { mediaMock.probe.mockResolvedValue(probeStub.audioStreamAac); configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]); @@ -1091,9 +1163,9 @@ describe(MediaService.name, () => { ); }); - it('should disable thread pooling for h264 if thread limit is above 0', async () => { + it('should disable thread pooling for h264 if thread limit is 1', async () => { mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer); - configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 }]); + configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_THREADS, value: 1 }]); assetMock.getByIds.mockResolvedValue([assetStub.video]); await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mediaMock.transcode).toHaveBeenCalledWith( @@ -1111,9 +1183,8 @@ describe(MediaService.name, () => { '-v verbose', '-vf scale=-2:720,format=yuv420p', '-preset ultrafast', - '-threads 2', - '-x264-params "pools=none"', - '-x264-params "frame-threads=2"', + '-threads 1', + '-x264-params frame-threads=1:pools=none', '-crf 23', ], twoPass: false, @@ -1148,10 +1219,10 @@ describe(MediaService.name, () => { ); }); - it('should disable thread pooling for hevc if thread limit is above 0', async () => { + it('should disable thread pooling for hevc if thread limit is 1', async () => { mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); configMock.load.mockResolvedValue([ - { key: SystemConfigKey.FFMPEG_THREADS, value: 2 }, + { key: SystemConfigKey.FFMPEG_THREADS, value: 1 }, { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }, ]); assetMock.getByIds.mockResolvedValue([assetStub.video]); @@ -1172,9 +1243,8 @@ describe(MediaService.name, () => { '-v verbose', '-vf scale=-2:720,format=yuv420p', '-preset ultrafast', - '-threads 2', - '-x265-params "pools=none"', - '-x265-params "frame-threads=2"', + '-threads 1', + '-x265-params frame-threads=1:pools=none', '-crf 23', ], twoPass: false, @@ -1213,6 +1283,157 @@ describe(MediaService.name, () => { ); }); + it('should use av1 if specified', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); + configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.AV1 }]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v av1', + '-c:a copy', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-vf scale=-2:720,format=yuv420p', + '-preset 12', + '-crf 23', + ], + twoPass: false, + }, + ); + }); + + it('should map `veryslow` preset to 4 for av1', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.AV1 }, + { key: SystemConfigKey.FFMPEG_PRESET, value: 'veryslow' }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v av1', + '-c:a copy', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-vf scale=-2:720,format=yuv420p', + '-preset 4', + '-crf 23', + ], + twoPass: false, + }, + ); + }); + + it('should set max bitrate for av1 if specified', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.AV1 }, + { key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '2M' }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v av1', + '-c:a copy', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-vf scale=-2:720,format=yuv420p', + '-preset 12', + '-crf 23', + '-svtav1-params mbr=2M', + ], + twoPass: false, + }, + ); + }); + + it('should set threads for av1 if specified', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.AV1 }, + { key: SystemConfigKey.FFMPEG_THREADS, value: 4 }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v av1', + '-c:a copy', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-vf scale=-2:720,format=yuv420p', + '-preset 12', + '-crf 23', + '-svtav1-params lp=4', + ], + twoPass: false, + }, + ); + }); + + it('should set both bitrate and threads for av1 if specified', async () => { + mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9); + configMock.load.mockResolvedValue([ + { key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.AV1 }, + { key: SystemConfigKey.FFMPEG_THREADS, value: 4 }, + { key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '2M' }, + ]); + assetMock.getByIds.mockResolvedValue([assetStub.video]); + await sut.handleVideoConversion({ id: assetStub.video.id }); + expect(mediaMock.transcode).toHaveBeenCalledWith( + '/original/path.ext', + 'upload/encoded-video/user-id/as/se/asset-id.mp4', + { + inputOptions: [], + outputOptions: [ + '-c:v av1', + '-c:a copy', + '-movflags faststart', + '-fps_mode passthrough', + '-map 0:0', + '-map 0:1', + '-v verbose', + '-vf scale=-2:720,format=yuv420p', + '-preset 12', + '-crf 23', + '-svtav1-params lp=4:mbr=2M', + ], + twoPass: false, + }, + ); + }); + it('should skip transcoding for audioless videos with optimal policy if video codec is correct', async () => { mediaMock.probe.mockResolvedValue(probeStub.noAudioStreams); configMock.load.mockResolvedValue([ diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index c56fd26e6a..47fa31abcc 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -25,13 +25,14 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { AudioStreamInfo, IMediaRepository, VideoCodecHWConfig, VideoStreamInfo } from 'src/interfaces/media.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; import { + AV1Config, H264Config, HEVCConfig, NVENCConfig, @@ -45,7 +46,6 @@ import { usePagination } from 'src/utils/pagination'; @Injectable() export class MediaService { - private logger = new ImmichLogger(MediaService.name); private configCore: SystemConfigCore; private storageCore: StorageCore; private hasOpenCL?: boolean = undefined; @@ -59,15 +59,18 @@ export class MediaService { @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IMoveRepository) moveRepository: IMoveRepository, @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(MediaService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); this.storageCore = StorageCore.create( assetRepository, + cryptoRepository, moveRepository, personRepository, - cryptoRepository, - configRepository, storageRepository, + configRepository, + this.logger, ); } @@ -167,12 +170,15 @@ export class MediaService { } async handleGeneratePreview({ id }: IEntityJob): Promise { - const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true }); + const [{ image }, [asset]] = await Promise.all([ + this.configCore.getConfig(), + this.assetRepository.getByIds([id], { exifInfo: true }), + ]); if (!asset) { return JobStatus.FAILED; } - const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, ImageFormat.JPEG); + const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, image.previewFormat); await this.assetRepository.update({ id: asset.id, previewPath }); return JobStatus.SUCCESS; } @@ -210,18 +216,21 @@ export class MediaService { } } this.logger.log( - `Successfully generated ${format.toUpperCase()} ${asset.type.toLowerCase()} thumbnail for asset ${asset.id}`, + `Successfully generated ${format.toUpperCase()} ${asset.type.toLowerCase()} ${type} for asset ${asset.id}`, ); return path; } async handleGenerateThumbnail({ id }: IEntityJob): Promise { - const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true }); + const [{ image }, [asset]] = await Promise.all([ + this.configCore.getConfig(), + this.assetRepository.getByIds([id], { exifInfo: true }), + ]); if (!asset) { return JobStatus.FAILED; } - const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, ImageFormat.WEBP); + const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, image.thumbnailFormat); await this.assetRepository.update({ id: asset.id, thumbnailPath }); return JobStatus.SUCCESS; } @@ -433,6 +442,9 @@ export class MediaService { case VideoCodec.VP9: { return new VP9Config(config); } + case VideoCodec.AV1: { + return new AV1Config(config); + } default: { throw new UnsupportedMediaTypeException(`Codec '${config.targetVideoCodec}' is unsupported`); } diff --git a/server/src/services/memory.service.spec.ts b/server/src/services/memory.service.spec.ts index 5f045ffde8..b4dd4bd2ad 100644 --- a/server/src/services/memory.service.spec.ts +++ b/server/src/services/memory.service.spec.ts @@ -7,10 +7,11 @@ import { memoryStub } from 'test/fixtures/memory.stub'; import { userStub } from 'test/fixtures/user.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newMemoryRepositoryMock } from 'test/repositories/memory.repository.mock'; +import { Mocked } from 'vitest'; describe(MemoryService.name, () => { let accessMock: IAccessRepositoryMock; - let memoryMock: jest.Mocked; + let memoryMock: Mocked; let sut: MemoryService; beforeEach(() => { diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index fd2c3e388c..5adeb1c774 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1,5 +1,4 @@ import { BinaryField } from 'exiftool-vendored'; -import { when } from 'jest-when'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; @@ -12,12 +11,14 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; +import { IUserRepository } from 'src/interfaces/user.interface'; import { MetadataService, Orientation } from 'src/services/metadata.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { fileStub } from 'test/fixtures/file.stub'; @@ -28,26 +29,31 @@ import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.moc import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked } from 'vitest'; describe(MetadataService.name, () => { - let albumMock: jest.Mocked; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let cryptoRepository: jest.Mocked; - let jobMock: jest.Mocked; - let metadataMock: jest.Mocked; - let moveMock: jest.Mocked; - let mediaMock: jest.Mocked; - let personMock: jest.Mocked; - let storageMock: jest.Mocked; - let eventMock: jest.Mocked; - let databaseMock: jest.Mocked; + let albumMock: Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let cryptoRepository: Mocked; + let jobMock: Mocked; + let metadataMock: Mocked; + let moveMock: Mocked; + let mediaMock: Mocked; + let personMock: Mocked; + let storageMock: Mocked; + let eventMock: Mocked; + let databaseMock: Mocked; + let userMock: Mocked; + let loggerMock: Mocked; let sut: MetadataService; beforeEach(() => { @@ -63,6 +69,8 @@ describe(MetadataService.name, () => { storageMock = newStorageRepositoryMock(); mediaMock = newMediaRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); + userMock = newUserRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); sut = new MetadataService( albumMock, @@ -77,6 +85,8 @@ describe(MetadataService.name, () => { personMock, storageMock, configMock, + userMock, + loggerMock, ); }); @@ -248,14 +258,13 @@ describe(MetadataService.name, () => { const originalDate = new Date('2023-11-21T16:13:17.517Z'); const sidecarDate = new Date('2022-01-01T00:00:00.000Z'); assetMock.getByIds.mockResolvedValue([assetStub.sidecar]); - when(metadataMock.readTags) - .calledWith(assetStub.sidecar.originalPath) - // higher priority tag - .mockResolvedValue({ CreationDate: originalDate.toISOString() }); - when(metadataMock.readTags) - .calledWith(assetStub.sidecar.sidecarPath as string) - // lower priority tag, but in sidecar - .mockResolvedValue({ CreateDate: sidecarDate.toISOString() }); + metadataMock.readTags.mockImplementation((path) => { + const map = { + [assetStub.sidecar.originalPath]: originalDate.toISOString(), + [assetStub.sidecar.sidecarPath as string]: sidecarDate.toISOString(), + }; + return Promise.resolve({ CreationDate: map[path] ?? new Date().toISOString() }); + }); await sut.handleMetadataExtraction({ id: assetStub.image.id }); expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.sidecar.id]); @@ -368,6 +377,7 @@ describe(MetadataService.name, () => { ); expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.livePhotoStillAsset.id]); expect(assetMock.create).toHaveBeenCalled(); // This could have arguments added + expect(userMock.updateUsage).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.ownerId, 512); expect(storageMock.writeFile).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.originalPath, video); expect(assetMock.update).toHaveBeenNthCalledWith(1, { id: assetStub.livePhotoStillAsset.id, @@ -396,6 +406,7 @@ describe(MetadataService.name, () => { ); expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.livePhotoStillAsset.id]); expect(assetMock.create).toHaveBeenCalled(); // This could have arguments added + expect(userMock.updateUsage).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.ownerId, 512); expect(storageMock.writeFile).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.originalPath, video); expect(assetMock.update).toHaveBeenNthCalledWith(1, { id: assetStub.livePhotoStillAsset.id, @@ -422,6 +433,7 @@ describe(MetadataService.name, () => { expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.livePhotoStillAsset.id]); expect(storageMock.readFile).toHaveBeenCalledWith(assetStub.livePhotoStillAsset.originalPath, expect.any(Object)); expect(assetMock.create).toHaveBeenCalled(); // This could have arguments added + expect(userMock.updateUsage).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.ownerId, 512); expect(storageMock.writeFile).toHaveBeenCalledWith(assetStub.livePhotoMotionAsset.originalPath, video); expect(assetMock.update).toHaveBeenNthCalledWith(1, { id: assetStub.livePhotoStillAsset.id, @@ -439,7 +451,9 @@ describe(MetadataService.name, () => { }); cryptoRepository.hashSha1.mockReturnValue(randomBytes(512)); assetMock.getByChecksum.mockResolvedValue(null); - assetMock.create.mockResolvedValue(assetStub.livePhotoMotionAsset); + assetMock.create.mockImplementation((asset) => Promise.resolve({ ...assetStub.livePhotoMotionAsset, ...asset })); + const video = randomBytes(512); + storageMock.readFile.mockResolvedValue(video); await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(jobMock.queue).toHaveBeenNthCalledWith(2, { @@ -448,7 +462,7 @@ describe(MetadataService.name, () => { }); }); - it('should not create a new motionphoto video asset if the of the extracted video matches an existing asset', async () => { + it('should not create a new motion photo video asset if the hash of the extracted video matches an existing asset', async () => { assetMock.getByIds.mockResolvedValue([assetStub.livePhotoStillAsset]); metadataMock.readTags.mockResolvedValue({ Directory: 'foo/bar/', @@ -458,6 +472,8 @@ describe(MetadataService.name, () => { }); cryptoRepository.hashSha1.mockReturnValue(randomBytes(512)); assetMock.getByChecksum.mockResolvedValue(assetStub.livePhotoMotionAsset); + const video = randomBytes(512); + storageMock.readFile.mockResolvedValue(video); await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); expect(assetMock.create).toHaveBeenCalledTimes(0); @@ -467,6 +483,50 @@ describe(MetadataService.name, () => { expect(jobMock.queue).toHaveBeenCalledTimes(0); }); + it('should link and hide motion video asset to still asset if the hash of the extracted video matches an existing asset', async () => { + assetMock.getByIds.mockResolvedValue([{ ...assetStub.livePhotoStillAsset, livePhotoVideoId: null }]); + metadataMock.readTags.mockResolvedValue({ + Directory: 'foo/bar/', + MotionPhoto: 1, + MicroVideo: 1, + MicroVideoOffset: 1, + }); + cryptoRepository.hashSha1.mockReturnValue(randomBytes(512)); + assetMock.getByChecksum.mockResolvedValue({ ...assetStub.livePhotoMotionAsset, isVisible: true }); + const video = randomBytes(512); + storageMock.readFile.mockResolvedValue(video); + + await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); + expect(assetMock.update).toHaveBeenNthCalledWith(1, { + id: assetStub.livePhotoMotionAsset.id, + isVisible: false, + }); + expect(assetMock.update).toHaveBeenNthCalledWith(2, { + id: assetStub.livePhotoStillAsset.id, + livePhotoVideoId: assetStub.livePhotoMotionAsset.id, + }); + }); + + it('should not update storage usage if motion photo is external', async () => { + assetMock.getByIds.mockResolvedValue([ + { ...assetStub.livePhotoStillAsset, livePhotoVideoId: null, isExternal: true }, + ]); + metadataMock.readTags.mockResolvedValue({ + Directory: 'foo/bar/', + MotionPhoto: 1, + MicroVideo: 1, + MicroVideoOffset: 1, + }); + cryptoRepository.hashSha1.mockReturnValue(randomBytes(512)); + assetMock.getByChecksum.mockResolvedValue(null); + assetMock.create.mockResolvedValue(assetStub.livePhotoMotionAsset); + const video = randomBytes(512); + storageMock.readFile.mockResolvedValue(video); + + await sut.handleMetadataExtraction({ id: assetStub.livePhotoStillAsset.id }); + expect(userMock.updateUsage).not.toHaveBeenCalled(); + }); + it('should save all metadata', async () => { const tags: ImmichTags = { BitsPerSample: 1, diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 9b249a49f4..e28f1cd474 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -25,13 +25,14 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMetadataRepository, ImmichTags } from 'src/interfaces/metadata.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; +import { IUserRepository } from 'src/interfaces/user.interface'; import { handlePromiseError } from 'src/utils/misc'; import { usePagination } from 'src/utils/pagination'; @@ -97,7 +98,6 @@ const validate = (value: T): NonNullable | null => { @Injectable() export class MetadataService { - private logger = new ImmichLogger(MetadataService.name); private storageCore: StorageCore; private configCore: SystemConfigCore; private subscription: Subscription | null = null; @@ -115,15 +115,19 @@ export class MetadataService { @Inject(IPersonRepository) personRepository: IPersonRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, + @Inject(IUserRepository) private userRepository: IUserRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(MetadataService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); this.storageCore = StorageCore.create( assetRepository, + cryptoRepository, moveRepository, personRepository, - cryptoRepository, - configRepository, storageRepository, + configRepository, + this.logger, ); } @@ -405,13 +409,19 @@ export class MetadataService { } const checksum = this.cryptoRepository.hashSha1(video); - let motionAsset = await this.assetRepository.getByChecksum(asset.ownerId, checksum); + let motionAsset = await this.assetRepository.getByChecksum(asset.libraryId, checksum); if (motionAsset) { this.logger.debug( `Asset ${asset.id}'s motion photo video with checksum ${checksum.toString( 'base64', )} already exists in the repository`, ); + + // Hide the motion photo video asset if it's not already hidden to prepare for linking + if (motionAsset.isVisible) { + await this.assetRepository.update({ id: motionAsset.id, isVisible: false }); + this.logger.log(`Hid unlinked motion photo video asset (${motionAsset.id})`); + } } else { // We create a UUID in advance so that each extracted video can have a unique filename // (allowing us to delete old ones if necessary) @@ -438,11 +448,18 @@ export class MetadataService { this.storageCore.ensureFolders(motionPath); await this.storageRepository.writeFile(motionAsset.originalPath, video); await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: motionAsset.id } }); + if (!asset.isExternal) { + await this.userRepository.updateUsage(asset.ownerId, video.byteLength); + } + } + + if (asset.livePhotoVideoId !== motionAsset.id) { await this.assetRepository.update({ id: asset.id, livePhotoVideoId: motionAsset.id }); // If the asset already had an associated livePhotoVideo, delete it, because // its checksum doesn't match the checksum of the motionAsset we just extracted - // (if it did, getByChecksum() would've returned non-null) + // (if it did, getByChecksum() would've returned a motionAsset with the same ID as livePhotoVideoId) + // note asset.livePhotoVideoId is not motionAsset.id yet if (asset.livePhotoVideoId) { await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } }); this.logger.log(`Removed old motion photo video asset (${asset.livePhotoVideoId})`); diff --git a/server/src/services/partner.service.spec.ts b/server/src/services/partner.service.spec.ts index a3c4af7367..70c73b0d94 100644 --- a/server/src/services/partner.service.spec.ts +++ b/server/src/services/partner.service.spec.ts @@ -7,6 +7,7 @@ import { PartnerService } from 'src/services/partner.service'; import { authStub } from 'test/fixtures/auth.stub'; import { partnerStub } from 'test/fixtures/partner.stub'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; +import { Mocked } from 'vitest'; const responseDto = { admin: { @@ -49,8 +50,8 @@ const responseDto = { describe(PartnerService.name, () => { let sut: PartnerService; - let partnerMock: jest.Mocked; - let accessMock: jest.Mocked; + let partnerMock: Mocked; + let accessMock: Mocked; beforeEach(() => { partnerMock = newPartnerRepositoryMock(); diff --git a/server/src/services/person.service.spec.ts b/server/src/services/person.service.spec.ts index 501154c1db..a9d96bbcea 100644 --- a/server/src/services/person.service.spec.ts +++ b/server/src/services/person.service.spec.ts @@ -6,6 +6,7 @@ import { Colorspace, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; @@ -23,6 +24,7 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMachineLearningRepositoryMock } from 'test/repositories/machine-learning.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; @@ -31,6 +33,7 @@ import { newSearchRepositoryMock } from 'test/repositories/search.repository.moc import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { IsNull } from 'typeorm'; +import { Mocked } from 'vitest'; const responseDto: PersonResponseDto = { id: 'person-1', @@ -61,16 +64,17 @@ const detectFaceMock = { describe(PersonService.name, () => { let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let jobMock: jest.Mocked; - let machineLearningMock: jest.Mocked; - let mediaMock: jest.Mocked; - let moveMock: jest.Mocked; - let personMock: jest.Mocked; - let storageMock: jest.Mocked; - let searchMock: jest.Mocked; - let cryptoMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let jobMock: Mocked; + let machineLearningMock: Mocked; + let mediaMock: Mocked; + let moveMock: Mocked; + let personMock: Mocked; + let storageMock: Mocked; + let searchMock: Mocked; + let cryptoMock: Mocked; + let loggerMock: Mocked; let sut: PersonService; beforeEach(() => { @@ -85,6 +89,7 @@ describe(PersonService.name, () => { storageMock = newStorageRepositoryMock(); searchMock = newSearchRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); sut = new PersonService( accessMock, assetMock, @@ -97,6 +102,7 @@ describe(PersonService.name, () => { jobMock, searchMock, cryptoMock, + loggerMock, ); mediaMock.crop.mockResolvedValue(croppedFace); diff --git a/server/src/services/person.service.ts b/server/src/services/person.service.ts index d2bc81b0ea..77b7e552cc 100644 --- a/server/src/services/person.service.ts +++ b/server/src/services/person.service.ts @@ -38,6 +38,7 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { CropOptions, IMediaRepository } from 'src/interfaces/media.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; @@ -46,7 +47,6 @@ import { ISearchRepository } from 'src/interfaces/search.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { CacheControl, ImmichFileResponse } from 'src/utils/file'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; import { usePagination } from 'src/utils/pagination'; import { IsNull } from 'typeorm'; @@ -56,7 +56,6 @@ export class PersonService { private access: AccessCore; private configCore: SystemConfigCore; private storageCore: StorageCore; - readonly logger = new ImmichLogger(PersonService.name); constructor( @Inject(IAccessRepository) accessRepository: IAccessRepository, @@ -70,16 +69,19 @@ export class PersonService { @Inject(IJobRepository) private jobRepository: IJobRepository, @Inject(ISearchRepository) private smartInfoRepository: ISearchRepository, @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { this.access = AccessCore.create(accessRepository); - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(PersonService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); this.storageCore = StorageCore.create( assetRepository, + cryptoRepository, moveRepository, repository, - cryptoRepository, - configRepository, storageRepository, + configRepository, + this.logger, ); } diff --git a/server/src/services/search.service.spec.ts b/server/src/services/search.service.spec.ts index 72b543f2d7..a81ea87973 100644 --- a/server/src/services/search.service.spec.ts +++ b/server/src/services/search.service.spec.ts @@ -2,6 +2,7 @@ import { mapAsset } from 'src/dtos/asset-response.dto'; import { SearchDto } from 'src/dtos/search.dto'; import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; @@ -13,24 +14,27 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { personStub } from 'test/fixtures/person.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMachineLearningRepositoryMock } from 'test/repositories/machine-learning.repository.mock'; import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newSearchRepositoryMock } from 'test/repositories/search.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked, vitest } from 'vitest'; -jest.useFakeTimers(); +vitest.useFakeTimers(); describe(SearchService.name, () => { let sut: SearchService; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let machineMock: jest.Mocked; - let personMock: jest.Mocked; - let searchMock: jest.Mocked; - let partnerMock: jest.Mocked; - let metadataMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let machineMock: Mocked; + let personMock: Mocked; + let searchMock: Mocked; + let partnerMock: Mocked; + let metadataMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); @@ -40,8 +44,18 @@ describe(SearchService.name, () => { searchMock = newSearchRepositoryMock(); partnerMock = newPartnerRepositoryMock(); metadataMock = newMetadataRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); - sut = new SearchService(configMock, machineMock, personMock, searchMock, assetMock, partnerMock, metadataMock); + sut = new SearchService( + configMock, + machineMock, + personMock, + searchMock, + assetMock, + partnerMock, + metadataMock, + loggerMock, + ); }); it('should work', () => { diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 9422dac86b..b8e9f13fa6 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -18,6 +18,7 @@ import { import { AssetOrder } from 'src/entities/album.entity'; import { AssetEntity } from 'src/entities/asset.entity'; import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; import { IPartnerRepository } from 'src/interfaces/partner.interface'; @@ -37,8 +38,10 @@ export class SearchService { @Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, @Inject(IMetadataRepository) private metadataRepository: IMetadataRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(SearchService.name); + this.configCore = SystemConfigCore.create(configRepository, logger); } async searchPerson(auth: AuthDto, dto: SearchPeopleDto): Promise { diff --git a/server/src/services/server-info.service.spec.ts b/server/src/services/server-info.service.spec.ts index 0348f26d2d..836909b74f 100644 --- a/server/src/services/server-info.service.spec.ts +++ b/server/src/services/server-info.service.spec.ts @@ -1,6 +1,7 @@ import { serverVersion } from 'src/constants'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { IEventRepository } from 'src/interfaces/event.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; @@ -8,20 +9,23 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interf import { IUserRepository } from 'src/interfaces/user.interface'; import { ServerInfoService } from 'src/services/server-info.service'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newServerInfoRepositoryMock } from 'test/repositories/system-info.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked } from 'vitest'; describe(ServerInfoService.name, () => { let sut: ServerInfoService; - let eventMock: jest.Mocked; - let configMock: jest.Mocked; - let serverInfoMock: jest.Mocked; - let storageMock: jest.Mocked; - let userMock: jest.Mocked; - let systemMetadataMock: jest.Mocked; + let eventMock: Mocked; + let configMock: Mocked; + let serverInfoMock: Mocked; + let storageMock: Mocked; + let userMock: Mocked; + let systemMetadataMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { configMock = newSystemConfigRepositoryMock(); @@ -30,8 +34,17 @@ describe(ServerInfoService.name, () => { storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); systemMetadataMock = newSystemMetadataRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); - sut = new ServerInfoService(eventMock, configMock, userMock, serverInfoMock, storageMock, systemMetadataMock); + sut = new ServerInfoService( + eventMock, + configMock, + userMock, + serverInfoMock, + storageMock, + systemMetadataMock, + loggerMock, + ); }); it('should work', () => { diff --git a/server/src/services/server-info.service.ts b/server/src/services/server-info.service.ts index 9f0c1e290c..bb092896bf 100644 --- a/server/src/services/server-info.service.ts +++ b/server/src/services/server-info.service.ts @@ -15,19 +15,18 @@ import { } from 'src/dtos/server-info.dto'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { ClientEvent, IEventRepository, ServerEvent, ServerEventMap } from 'src/interfaces/event.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository, UserStatsQueryResponse } from 'src/interfaces/user.interface'; import { asHumanReadable } from 'src/utils/bytes'; -import { ImmichLogger } from 'src/utils/logger'; import { mimeTypes } from 'src/utils/mime-types'; import { Version } from 'src/utils/version'; @Injectable() export class ServerInfoService { - private logger = new ImmichLogger(ServerInfoService.name); private configCore: SystemConfigCore; private releaseVersion = serverVersion; private releaseVersionCheckedAt: DateTime | null = null; @@ -38,9 +37,11 @@ export class ServerInfoService { @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IServerInfoRepository) private repository: IServerInfoRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, - @Inject(ISystemMetadataRepository) private readonly systemMetadataRepository: ISystemMetadataRepository, + @Inject(ISystemMetadataRepository) private systemMetadataRepository: ISystemMetadataRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(ServerInfoService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); } onConnect() {} diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index cad52928ca..6baeac858a 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -12,12 +12,13 @@ import { sharedLinkResponseStub, sharedLinkStub } from 'test/fixtures/shared-lin import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newSharedLinkRepositoryMock } from 'test/repositories/shared-link.repository.mock'; +import { Mocked } from 'vitest'; describe(SharedLinkService.name, () => { let sut: SharedLinkService; let accessMock: IAccessRepositoryMock; - let cryptoMock: jest.Mocked; - let shareMock: jest.Mocked; + let cryptoMock: Mocked; + let shareMock: Mocked; beforeEach(() => { accessMock = newAccessRepositoryMock(); diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index 2e1dfbafc0..4d85c00253 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -3,6 +3,7 @@ import { SystemConfigKey } from 'src/entities/system-config.entity'; import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; @@ -12,9 +13,11 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMachineLearningRepositoryMock } from 'test/repositories/machine-learning.repository.mock'; import { newSearchRepositoryMock } from 'test/repositories/search.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked } from 'vitest'; const asset = { id: 'asset-1', @@ -23,12 +26,13 @@ const asset = { describe(SmartInfoService.name, () => { let sut: SmartInfoService; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let jobMock: jest.Mocked; - let searchMock: jest.Mocked; - let machineMock: jest.Mocked; - let databaseMock: jest.Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let jobMock: Mocked; + let searchMock: Mocked; + let machineMock: Mocked; + let databaseMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); @@ -37,7 +41,8 @@ describe(SmartInfoService.name, () => { jobMock = newJobRepositoryMock(); machineMock = newMachineLearningRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); - sut = new SmartInfoService(assetMock, databaseMock, jobMock, machineMock, searchMock, configMock); + loggerMock = newLoggerRepositoryMock(); + sut = new SmartInfoService(assetMock, databaseMock, jobMock, machineMock, searchMock, configMock, loggerMock); assetMock.getByIds.mockResolvedValue([asset]); }); diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index f9d36c238c..9de5edbd88 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -11,16 +11,15 @@ import { JobStatus, QueueName, } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; import { usePagination } from 'src/utils/pagination'; @Injectable() export class SmartInfoService { private configCore: SystemConfigCore; - private logger = new ImmichLogger(SmartInfoService.name); constructor( @Inject(IAssetRepository) private assetRepository: IAssetRepository, @@ -29,8 +28,10 @@ export class SmartInfoService { @Inject(IMachineLearningRepository) private machineLearning: IMachineLearningRepository, @Inject(ISearchRepository) private repository: ISearchRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(SmartInfoService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); } async init() { diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index ba1cb3e59b..952b656546 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,6 +1,6 @@ -import { when } from 'jest-when'; import { Stats } from 'node:fs'; import { SystemConfigCore, defaults } from 'src/cores/system-config.core'; +import { AssetEntity } from 'src/entities/asset.entity'; import { AssetPathType } from 'src/entities/move.entity'; import { SystemConfig, SystemConfigKey } from 'src/entities/system-config.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; @@ -8,6 +8,7 @@ import { IAssetRepository } from 'src/interfaces/asset.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; @@ -20,23 +21,26 @@ import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock' import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newDatabaseRepositoryMock } from 'test/repositories/database.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newPersonRepositoryMock } from 'test/repositories/person.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked } from 'vitest'; describe(StorageTemplateService.name, () => { let sut: StorageTemplateService; - let albumMock: jest.Mocked; - let assetMock: jest.Mocked; - let configMock: jest.Mocked; - let moveMock: jest.Mocked; - let personMock: jest.Mocked; - let storageMock: jest.Mocked; - let userMock: jest.Mocked; - let cryptoMock: jest.Mocked; - let databaseMock: jest.Mocked; + let albumMock: Mocked; + let assetMock: Mocked; + let configMock: Mocked; + let moveMock: Mocked; + let personMock: Mocked; + let storageMock: Mocked; + let userMock: Mocked; + let cryptoMock: Mocked; + let databaseMock: Mocked; + let loggerMock: Mocked; it('should work', () => { expect(sut).toBeDefined(); @@ -52,6 +56,7 @@ describe(StorageTemplateService.name, () => { userMock = newUserRepositoryMock(); cryptoMock = newCryptoRepositoryMock(); databaseMock = newDatabaseRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); configMock.load.mockResolvedValue([{ key: SystemConfigKey.STORAGE_TEMPLATE_ENABLED, value: true }]); @@ -65,9 +70,10 @@ describe(StorageTemplateService.name, () => { userMock, cryptoMock, databaseMock, + loggerMock, ); - SystemConfigCore.create(configMock).config$.next(defaults); + SystemConfigCore.create(configMock, loggerMock).config$.next(defaults); }); describe('onValidateConfig', () => { @@ -118,43 +124,28 @@ describe(StorageTemplateService.name, () => { const newMotionPicturePath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${assetStub.livePhotoStillAsset.id}.mp4`; const newStillPicturePath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${assetStub.livePhotoStillAsset.id}.jpeg`; - when(assetMock.getByIds) - .calledWith([assetStub.livePhotoStillAsset.id], { exifInfo: true }) - .mockResolvedValue([assetStub.livePhotoStillAsset]); + assetMock.getByIds.mockImplementation((ids) => { + const assets = [assetStub.livePhotoStillAsset, assetStub.livePhotoMotionAsset]; + return Promise.resolve( + ids.map((id) => assets.find((asset) => asset.id === id)).filter((asset) => !!asset), + ) as Promise; + }); - when(assetMock.getByIds) - .calledWith([assetStub.livePhotoMotionAsset.id], { exifInfo: true }) - .mockResolvedValue([assetStub.livePhotoMotionAsset]); + moveMock.create.mockResolvedValueOnce({ + id: '123', + entityId: assetStub.livePhotoStillAsset.id, + pathType: AssetPathType.ORIGINAL, + oldPath: assetStub.livePhotoStillAsset.originalPath, + newPath: newStillPicturePath, + }); - when(moveMock.create) - .calledWith({ - entityId: assetStub.livePhotoStillAsset.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.livePhotoStillAsset.originalPath, - newPath: newStillPicturePath, - }) - .mockResolvedValue({ - id: '123', - entityId: assetStub.livePhotoStillAsset.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.livePhotoStillAsset.originalPath, - newPath: newStillPicturePath, - }); - - when(moveMock.create) - .calledWith({ - entityId: assetStub.livePhotoMotionAsset.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.livePhotoMotionAsset.originalPath, - newPath: newMotionPicturePath, - }) - .mockResolvedValue({ - id: '124', - entityId: assetStub.livePhotoMotionAsset.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.livePhotoMotionAsset.originalPath, - newPath: newMotionPicturePath, - }); + moveMock.create.mockResolvedValueOnce({ + id: '124', + entityId: assetStub.livePhotoMotionAsset.id, + pathType: AssetPathType.ORIGINAL, + oldPath: assetStub.livePhotoMotionAsset.originalPath, + newPath: newMotionPicturePath, + }); await expect(sut.handleMigrationSingle({ id: assetStub.livePhotoStillAsset.id })).resolves.toBe( JobStatus.SUCCESS, @@ -177,34 +168,22 @@ describe(StorageTemplateService.name, () => { const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${assetStub.image.id}.jpg`; const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${assetStub.image.id}.jpg`; - when(storageMock.checkFileExists).calledWith(assetStub.image.originalPath).mockResolvedValue(true); - when(storageMock.checkFileExists).calledWith(previousFailedNewPath).mockResolvedValue(false); - - when(moveMock.getByEntity).calledWith(assetStub.image.id, AssetPathType.ORIGINAL).mockResolvedValue({ + storageMock.checkFileExists.mockImplementation((path) => Promise.resolve(path === assetStub.image.originalPath)); + moveMock.getByEntity.mockResolvedValue({ id: '123', entityId: assetStub.image.id, pathType: AssetPathType.ORIGINAL, oldPath: assetStub.image.originalPath, newPath: previousFailedNewPath, }); - - when(assetMock.getByIds) - .calledWith([assetStub.image.id], { exifInfo: true }) - .mockResolvedValue([assetStub.image]); - - when(moveMock.update) - .calledWith({ - id: '123', - oldPath: assetStub.image.originalPath, - newPath, - }) - .mockResolvedValue({ - id: '123', - entityId: assetStub.image.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.image.originalPath, - newPath, - }); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + moveMock.update.mockResolvedValue({ + id: '123', + entityId: assetStub.image.id, + pathType: AssetPathType.ORIGINAL, + oldPath: assetStub.image.originalPath, + newPath, + }); await expect(sut.handleMigrationSingle({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); @@ -226,38 +205,24 @@ describe(StorageTemplateService.name, () => { const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${assetStub.image.id}.jpg`; const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${assetStub.image.id}.jpg`; - when(storageMock.checkFileExists).calledWith(assetStub.image.originalPath).mockResolvedValue(false); - when(storageMock.checkFileExists).calledWith(previousFailedNewPath).mockResolvedValue(true); - when(storageMock.stat) - .calledWith(previousFailedNewPath) - .mockResolvedValue({ size: 5000 } as Stats); - when(cryptoMock.hashFile).calledWith(previousFailedNewPath).mockResolvedValue(assetStub.image.checksum); - - when(moveMock.getByEntity).calledWith(assetStub.image.id, AssetPathType.ORIGINAL).mockResolvedValue({ + storageMock.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); + storageMock.stat.mockResolvedValue({ size: 5000 } as Stats); + cryptoMock.hashFile.mockResolvedValue(assetStub.image.checksum); + moveMock.getByEntity.mockResolvedValue({ id: '123', entityId: assetStub.image.id, pathType: AssetPathType.ORIGINAL, oldPath: assetStub.image.originalPath, newPath: previousFailedNewPath, }); - - when(assetMock.getByIds) - .calledWith([assetStub.image.id], { exifInfo: true }) - .mockResolvedValue([assetStub.image]); - - when(moveMock.update) - .calledWith({ - id: '123', - oldPath: previousFailedNewPath, - newPath, - }) - .mockResolvedValue({ - id: '123', - entityId: assetStub.image.id, - pathType: AssetPathType.ORIGINAL, - oldPath: previousFailedNewPath, - newPath, - }); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + moveMock.update.mockResolvedValue({ + id: '123', + entityId: assetStub.image.id, + pathType: AssetPathType.ORIGINAL, + oldPath: previousFailedNewPath, + newPath, + }); await expect(sut.handleMigrationSingle({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); @@ -281,30 +246,17 @@ describe(StorageTemplateService.name, () => { userMock.get.mockResolvedValue(userStub.user1); const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${assetStub.image.id}.jpg`; - when(storageMock.rename).calledWith(assetStub.image.originalPath, newPath).mockRejectedValue({ code: 'EXDEV' }); - when(storageMock.stat) - .calledWith(newPath) - .mockResolvedValue({ size: 5000 } as Stats); - when(cryptoMock.hashFile).calledWith(newPath).mockResolvedValue(Buffer.from('different-hash', 'utf8')); - - when(assetMock.getByIds) - .calledWith([assetStub.image.id], { exifInfo: true }) - .mockResolvedValue([assetStub.image]); - - when(moveMock.create) - .calledWith({ - entityId: assetStub.image.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.image.originalPath, - newPath: newPath, - }) - .mockResolvedValue({ - id: '123', - entityId: assetStub.image.id, - pathType: AssetPathType.ORIGINAL, - oldPath: assetStub.image.originalPath, - newPath, - }); + storageMock.rename.mockRejectedValue({ code: 'EXDEV' }); + storageMock.stat.mockResolvedValue({ size: 5000 } as Stats); + cryptoMock.hashFile.mockResolvedValue(Buffer.from('different-hash', 'utf8')); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + moveMock.create.mockResolvedValue({ + id: '123', + entityId: assetStub.image.id, + pathType: AssetPathType.ORIGINAL, + oldPath: assetStub.image.originalPath, + newPath, + }); await expect(sut.handleMigrationSingle({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); @@ -335,38 +287,24 @@ describe(StorageTemplateService.name, () => { const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${assetStub.image.id}.jpg`; const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${assetStub.image.id}.jpg`; - when(storageMock.checkFileExists).calledWith(assetStub.image.originalPath).mockResolvedValue(false); - when(storageMock.checkFileExists).calledWith(previousFailedNewPath).mockResolvedValue(true); - when(storageMock.stat) - .calledWith(previousFailedNewPath) - .mockResolvedValue({ size: failedPathSize } as Stats); - when(cryptoMock.hashFile).calledWith(previousFailedNewPath).mockResolvedValue(failedPathChecksum); - - when(moveMock.getByEntity).calledWith(assetStub.image.id, AssetPathType.ORIGINAL).mockResolvedValue({ + storageMock.checkFileExists.mockImplementation((path) => Promise.resolve(previousFailedNewPath === path)); + storageMock.stat.mockResolvedValue({ size: failedPathSize } as Stats); + cryptoMock.hashFile.mockResolvedValue(failedPathChecksum); + moveMock.getByEntity.mockResolvedValue({ id: '123', entityId: assetStub.image.id, pathType: AssetPathType.ORIGINAL, oldPath: assetStub.image.originalPath, newPath: previousFailedNewPath, }); - - when(assetMock.getByIds) - .calledWith([assetStub.image.id], { exifInfo: true }) - .mockResolvedValue([assetStub.image]); - - when(moveMock.update) - .calledWith({ - id: '123', - oldPath: previousFailedNewPath, - newPath, - }) - .mockResolvedValue({ - id: '123', - entityId: assetStub.image.id, - pathType: AssetPathType.ORIGINAL, - oldPath: previousFailedNewPath, - newPath, - }); + assetMock.getByIds.mockResolvedValue([assetStub.image]); + moveMock.update.mockResolvedValue({ + id: '123', + entityId: assetStub.image.id, + pathType: AssetPathType.ORIGINAL, + oldPath: previousFailedNewPath, + newPath, + }); await expect(sut.handleMigrationSingle({ id: assetStub.image.id })).resolves.toBe(JobStatus.SUCCESS); @@ -408,13 +346,8 @@ describe(StorageTemplateService.name, () => { newPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg', }); - when(storageMock.checkFileExists) - .calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg') - .mockResolvedValue(true); - - when(storageMock.checkFileExists) - .calledWith('upload/library/user-id/2023/2023-02-23/asset-id+1.jpg') - .mockResolvedValue(false); + storageMock.checkFileExists.mockResolvedValueOnce(true); + storageMock.checkFileExists.mockResolvedValueOnce(false); await sut.handleMigration(); @@ -538,18 +471,18 @@ describe(StorageTemplateService.name, () => { oldPath: assetStub.image.originalPath, newPath, }); - when(storageMock.stat) - .calledWith(newPath) - .mockResolvedValue({ - size: 5000, - } as Stats); - when(storageMock.stat) - .calledWith(assetStub.image.originalPath) - .mockResolvedValue({ - atime: new Date(), - mtime: new Date(), - } as Stats); - when(cryptoMock.hashFile).calledWith(newPath).mockResolvedValue(assetStub.image.checksum); + storageMock.stat.mockResolvedValueOnce({ + atime: new Date(), + mtime: new Date(), + } as Stats); + storageMock.stat.mockResolvedValueOnce({ + size: 5000, + } as Stats); + storageMock.stat.mockResolvedValueOnce({ + atime: new Date(), + mtime: new Date(), + } as Stats); + cryptoMock.hashFile.mockResolvedValue(assetStub.image.checksum); await sut.handleMigration(); @@ -581,11 +514,9 @@ describe(StorageTemplateService.name, () => { oldPath: assetStub.image.originalPath, newPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg', }); - when(storageMock.stat) - .calledWith('upload/library/user-id/2023/2023-02-23/asset-id.jpg') - .mockResolvedValue({ - size: 100, - } as Stats); + storageMock.stat.mockResolvedValue({ + size: 100, + } as Stats); await sut.handleMigration(); diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index 280c37b95b..835f5ea7d7 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -24,13 +24,13 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { DatabaseLock, IDatabaseRepository } from 'src/interfaces/database.interface'; import { ServerAsyncEvent, ServerAsyncEventMap } from 'src/interfaces/event.interface'; import { IEntityJob, JOBS_ASSET_PAGINATION_SIZE, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { getLivePhotoMotionFilename } from 'src/utils/file'; -import { ImmichLogger } from 'src/utils/logger'; import { usePagination } from 'src/utils/pagination'; export interface MoveAssetMetadata { @@ -47,7 +47,6 @@ interface RenderMetadata { @Injectable() export class StorageTemplateService { - private logger = new ImmichLogger(StorageTemplateService.name); private configCore: SystemConfigCore; private storageCore: StorageCore; private _template: { @@ -73,16 +72,19 @@ export class StorageTemplateService { @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository, @Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(StorageTemplateService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); this.configCore.config$.subscribe((config) => this.onConfig(config)); this.storageCore = StorageCore.create( assetRepository, + cryptoRepository, moveRepository, personRepository, - cryptoRepository, - configRepository, storageRepository, + configRepository, + this.logger, ); } diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 977f632d5e..ee574e0ba6 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -1,14 +1,19 @@ +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { StorageService } from 'src/services/storage.service'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; +import { Mocked } from 'vitest'; describe(StorageService.name, () => { let sut: StorageService; - let storageMock: jest.Mocked; + let storageMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { storageMock = newStorageRepositoryMock(); - sut = new StorageService(storageMock); + loggerMock = newLoggerRepositoryMock(); + sut = new StorageService(storageMock, loggerMock); }); it('should work', () => { diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 81fdb4f415..829888e244 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,14 +1,17 @@ import { Inject, Injectable } from '@nestjs/common'; import { StorageCore, StorageFolder } from 'src/cores/storage.core'; import { IDeleteFilesJob, JobStatus } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; -import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class StorageService { - private logger = new ImmichLogger(StorageService.name); - - constructor(@Inject(IStorageRepository) private storageRepository: IStorageRepository) {} + constructor( + @Inject(IStorageRepository) private storageRepository: IStorageRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, + ) { + this.logger.setContext(StorageService.name); + } init() { const libraryBase = StorageCore.getBaseFolder(StorageFolder.LIBRARY); diff --git a/server/src/services/sync.service.spec.ts b/server/src/services/sync.service.spec.ts new file mode 100644 index 0000000000..87205c08f1 --- /dev/null +++ b/server/src/services/sync.service.spec.ts @@ -0,0 +1,102 @@ +import { mapAsset } from 'src/dtos/asset-response.dto'; +import { AssetEntity } from 'src/entities/asset.entity'; +import { IAccessRepository } from 'src/interfaces/access.interface'; +import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IAuditRepository } from 'src/interfaces/audit.interface'; +import { IPartnerRepository } from 'src/interfaces/partner.interface'; +import { SyncService } from 'src/services/sync.service'; +import { assetStub } from 'test/fixtures/asset.stub'; +import { authStub } from 'test/fixtures/auth.stub'; +import { partnerStub } from 'test/fixtures/partner.stub'; +import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; +import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; +import { newAuditRepositoryMock } from 'test/repositories/audit.repository.mock'; +import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; +import { Mocked } from 'vitest'; + +const untilDate = new Date(2024); +const mapAssetOpts = { auth: authStub.user1, stripMetadata: false, withStack: true }; + +describe(SyncService.name, () => { + let sut: SyncService; + let accessMock: Mocked; + let assetMock: Mocked; + let partnerMock: Mocked; + let auditMock: Mocked; + + beforeEach(() => { + partnerMock = newPartnerRepositoryMock(); + assetMock = newAssetRepositoryMock(); + accessMock = newAccessRepositoryMock(); + auditMock = newAuditRepositoryMock(); + sut = new SyncService(accessMock, assetMock, partnerMock, auditMock); + }); + + it('should exist', () => { + expect(sut).toBeDefined(); + }); + + describe('getAllAssetsForUserFullSync', () => { + it('should return a list of all assets owned by the user', async () => { + assetMock.getAllForUserFullSync.mockResolvedValue([assetStub.external, assetStub.hasEncodedVideo]); + await expect( + sut.getAllAssetsForUserFullSync(authStub.user1, { limit: 2, updatedUntil: untilDate }), + ).resolves.toEqual([ + mapAsset(assetStub.external, mapAssetOpts), + mapAsset(assetStub.hasEncodedVideo, mapAssetOpts), + ]); + expect(assetMock.getAllForUserFullSync).toHaveBeenCalledWith({ + ownerId: authStub.user1.user.id, + updatedUntil: untilDate, + limit: 2, + }); + }); + }); + + describe('getChangesForDeltaSync', () => { + it('should return a response requiring a full sync when partners are out of sync', async () => { + partnerMock.getAll.mockResolvedValue([partnerStub.adminToUser1]); + await expect( + sut.getChangesForDeltaSync(authStub.user1, { updatedAfter: new Date(), userIds: [authStub.user1.user.id] }), + ).resolves.toEqual({ needsFullSync: true, upserted: [], deleted: [] }); + expect(assetMock.getChangedDeltaSync).toHaveBeenCalledTimes(0); + expect(auditMock.getAfter).toHaveBeenCalledTimes(0); + }); + + it('should return a response requiring a full sync when last sync was too long ago', async () => { + partnerMock.getAll.mockResolvedValue([]); + await expect( + sut.getChangesForDeltaSync(authStub.user1, { updatedAfter: new Date(2000), userIds: [authStub.user1.user.id] }), + ).resolves.toEqual({ needsFullSync: true, upserted: [], deleted: [] }); + expect(assetMock.getChangedDeltaSync).toHaveBeenCalledTimes(0); + expect(auditMock.getAfter).toHaveBeenCalledTimes(0); + }); + + it('should return a response requiring a full sync when there are too many changes', async () => { + partnerMock.getAll.mockResolvedValue([]); + assetMock.getChangedDeltaSync.mockResolvedValue( + Array.from({ length: 10_000 }).fill(assetStub.image), + ); + await expect( + sut.getChangesForDeltaSync(authStub.user1, { updatedAfter: new Date(), userIds: [authStub.user1.user.id] }), + ).resolves.toEqual({ needsFullSync: true, upserted: [], deleted: [] }); + expect(assetMock.getChangedDeltaSync).toHaveBeenCalledTimes(1); + expect(auditMock.getAfter).toHaveBeenCalledTimes(0); + }); + + it('should return a response with changes and deletions', async () => { + partnerMock.getAll.mockResolvedValue([]); + assetMock.getChangedDeltaSync.mockResolvedValue([assetStub.image1]); + auditMock.getAfter.mockResolvedValue([assetStub.external.id]); + await expect( + sut.getChangesForDeltaSync(authStub.user1, { updatedAfter: new Date(), userIds: [authStub.user1.user.id] }), + ).resolves.toEqual({ + needsFullSync: false, + upserted: [mapAsset(assetStub.image1, mapAssetOpts)], + deleted: [assetStub.external.id], + }); + expect(assetMock.getChangedDeltaSync).toHaveBeenCalledTimes(1); + expect(auditMock.getAfter).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts new file mode 100644 index 0000000000..be11d36fa0 --- /dev/null +++ b/server/src/services/sync.service.ts @@ -0,0 +1,77 @@ +import { Inject } from '@nestjs/common'; +import _ from 'lodash'; +import { DateTime } from 'luxon'; +import { AUDIT_LOG_MAX_DURATION } from 'src/constants'; +import { AccessCore, Permission } from 'src/cores/access.core'; +import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto'; +import { AuthDto } from 'src/dtos/auth.dto'; +import { AssetDeltaSyncDto, AssetDeltaSyncResponseDto, AssetFullSyncDto } from 'src/dtos/sync.dto'; +import { DatabaseAction, EntityType } from 'src/entities/audit.entity'; +import { IAccessRepository } from 'src/interfaces/access.interface'; +import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { IAuditRepository } from 'src/interfaces/audit.interface'; +import { IPartnerRepository } from 'src/interfaces/partner.interface'; + +export class SyncService { + private access: AccessCore; + + constructor( + @Inject(IAccessRepository) accessRepository: IAccessRepository, + @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, + @Inject(IAuditRepository) private auditRepository: IAuditRepository, + ) { + this.access = AccessCore.create(accessRepository); + } + + async getAllAssetsForUserFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise { + const userId = dto.userId || auth.user.id; + await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId); + const assets = await this.assetRepository.getAllForUserFullSync({ + ownerId: userId, + lastCreationDate: dto.lastCreationDate, + updatedUntil: dto.updatedUntil, + lastId: dto.lastId, + limit: dto.limit, + }); + const options = { auth, stripMetadata: false, withStack: true }; + return assets.map((a) => mapAsset(a, options)); + } + + async getChangesForDeltaSync(auth: AuthDto, dto: AssetDeltaSyncDto): Promise { + await this.access.requirePermission(auth, Permission.TIMELINE_READ, dto.userIds); + const partner = await this.partnerRepository.getAll(auth.user.id); + const userIds = [auth.user.id, ...partner.filter((p) => p.sharedWithId == auth.user.id).map((p) => p.sharedById)]; + userIds.sort(); + dto.userIds.sort(); + const duration = DateTime.now().diff(DateTime.fromJSDate(dto.updatedAfter)); + + if (!_.isEqual(userIds, dto.userIds) || duration > AUDIT_LOG_MAX_DURATION) { + // app does not have the correct partners synced + // or app has not synced in the last 100 days + return { needsFullSync: true, deleted: [], upserted: [] }; + } + + const limit = 10_000; + const upserted = await this.assetRepository.getChangedDeltaSync({ limit, updatedAfter: dto.updatedAfter, userIds }); + + if (upserted.length === limit) { + // too many changes -> do a full sync (paginated) instead + return { needsFullSync: true, deleted: [], upserted: [] }; + } + + const deleted = await this.auditRepository.getAfter(dto.updatedAfter, { + userIds: userIds, + entityType: EntityType.ASSET, + action: DatabaseAction.DELETE, + }); + + const options = { auth, stripMetadata: false, withStack: true }; + const result = { + needsFullSync: false, + upserted: upserted.map((a) => mapAsset(a, options)), + deleted, + }; + return result; + } +} diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 4bb5dd0a1b..49bf8d6544 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -16,12 +16,14 @@ import { } from 'src/entities/system-config.entity'; import { IEventRepository, ServerEvent } from 'src/interfaces/event.interface'; import { QueueName } from 'src/interfaces/job.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { SystemConfigService } from 'src/services/system-config.service'; -import { ImmichLogger } from 'src/utils/logger'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; +import { Mocked } from 'vitest'; const updates: SystemConfigEntity[] = [ { key: SystemConfigKey.FFMPEG_CRF, value: 30 }, @@ -154,15 +156,17 @@ const updatedConfig = Object.freeze({ describe(SystemConfigService.name, () => { let sut: SystemConfigService; - let configMock: jest.Mocked; - let eventMock: jest.Mocked; - let smartInfoMock: jest.Mocked; + let configMock: Mocked; + let eventMock: Mocked; + let loggerMock: Mocked; + let smartInfoMock: Mocked; beforeEach(() => { delete process.env.IMMICH_CONFIG_FILE; configMock = newSystemConfigRepositoryMock(); eventMock = newEventRepositoryMock(); - sut = new SystemConfigService(configMock, eventMock, smartInfoMock); + loggerMock = newLoggerRepositoryMock(); + sut = new SystemConfigService(configMock, eventMock, loggerMock, smartInfoMock); }); it('should work', () => { @@ -179,16 +183,6 @@ describe(SystemConfigService.name, () => { }); describe('getConfig', () => { - let warnLog: jest.SpyInstance; - - beforeEach(() => { - warnLog = jest.spyOn(ImmichLogger.prototype, 'warn'); - }); - - afterEach(() => { - warnLog.mockRestore(); - }); - it('should return the default config', async () => { configMock.load.mockResolvedValue([]); @@ -266,7 +260,7 @@ describe(SystemConfigService.name, () => { configMock.readFile.mockResolvedValue(partialConfig); await sut.getConfig(); - expect(warnLog).toHaveBeenCalled(); + expect(loggerMock.warn).toHaveBeenCalled(); }); const tests = [ @@ -285,7 +279,7 @@ describe(SystemConfigService.name, () => { if (test.warn) { await sut.getConfig(); - expect(warnLog).toHaveBeenCalled(); + expect(loggerMock.warn).toHaveBeenCalled(); } else { await expect(sut.getConfig()).rejects.toBeInstanceOf(Error); } @@ -346,19 +340,6 @@ describe(SystemConfigService.name, () => { }); }); - describe('refreshConfig', () => { - it('should notify the subscribers', async () => { - const changeMock = jest.fn(); - const subscription = sut.config$.subscribe(changeMock); - - await sut.refreshConfig(); - - expect(changeMock).toHaveBeenCalledWith(defaults); - - subscription.unsubscribe(); - }); - }); - describe('getCustomCss', () => { it('should return the default theme', async () => { await expect(sut.getCustomCss()).resolves.toEqual(defaults.theme.customCss); diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index bc57c83ac6..2203f7a687 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -22,21 +22,22 @@ import { ServerAsyncEventMap, ServerEvent, } from 'src/interfaces/event.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ISearchRepository } from 'src/interfaces/search.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class SystemConfigService { - private logger = new ImmichLogger(SystemConfigService.name); private core: SystemConfigCore; constructor( @Inject(ISystemConfigRepository) private repository: ISystemConfigRepository, @Inject(IEventRepository) private eventRepository: IEventRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, @Inject(ISearchRepository) private smartInfoRepository: ISearchRepository, ) { - this.core = SystemConfigCore.create(repository); + this.logger.setContext(SystemConfigService.name); + this.core = SystemConfigCore.create(repository, this.logger); this.core.config$.subscribe((config) => this.setLogLevel(config)); } @@ -90,13 +91,6 @@ export class SystemConfigService { return mapConfig(newConfig); } - // this is only used by the cli on config change, and it's not actually needed anymore - async refreshConfig() { - this.eventRepository.serverSend(ServerEvent.CONFIG_UPDATE, null); - await this.core.refreshConfig(); - return true; - } - getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto { const options = new SystemConfigTemplateStorageOptionDto(); @@ -137,7 +131,7 @@ export class SystemConfigService { const envLevel = this.getEnvLogLevel(); const configLevel = logging.enabled ? logging.level : false; const level = envLevel ?? configLevel; - ImmichLogger.setLogLevel(level); + this.logger.setLogLevel(level); this.logger.log(`LogLevel=${level} ${envLevel ? '(set via LOG_LEVEL)' : '(set via system config)'}`); } diff --git a/server/src/services/tag.service.spec.ts b/server/src/services/tag.service.spec.ts index 2d684616a3..4323c061e1 100644 --- a/server/src/services/tag.service.spec.ts +++ b/server/src/services/tag.service.spec.ts @@ -1,5 +1,4 @@ import { BadRequestException } from '@nestjs/common'; -import { when } from 'jest-when'; import { AssetIdErrorReason } from 'src/dtos/asset-ids.response.dto'; import { TagType } from 'src/entities/tag.entity'; import { ITagRepository } from 'src/interfaces/tag.interface'; @@ -8,10 +7,11 @@ import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { tagResponseStub, tagStub } from 'test/fixtures/tag.stub'; import { newTagRepositoryMock } from 'test/repositories/tag.repository.mock'; +import { Mocked } from 'vitest'; describe(TagService.name, () => { let sut: TagService; - let tagMock: jest.Mocked; + let tagMock: Mocked; beforeEach(() => { tagMock = newTagRepositoryMock(); @@ -129,9 +129,7 @@ describe(TagService.name, () => { it('should reject duplicate asset ids and accept new ones', async () => { tagMock.getById.mockResolvedValue(tagStub.tag1); - - when(tagMock.hasAsset).calledWith(authStub.admin.user.id, 'tag-1', 'asset-1').mockResolvedValue(true); - when(tagMock.hasAsset).calledWith(authStub.admin.user.id, 'tag-1', 'asset-2').mockResolvedValue(false); + tagMock.hasAsset.mockImplementation((userId, tagId, assetId) => Promise.resolve(assetId === 'asset-1')); await expect( sut.addAssets(authStub.admin, 'tag-1', { @@ -160,9 +158,7 @@ describe(TagService.name, () => { it('should accept accept ids that are tagged and reject the rest', async () => { tagMock.getById.mockResolvedValue(tagStub.tag1); - - when(tagMock.hasAsset).calledWith(authStub.admin.user.id, 'tag-1', 'asset-1').mockResolvedValue(true); - when(tagMock.hasAsset).calledWith(authStub.admin.user.id, 'tag-1', 'asset-2').mockResolvedValue(false); + tagMock.hasAsset.mockImplementation((userId, tagId, assetId) => Promise.resolve(assetId === 'asset-1')); await expect( sut.removeAssets(authStub.admin, 'tag-1', { diff --git a/server/src/services/timeline.service.spec.ts b/server/src/services/timeline.service.spec.ts index c6f058022f..981fc11c3f 100644 --- a/server/src/services/timeline.service.spec.ts +++ b/server/src/services/timeline.service.spec.ts @@ -7,12 +7,13 @@ import { authStub } from 'test/fixtures/auth.stub'; import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositories/access.repository.mock'; import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock'; +import { Mocked } from 'vitest'; describe(TimelineService.name, () => { let sut: TimelineService; let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let partnerMock: jest.Mocked; + let assetMock: Mocked; + let partnerMock: Mocked; beforeEach(() => { accessMock = newAccessRepositoryMock(); assetMock = newAssetRepositoryMock(); diff --git a/server/src/services/trash.service.spec.ts b/server/src/services/trash.service.spec.ts index ecdf577ed3..7974efed62 100644 --- a/server/src/services/trash.service.spec.ts +++ b/server/src/services/trash.service.spec.ts @@ -9,13 +9,14 @@ import { IAccessRepositoryMock, newAccessRepositoryMock } from 'test/repositorie import { newAssetRepositoryMock } from 'test/repositories/asset.repository.mock'; import { newEventRepositoryMock } from 'test/repositories/event.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; +import { Mocked } from 'vitest'; describe(TrashService.name, () => { let sut: TrashService; let accessMock: IAccessRepositoryMock; - let assetMock: jest.Mocked; - let jobMock: jest.Mocked; - let eventMock: jest.Mocked; + let assetMock: Mocked; + let jobMock: Mocked; + let eventMock: Mocked; it('should work', () => { expect(sut).toBeDefined(); diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index 973f644d3c..1bf4fc1012 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -4,13 +4,13 @@ import { InternalServerErrorException, NotFoundException, } from '@nestjs/common'; -import { when } from 'jest-when'; import { UpdateUserDto, mapUser } from 'src/dtos/user.dto'; import { UserEntity, UserStatus } from 'src/entities/user.entity'; import { IAlbumRepository } from 'src/interfaces/album.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IJobRepository, JobName } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; @@ -23,9 +23,11 @@ import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock' import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock'; import { newJobRepositoryMock } from 'test/repositories/job.repository.mock'; import { newLibraryRepositoryMock } from 'test/repositories/library.repository.mock'; +import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; +import { Mocked, vitest } from 'vitest'; const makeDeletedAt = (daysAgo: number) => { const deletedAt = new Date(); @@ -35,14 +37,15 @@ const makeDeletedAt = (daysAgo: number) => { describe(UserService.name, () => { let sut: UserService; - let userMock: jest.Mocked; - let cryptoRepositoryMock: jest.Mocked; + let userMock: Mocked; + let cryptoRepositoryMock: Mocked; - let albumMock: jest.Mocked; - let jobMock: jest.Mocked; - let libraryMock: jest.Mocked; - let storageMock: jest.Mocked; - let configMock: jest.Mocked; + let albumMock: Mocked; + let jobMock: Mocked; + let libraryMock: Mocked; + let storageMock: Mocked; + let configMock: Mocked; + let loggerMock: Mocked; beforeEach(() => { albumMock = newAlbumRepositoryMock(); @@ -52,13 +55,22 @@ describe(UserService.name, () => { libraryMock = newLibraryRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); + loggerMock = newLoggerRepositoryMock(); - sut = new UserService(albumMock, cryptoRepositoryMock, jobMock, libraryMock, storageMock, configMock, userMock); + sut = new UserService( + albumMock, + cryptoRepositoryMock, + jobMock, + libraryMock, + storageMock, + configMock, + userMock, + loggerMock, + ); - when(userMock.get).calledWith(authStub.admin.user.id, {}).mockResolvedValue(userStub.admin); - when(userMock.get).calledWith(authStub.admin.user.id, { withDeleted: true }).mockResolvedValue(userStub.admin); - when(userMock.get).calledWith(authStub.user1.user.id, {}).mockResolvedValue(userStub.user1); - when(userMock.get).calledWith(authStub.user1.user.id, { withDeleted: true }).mockResolvedValue(userStub.user1); + userMock.get.mockImplementation((userId) => + Promise.resolve([userStub.admin, userStub.user1].find((user) => user.id === userId) ?? null), + ); }); describe('getAll', () => { @@ -136,12 +148,10 @@ describe(UserService.name, () => { }); it('user can only update its information', async () => { - when(userMock.get) - .calledWith('not_immich_auth_user_id', {}) - .mockResolvedValueOnce({ - ...userStub.user1, - id: 'not_immich_auth_user_id', - }); + userMock.get.mockResolvedValueOnce({ + ...userStub.user1, + id: 'not_immich_auth_user_id', + }); const result = sut.update( { user: userStub.user1 }, @@ -195,7 +205,7 @@ describe(UserService.name, () => { shouldChangePassword: true, }; - when(userMock.update).calledWith(userStub.user1.id, update).mockResolvedValueOnce(userStub.user1); + userMock.update.mockResolvedValueOnce(userStub.user1); await sut.update(authStub.admin, update); expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, { id: 'user-id', @@ -204,7 +214,7 @@ describe(UserService.name, () => { }); it('update user information should throw error if user not found', async () => { - when(userMock.get).calledWith(userStub.user1.id, {}).mockResolvedValueOnce(null); + userMock.get.mockResolvedValueOnce(null); const result = sut.update(authStub.admin, { id: userStub.user1.id, @@ -217,7 +227,7 @@ describe(UserService.name, () => { it('should let the admin update himself', async () => { const dto = { id: userStub.admin.id, shouldChangePassword: true, isAdmin: true }; - when(userMock.update).calledWith(userStub.admin.id, dto).mockResolvedValueOnce(userStub.admin); + userMock.update.mockResolvedValueOnce(userStub.admin); await sut.update(authStub.admin, dto); @@ -227,7 +237,7 @@ describe(UserService.name, () => { it('should not let the another user become an admin', async () => { const dto = { id: userStub.user1.id, shouldChangePassword: true, isAdmin: true }; - when(userMock.get).calledWith(userStub.user1.id, {}).mockResolvedValueOnce(userStub.user1); + userMock.get.mockResolvedValueOnce(userStub.user1); await expect(sut.update(authStub.admin, dto)).rejects.toBeInstanceOf(BadRequestException); }); @@ -235,7 +245,7 @@ describe(UserService.name, () => { describe('restore', () => { it('should throw error if user could not be found', async () => { - when(userMock.get).calledWith(userStub.admin.id, { withDeleted: true }).mockResolvedValue(null); + userMock.get.mockResolvedValue(null); await expect(sut.restore(authStub.admin, userStub.admin.id)).rejects.toThrowError(BadRequestException); expect(userMock.update).not.toHaveBeenCalled(); }); @@ -298,7 +308,7 @@ describe(UserService.name, () => { describe('create', () => { it('should not create a user if there is no local admin account', async () => { - when(userMock.getAdmin).calledWith().mockResolvedValueOnce(null); + userMock.getAdmin.mockResolvedValueOnce(null); await expect( sut.create({ @@ -335,6 +345,7 @@ describe(UserService.name, () => { describe('createProfileImage', () => { it('should throw an error if the user does not exist', async () => { const file = { path: '/profile/path' } as Express.Multer.File; + userMock.get.mockResolvedValue(null); userMock.update.mockResolvedValue({ ...userStub.admin, profileImagePath: file.path }); await expect(sut.createProfileImage(authStub.admin, file)).rejects.toThrowError(BadRequestException); @@ -422,7 +433,7 @@ describe(UserService.name, () => { describe('resetAdminPassword', () => { it('should only work when there is an admin account', async () => { userMock.getAdmin.mockResolvedValue(null); - const ask = jest.fn().mockResolvedValue('new-password'); + const ask = vitest.fn().mockResolvedValue('new-password'); await expect(sut.resetAdminPassword(ask)).rejects.toBeInstanceOf(BadRequestException); @@ -431,7 +442,7 @@ describe(UserService.name, () => { it('should default to a random password', async () => { userMock.getAdmin.mockResolvedValue(userStub.admin); - const ask = jest.fn().mockImplementation(() => {}); + const ask = vitest.fn().mockImplementation(() => {}); const response = await sut.resetAdminPassword(ask); @@ -445,7 +456,7 @@ describe(UserService.name, () => { it('should use the supplied password', async () => { userMock.getAdmin.mockResolvedValue(userStub.admin); - const ask = jest.fn().mockResolvedValue('new-password'); + const ask = vitest.fn().mockResolvedValue('new-password'); const response = await sut.resetAdminPassword(ask); diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index a2bc8c7cfc..cb9012d641 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -11,16 +11,15 @@ import { IAlbumRepository } from 'src/interfaces/album.interface'; import { ICryptoRepository } from 'src/interfaces/crypto.interface'; import { IEntityJob, IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface'; import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { IUserRepository, UserFindOptions } from 'src/interfaces/user.interface'; import { CacheControl, ImmichFileResponse } from 'src/utils/file'; -import { ImmichLogger } from 'src/utils/logger'; @Injectable() export class UserService { private configCore: SystemConfigCore; - private logger = new ImmichLogger(UserService.name); private userCore: UserCore; constructor( @@ -31,9 +30,11 @@ export class UserService { @Inject(IStorageRepository) private storageRepository: IStorageRepository, @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IUserRepository) private userRepository: IUserRepository, + @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { this.userCore = UserCore.create(cryptoRepository, libraryRepository, userRepository); - this.configCore = SystemConfigCore.create(configRepository); + this.logger.setContext(UserService.name); + this.configCore = SystemConfigCore.create(configRepository, this.logger); } async getAll(auth: AuthDto, isAll: boolean): Promise { diff --git a/server/src/utils/logger.ts b/server/src/utils/logger.ts index fef13a8fbb..05e8feb498 100644 --- a/server/src/utils/logger.ts +++ b/server/src/utils/logger.ts @@ -4,6 +4,7 @@ import { LogLevel } from 'src/entities/system-config.entity'; const LOG_LEVELS = [LogLevel.VERBOSE, LogLevel.DEBUG, LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; +// TODO move implementation to logger.repository.ts export class ImmichLogger extends ConsoleLogger { private static logLevels: LogLevel[] = [LogLevel.LOG, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL]; diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 5f1218766a..d0a6d4d740 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -37,9 +37,12 @@ class BaseConfig implements VideoCodecSWConfig { } getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { + const videoCodec = [TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target) ? this.getVideoCodec() : 'copy'; + const audioCodec = [TranscodeTarget.ALL, TranscodeTarget.AUDIO].includes(target) ? this.getAudioCodec() : 'copy'; + const options = [ - `-c:v ${[TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target) ? this.getVideoCodec() : 'copy'}`, - `-c:a ${[TranscodeTarget.ALL, TranscodeTarget.AUDIO].includes(target) ? this.getAudioCodec() : 'copy'}`, + `-c:v ${videoCodec}`, + `-c:a ${audioCodec}`, // Makes a second pass moving the moov atom to the // beginning of the file for improved playback speed. '-movflags faststart', @@ -61,7 +64,10 @@ class BaseConfig implements VideoCodecSWConfig { options.push(`-g ${this.getGopSize()}`); } - if (this.config.targetVideoCodec === VideoCodec.HEVC) { + if ( + this.config.targetVideoCodec === VideoCodec.HEVC && + (videoCodec !== 'copy' || videoStream.codecName === 'hevc') + ) { options.push('-tag:v hvc1'); } @@ -118,7 +124,7 @@ class BaseConfig implements VideoCodecSWConfig { return false; } - return this.isBitrateConstrained() || this.config.targetVideoCodec === VideoCodec.VP9; + return this.isBitrateConstrained(); } getBitrateDistribution() { @@ -259,7 +265,7 @@ export class BaseHWConfig extends BaseConfig implements VideoCodecHWConfig { } getSupportedCodecs() { - return [VideoCodec.H264, VideoCodec.HEVC, VideoCodec.VP9]; + return [VideoCodec.H264, VideoCodec.HEVC]; } validateDevices(devices: string[]) { @@ -343,27 +349,23 @@ export class ThumbnailConfig extends BaseConfig { export class H264Config extends BaseConfig { getThreadOptions() { - if (this.config.threads <= 0) { - return []; + const options = super.getThreadOptions(); + if (this.config.threads === 1) { + options.push('-x264-params frame-threads=1:pools=none'); } - return [ - ...super.getThreadOptions(), - '-x264-params "pools=none"', - `-x264-params "frame-threads=${this.config.threads}"`, - ]; + + return options; } } export class HEVCConfig extends BaseConfig { getThreadOptions() { - if (this.config.threads <= 0) { - return []; + const options = super.getThreadOptions(); + if (this.config.threads === 1) { + options.push('-x265-params frame-threads=1:pools=none'); } - return [ - ...super.getThreadOptions(), - '-x265-params "pools=none"', - `-x265-params "frame-threads=${this.config.threads}"`, - ]; + + return options; } } @@ -392,6 +394,44 @@ export class VP9Config extends BaseConfig { getThreadOptions() { return ['-row-mt 1', ...super.getThreadOptions()]; } + + eligibleForTwoPass() { + return this.config.twoPass; + } +} + +export class AV1Config extends BaseConfig { + getPresetOptions() { + const speed = this.getPresetIndex() + 4; // Use 4 as slowest, giving us an effective range of 4-12 which is far more useful than 0-8 + if (speed >= 0) { + return [`-preset ${speed}`]; + } + return []; + } + + getBitrateOptions() { + const options = [`-crf ${this.config.crf}`]; + const bitrates = this.getBitrateDistribution(); + const svtparams = []; + if (this.config.threads > 0) { + svtparams.push(`lp=${this.config.threads}`); + } + if (bitrates.max > 0) { + svtparams.push(`mbr=${bitrates.max}${bitrates.unit}`); + } + if (svtparams.length > 0) { + options.push(`-svtav1-params ${svtparams.join(':')}`); + } + return options; + } + + getThreadOptions() { + return []; // Already set above with svtav1-params + } + + eligibleForTwoPass() { + return this.config.twoPass; + } } export class NVENCConfig extends BaseHWConfig { @@ -525,6 +565,10 @@ export class QSVConfig extends BaseHWConfig { return options; } + getSupportedCodecs() { + return [VideoCodec.H264, VideoCodec.HEVC, VideoCodec.VP9]; + } + // recommended from https://github.com/intel/media-delivery/blob/master/doc/benchmarks/intel-iris-xe-max-graphics/intel-iris-xe-max-graphics.md getBFrames() { if (this.config.bframes < 0) { @@ -603,6 +647,10 @@ export class VAAPIConfig extends BaseHWConfig { return options; } + getSupportedCodecs() { + return [VideoCodec.H264, VideoCodec.HEVC, VideoCodec.VP9]; + } + useCQP() { return this.config.cqMode !== CQMode.ICQ || this.config.targetVideoCodec === VideoCodec.VP9; } diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 3837c62798..c11c936a1a 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -17,12 +17,12 @@ import { IMMICH_API_KEY_NAME, serverVersion, } from 'src/constants'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { Metadata } from 'src/middleware/auth.guard'; -import { ImmichLogger } from 'src/utils/logger'; export const isConnectionAborted = (error: Error | any) => error.code === 'ECONNABORTED'; -export const handlePromiseError = (promise: Promise, logger: ImmichLogger): void => { +export const handlePromiseError = (promise: Promise, logger: ILoggerRepository): void => { promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack)); }; diff --git a/server/src/utils/sql.ts b/server/src/utils/sql.ts index 662c40fcba..36b485b88a 100644 --- a/server/src/utils/sql.ts +++ b/server/src/utils/sql.ts @@ -12,6 +12,7 @@ import { format } from 'sql-formatter'; import { databaseConfig } from 'src/database.config'; import { GENERATE_SQL_KEY, GenerateSqlQueries } from 'src/decorators'; import { entities } from 'src/entities'; +import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { repositories } from 'src/repositories'; import { AccessRepository } from 'src/repositories/access.repository'; import { AuthService } from 'src/services/auth.service'; @@ -58,6 +59,9 @@ class SqlGenerator { try { await this.setup(); for (const repository of repositories) { + if (repository.provide === ILoggerRepository) { + continue; + } await this.process(repository); } await this.write(); diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 0b2ff82a3d..7aa49866d0 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -225,7 +225,7 @@ export const assetStub = { deviceId: 'device-id', originalPath: '/data/user1/photo.jpg', previewPath: '/uploads/user-id/thumbs/path.jpg', - checksum: Buffer.from('file hash', 'utf8'), + checksum: Buffer.from('path hash', 'utf8'), type: AssetType.IMAGE, thumbnailPath: '/uploads/user-id/webp/path.ext', thumbhash: Buffer.from('blablabla', 'base64'), @@ -295,6 +295,46 @@ export const assetStub = { deletedAt: null, }), + externalOffline: Object.freeze({ + id: 'asset-id', + deviceAssetId: 'device-asset-id', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), + owner: userStub.user1, + ownerId: 'user-id', + deviceId: 'device-id', + originalPath: '/data/user1/photo.jpg', + previewPath: '/uploads/user-id/thumbs/path.jpg', + checksum: Buffer.from('path hash', 'utf8'), + type: AssetType.IMAGE, + thumbnailPath: '/uploads/user-id/webp/path.ext', + thumbhash: Buffer.from('blablabla', 'base64'), + encodedVideoPath: null, + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), + localDateTime: new Date('2023-02-23T05:06:29.716Z'), + isFavorite: true, + isArchived: false, + isReadOnly: false, + isExternal: true, + duration: null, + isVisible: true, + livePhotoVideo: null, + livePhotoVideoId: null, + isOffline: true, + libraryId: 'library-id', + library: libraryStub.externalLibrary1, + tags: [], + sharedLinks: [], + originalFileName: 'asset-id.jpg', + faces: [], + sidecarPath: null, + exifInfo: { + fileSizeInByte: 5000, + } as ExifEntity, + deletedAt: null, + }), + image1: Object.freeze({ id: 'asset-id-1', deviceAssetId: 'device-asset-id', diff --git a/server/test/fixtures/media.stub.ts b/server/test/fixtures/media.stub.ts index 5070586ac9..323a5ac5cf 100644 --- a/server/test/fixtures/media.stub.ts +++ b/server/test/fixtures/media.stub.ts @@ -173,4 +173,8 @@ export const probeStub = { bitrate: 0, }, }), + videoStreamH264: Object.freeze({ + ...probeStubDefault, + videoStreams: [{ ...probeStubDefaultVideoStream[0], codecName: 'h264' }], + }), }; diff --git a/server/test/repositories/access.repository.mock.ts b/server/test/repositories/access.repository.mock.ts index fe7de7c833..21d298599f 100644 --- a/server/test/repositories/access.repository.mock.ts +++ b/server/test/repositories/access.repository.mock.ts @@ -1,16 +1,17 @@ import { AccessCore } from 'src/cores/access.core'; import { IAccessRepository } from 'src/interfaces/access.interface'; +import { Mocked, vitest } from 'vitest'; export interface IAccessRepositoryMock { - activity: jest.Mocked; - asset: jest.Mocked; - album: jest.Mocked; - authDevice: jest.Mocked; - library: jest.Mocked; - timeline: jest.Mocked; - memory: jest.Mocked; - person: jest.Mocked; - partner: jest.Mocked; + activity: Mocked; + asset: Mocked; + album: Mocked; + authDevice: Mocked; + library: Mocked; + timeline: Mocked; + memory: Mocked; + person: Mocked; + partner: Mocked; } export const newAccessRepositoryMock = (reset = true): IAccessRepositoryMock => { @@ -20,47 +21,47 @@ export const newAccessRepositoryMock = (reset = true): IAccessRepositoryMock => return { activity: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), - checkAlbumOwnerAccess: jest.fn().mockResolvedValue(new Set()), - checkCreateAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkAlbumOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkCreateAccess: vitest.fn().mockResolvedValue(new Set()), }, asset: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), - checkAlbumAccess: jest.fn().mockResolvedValue(new Set()), - checkPartnerAccess: jest.fn().mockResolvedValue(new Set()), - checkSharedLinkAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkAlbumAccess: vitest.fn().mockResolvedValue(new Set()), + checkPartnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkSharedLinkAccess: vitest.fn().mockResolvedValue(new Set()), }, album: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), - checkSharedAlbumAccess: jest.fn().mockResolvedValue(new Set()), - checkSharedLinkAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkSharedAlbumAccess: vitest.fn().mockResolvedValue(new Set()), + checkSharedLinkAccess: vitest.fn().mockResolvedValue(new Set()), }, authDevice: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, library: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, timeline: { - checkPartnerAccess: jest.fn().mockResolvedValue(new Set()), + checkPartnerAccess: vitest.fn().mockResolvedValue(new Set()), }, memory: { - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, person: { - checkFaceOwnerAccess: jest.fn().mockResolvedValue(new Set()), - checkOwnerAccess: jest.fn().mockResolvedValue(new Set()), + checkFaceOwnerAccess: vitest.fn().mockResolvedValue(new Set()), + checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()), }, partner: { - checkUpdateAccess: jest.fn().mockResolvedValue(new Set()), + checkUpdateAccess: vitest.fn().mockResolvedValue(new Set()), }, }; }; diff --git a/server/test/repositories/activity.repository.mock.ts b/server/test/repositories/activity.repository.mock.ts index 276b57c6cb..9d29d90ab8 100644 --- a/server/test/repositories/activity.repository.mock.ts +++ b/server/test/repositories/activity.repository.mock.ts @@ -1,10 +1,11 @@ import { IActivityRepository } from 'src/interfaces/activity.interface'; +import { Mocked, vitest } from 'vitest'; -export const newActivityRepositoryMock = (): jest.Mocked => { +export const newActivityRepositoryMock = (): Mocked => { return { - search: jest.fn(), - create: jest.fn(), - delete: jest.fn(), - getStatistics: jest.fn(), + search: vitest.fn(), + create: vitest.fn(), + delete: vitest.fn(), + getStatistics: vitest.fn(), }; }; diff --git a/server/test/repositories/album.repository.mock.ts b/server/test/repositories/album.repository.mock.ts index 38db70e4b8..9b852d7393 100644 --- a/server/test/repositories/album.repository.mock.ts +++ b/server/test/repositories/album.repository.mock.ts @@ -1,27 +1,28 @@ import { IAlbumRepository } from 'src/interfaces/album.interface'; +import { Mocked, vitest } from 'vitest'; -export const newAlbumRepositoryMock = (): jest.Mocked => { +export const newAlbumRepositoryMock = (): Mocked => { return { - getById: jest.fn(), - getByIds: jest.fn(), - getByAssetId: jest.fn(), - getMetadataForIds: jest.fn(), - getInvalidThumbnail: jest.fn(), - getOwned: jest.fn(), - getShared: jest.fn(), - getNotShared: jest.fn(), - restoreAll: jest.fn(), - softDeleteAll: jest.fn(), - deleteAll: jest.fn(), - getAll: jest.fn(), - addAssetIds: jest.fn(), - removeAsset: jest.fn(), - removeAssetIds: jest.fn(), - getAssetIds: jest.fn(), - hasAsset: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - updateThumbnails: jest.fn(), + getById: vitest.fn(), + getByIds: vitest.fn(), + getByAssetId: vitest.fn(), + getMetadataForIds: vitest.fn(), + getInvalidThumbnail: vitest.fn(), + getOwned: vitest.fn(), + getShared: vitest.fn(), + getNotShared: vitest.fn(), + restoreAll: vitest.fn(), + softDeleteAll: vitest.fn(), + deleteAll: vitest.fn(), + getAll: vitest.fn(), + addAssetIds: vitest.fn(), + removeAsset: vitest.fn(), + removeAssetIds: vitest.fn(), + getAssetIds: vitest.fn(), + hasAsset: vitest.fn(), + create: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), + updateThumbnails: vitest.fn(), }; }; diff --git a/server/test/repositories/api-key.repository.mock.ts b/server/test/repositories/api-key.repository.mock.ts index 32b8388a36..a7cfb6369a 100644 --- a/server/test/repositories/api-key.repository.mock.ts +++ b/server/test/repositories/api-key.repository.mock.ts @@ -1,12 +1,13 @@ import { IKeyRepository } from 'src/interfaces/api-key.interface'; +import { Mocked, vitest } from 'vitest'; -export const newKeyRepositoryMock = (): jest.Mocked => { +export const newKeyRepositoryMock = (): Mocked => { return { - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - getKey: jest.fn(), - getById: jest.fn(), - getByUserId: jest.fn(), + create: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), + getKey: vitest.fn(), + getById: vitest.fn(), + getByUserId: vitest.fn(), }; }; diff --git a/server/test/repositories/asset-stack.repository.mock.ts b/server/test/repositories/asset-stack.repository.mock.ts index 76ada96cdc..6106f8c997 100644 --- a/server/test/repositories/asset-stack.repository.mock.ts +++ b/server/test/repositories/asset-stack.repository.mock.ts @@ -1,10 +1,11 @@ import { IAssetStackRepository } from 'src/interfaces/asset-stack.interface'; +import { Mocked, vitest } from 'vitest'; -export const newAssetStackRepositoryMock = (): jest.Mocked => { +export const newAssetStackRepositoryMock = (): Mocked => { return { - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - getById: jest.fn(), + create: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), + getById: vitest.fn(), }; }; diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index 67770cd93b..694fc87cc2 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -1,39 +1,42 @@ import { IAssetRepository } from 'src/interfaces/asset.interface'; +import { Mocked, vitest } from 'vitest'; -export const newAssetRepositoryMock = (): jest.Mocked => { +export const newAssetRepositoryMock = (): Mocked => { return { - create: jest.fn(), - upsertExif: jest.fn(), - upsertJobStatus: jest.fn(), - getByDayOfYear: jest.fn(), - getByIds: jest.fn().mockResolvedValue([]), - getByIdsWithAllRelations: jest.fn().mockResolvedValue([]), - getByAlbumId: jest.fn(), - getByUserId: jest.fn(), - getById: jest.fn(), - getWithout: jest.fn(), - getByChecksum: jest.fn(), - getWith: jest.fn(), - getRandom: jest.fn(), - getFirstAssetForAlbumId: jest.fn(), - getLastUpdatedAssetForAlbumId: jest.fn(), - getAll: jest.fn().mockResolvedValue({ items: [], hasNextPage: false }), - getAllByDeviceId: jest.fn(), - updateAll: jest.fn(), - getLibraryAssetPaths: jest.fn(), - getByLibraryIdAndOriginalPath: jest.fn(), - deleteAll: jest.fn(), - update: jest.fn(), - remove: jest.fn(), - findLivePhotoMatch: jest.fn(), - getMapMarkers: jest.fn(), - getStatistics: jest.fn(), - getTimeBucket: jest.fn(), - getTimeBuckets: jest.fn(), - restoreAll: jest.fn(), - softDeleteAll: jest.fn(), - getAssetIdByCity: jest.fn(), - getAssetIdByTag: jest.fn(), - searchMetadata: jest.fn(), + create: vitest.fn(), + upsertExif: vitest.fn(), + upsertJobStatus: vitest.fn(), + getByDayOfYear: vitest.fn(), + getByIds: vitest.fn().mockResolvedValue([]), + getByIdsWithAllRelations: vitest.fn().mockResolvedValue([]), + getByAlbumId: vitest.fn(), + getByUserId: vitest.fn(), + getById: vitest.fn(), + getWithout: vitest.fn(), + getByChecksum: vitest.fn(), + getWith: vitest.fn(), + getRandom: vitest.fn(), + getFirstAssetForAlbumId: vitest.fn(), + getLastUpdatedAssetForAlbumId: vitest.fn(), + getAll: vitest.fn().mockResolvedValue({ items: [], hasNextPage: false }), + getAllByDeviceId: vitest.fn(), + updateAll: vitest.fn(), + getExternalLibraryAssetPaths: vitest.fn(), + getByLibraryIdAndOriginalPath: vitest.fn(), + deleteAll: vitest.fn(), + update: vitest.fn(), + remove: vitest.fn(), + findLivePhotoMatch: vitest.fn(), + getMapMarkers: vitest.fn(), + getStatistics: vitest.fn(), + getTimeBucket: vitest.fn(), + getTimeBuckets: vitest.fn(), + restoreAll: vitest.fn(), + softDeleteAll: vitest.fn(), + getAssetIdByCity: vitest.fn(), + getAssetIdByTag: vitest.fn(), + searchMetadata: vitest.fn(), + getAllForUserFullSync: vitest.fn(), + getChangedDeltaSync: vitest.fn(), }; }; diff --git a/server/test/repositories/audit.repository.mock.ts b/server/test/repositories/audit.repository.mock.ts index 9e4adf5608..13af834ce9 100644 --- a/server/test/repositories/audit.repository.mock.ts +++ b/server/test/repositories/audit.repository.mock.ts @@ -1,8 +1,9 @@ import { IAuditRepository } from 'src/interfaces/audit.interface'; +import { Mocked, vitest } from 'vitest'; -export const newAuditRepositoryMock = (): jest.Mocked => { +export const newAuditRepositoryMock = (): Mocked => { return { - getAfter: jest.fn(), - removeBefore: jest.fn(), + getAfter: vitest.fn(), + removeBefore: vitest.fn(), }; }; diff --git a/server/test/repositories/crypto.repository.mock.ts b/server/test/repositories/crypto.repository.mock.ts index 8d13814db9..98641be8bc 100644 --- a/server/test/repositories/crypto.repository.mock.ts +++ b/server/test/repositories/crypto.repository.mock.ts @@ -1,14 +1,15 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface'; +import { Mocked, vitest } from 'vitest'; -export const newCryptoRepositoryMock = (): jest.Mocked => { +export const newCryptoRepositoryMock = (): Mocked => { return { - randomUUID: jest.fn().mockReturnValue('random-uuid'), - randomBytes: jest.fn().mockReturnValue(Buffer.from('random-bytes', 'utf8')), - compareBcrypt: jest.fn().mockReturnValue(true), - hashBcrypt: jest.fn().mockImplementation((input) => Promise.resolve(`${input} (hashed)`)), - hashSha256: jest.fn().mockImplementation((input) => `${input} (hashed)`), - hashSha1: jest.fn().mockImplementation((input) => Buffer.from(`${input.toString()} (hashed)`)), - hashFile: jest.fn().mockImplementation((input) => `${input} (file-hashed)`), - newPassword: jest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')), + randomUUID: vitest.fn().mockReturnValue('random-uuid'), + randomBytes: vitest.fn().mockReturnValue(Buffer.from('random-bytes', 'utf8')), + compareBcrypt: vitest.fn().mockReturnValue(true), + hashBcrypt: vitest.fn().mockImplementation((input) => Promise.resolve(`${input} (hashed)`)), + hashSha256: vitest.fn().mockImplementation((input) => `${input} (hashed)`), + hashSha1: vitest.fn().mockImplementation((input) => Buffer.from(`${input.toString()} (hashed)`)), + hashFile: vitest.fn().mockImplementation((input) => `${input} (file-hashed)`), + newPassword: vitest.fn().mockReturnValue(Buffer.from('random-bytes').toString('base64')), }; }; diff --git a/server/test/repositories/database.repository.mock.ts b/server/test/repositories/database.repository.mock.ts index 704189571a..b4109a8375 100644 --- a/server/test/repositories/database.repository.mock.ts +++ b/server/test/repositories/database.repository.mock.ts @@ -1,21 +1,22 @@ import { IDatabaseRepository } from 'src/interfaces/database.interface'; import { Version } from 'src/utils/version'; +import { Mocked, vitest } from 'vitest'; -export const newDatabaseRepositoryMock = (): jest.Mocked => { +export const newDatabaseRepositoryMock = (): Mocked => { return { - getExtensionVersion: jest.fn(), - getAvailableExtensionVersion: jest.fn(), - getPreferredVectorExtension: jest.fn(), - getPostgresVersion: jest.fn().mockResolvedValue(new Version(14, 0, 0)), - createExtension: jest.fn().mockImplementation(() => Promise.resolve()), - updateExtension: jest.fn(), - updateVectorExtension: jest.fn(), - reindex: jest.fn(), - shouldReindex: jest.fn(), - runMigrations: jest.fn(), - withLock: jest.fn().mockImplementation((_, function_: () => Promise) => function_()), - tryLock: jest.fn(), - isBusy: jest.fn(), - wait: jest.fn(), + getExtensionVersion: vitest.fn(), + getAvailableExtensionVersion: vitest.fn(), + getPreferredVectorExtension: vitest.fn(), + getPostgresVersion: vitest.fn().mockResolvedValue(new Version(14, 0, 0)), + createExtension: vitest.fn().mockImplementation(() => Promise.resolve()), + updateExtension: vitest.fn(), + updateVectorExtension: vitest.fn(), + reindex: vitest.fn(), + shouldReindex: vitest.fn(), + runMigrations: vitest.fn(), + withLock: vitest.fn().mockImplementation((_, function_: () => Promise) => function_()), + tryLock: vitest.fn(), + isBusy: vitest.fn(), + wait: vitest.fn(), }; }; diff --git a/server/test/repositories/event.repository.mock.ts b/server/test/repositories/event.repository.mock.ts index b21d4a59ec..2277fec83b 100644 --- a/server/test/repositories/event.repository.mock.ts +++ b/server/test/repositories/event.repository.mock.ts @@ -1,10 +1,11 @@ import { IEventRepository } from 'src/interfaces/event.interface'; +import { Mocked, vitest } from 'vitest'; -export const newEventRepositoryMock = (): jest.Mocked => { +export const newEventRepositoryMock = (): Mocked => { return { - clientSend: jest.fn(), - clientBroadcast: jest.fn(), - serverSend: jest.fn(), - serverSendAsync: jest.fn(), + clientSend: vitest.fn(), + clientBroadcast: vitest.fn(), + serverSend: vitest.fn(), + serverSendAsync: vitest.fn(), }; }; diff --git a/server/test/repositories/job.repository.mock.ts b/server/test/repositories/job.repository.mock.ts index 9cd21fe874..6bffe184fd 100644 --- a/server/test/repositories/job.repository.mock.ts +++ b/server/test/repositories/job.repository.mock.ts @@ -1,20 +1,21 @@ import { IJobRepository } from 'src/interfaces/job.interface'; +import { Mocked, vitest } from 'vitest'; -export const newJobRepositoryMock = (): jest.Mocked => { +export const newJobRepositoryMock = (): Mocked => { return { - addHandler: jest.fn(), - addCronJob: jest.fn(), - deleteCronJob: jest.fn(), - updateCronJob: jest.fn(), - setConcurrency: jest.fn(), - empty: jest.fn(), - pause: jest.fn(), - resume: jest.fn(), - queue: jest.fn().mockImplementation(() => Promise.resolve()), - queueAll: jest.fn().mockImplementation(() => Promise.resolve()), - getQueueStatus: jest.fn(), - getJobCounts: jest.fn(), - clear: jest.fn(), - waitForQueueCompletion: jest.fn(), + addHandler: vitest.fn(), + addCronJob: vitest.fn(), + deleteCronJob: vitest.fn(), + updateCronJob: vitest.fn(), + setConcurrency: vitest.fn(), + empty: vitest.fn(), + pause: vitest.fn(), + resume: vitest.fn(), + queue: vitest.fn().mockImplementation(() => Promise.resolve()), + queueAll: vitest.fn().mockImplementation(() => Promise.resolve()), + getQueueStatus: vitest.fn(), + getJobCounts: vitest.fn(), + clear: vitest.fn(), + waitForQueueCompletion: vitest.fn(), }; }; diff --git a/server/test/repositories/library.repository.mock.ts b/server/test/repositories/library.repository.mock.ts index 6cdfb38f41..4280619862 100644 --- a/server/test/repositories/library.repository.mock.ts +++ b/server/test/repositories/library.repository.mock.ts @@ -1,18 +1,19 @@ import { ILibraryRepository } from 'src/interfaces/library.interface'; +import { Mocked, vitest } from 'vitest'; -export const newLibraryRepositoryMock = (): jest.Mocked => { +export const newLibraryRepositoryMock = (): Mocked => { return { - get: jest.fn(), - getCountForUser: jest.fn(), - create: jest.fn(), - delete: jest.fn(), - softDelete: jest.fn(), - update: jest.fn(), - getStatistics: jest.fn(), - getDefaultUploadLibrary: jest.fn(), - getUploadLibraryCount: jest.fn(), - getAssetIds: jest.fn(), - getAllDeleted: jest.fn(), - getAll: jest.fn(), + get: vitest.fn(), + getCountForUser: vitest.fn(), + create: vitest.fn(), + delete: vitest.fn(), + softDelete: vitest.fn(), + update: vitest.fn(), + getStatistics: vitest.fn(), + getDefaultUploadLibrary: vitest.fn(), + getUploadLibraryCount: vitest.fn(), + getAssetIds: vitest.fn(), + getAllDeleted: vitest.fn(), + getAll: vitest.fn(), }; }; diff --git a/server/test/repositories/logger.repository.mock.ts b/server/test/repositories/logger.repository.mock.ts new file mode 100644 index 0000000000..a8537bb2fa --- /dev/null +++ b/server/test/repositories/logger.repository.mock.ts @@ -0,0 +1,16 @@ +import { ILoggerRepository } from 'src/interfaces/logger.interface'; +import { Mocked, vitest } from 'vitest'; + +export const newLoggerRepositoryMock = (): Mocked => { + return { + setLogLevel: vitest.fn(), + setContext: vitest.fn(), + + verbose: vitest.fn(), + debug: vitest.fn(), + log: vitest.fn(), + warn: vitest.fn(), + error: vitest.fn(), + fatal: vitest.fn(), + }; +}; diff --git a/server/test/repositories/machine-learning.repository.mock.ts b/server/test/repositories/machine-learning.repository.mock.ts index bc35b4c855..9dd1bdca29 100644 --- a/server/test/repositories/machine-learning.repository.mock.ts +++ b/server/test/repositories/machine-learning.repository.mock.ts @@ -1,9 +1,10 @@ import { IMachineLearningRepository } from 'src/interfaces/machine-learning.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMachineLearningRepositoryMock = (): jest.Mocked => { +export const newMachineLearningRepositoryMock = (): Mocked => { return { - encodeImage: jest.fn(), - encodeText: jest.fn(), - detectFaces: jest.fn(), + encodeImage: vitest.fn(), + encodeText: vitest.fn(), + detectFaces: vitest.fn(), }; }; diff --git a/server/test/repositories/media.repository.mock.ts b/server/test/repositories/media.repository.mock.ts index b904766ea9..2eea47b6ac 100644 --- a/server/test/repositories/media.repository.mock.ts +++ b/server/test/repositories/media.repository.mock.ts @@ -1,11 +1,12 @@ import { IMediaRepository } from 'src/interfaces/media.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMediaRepositoryMock = (): jest.Mocked => { +export const newMediaRepositoryMock = (): Mocked => { return { - generateThumbhash: jest.fn(), - resize: jest.fn(), - crop: jest.fn(), - probe: jest.fn(), - transcode: jest.fn(), + generateThumbhash: vitest.fn(), + resize: vitest.fn(), + crop: vitest.fn(), + probe: vitest.fn(), + transcode: vitest.fn(), }; }; diff --git a/server/test/repositories/memory.repository.mock.ts b/server/test/repositories/memory.repository.mock.ts index 85b17a1985..fc3c968767 100644 --- a/server/test/repositories/memory.repository.mock.ts +++ b/server/test/repositories/memory.repository.mock.ts @@ -1,14 +1,15 @@ import { IMemoryRepository } from 'src/interfaces/memory.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMemoryRepositoryMock = (): jest.Mocked => { +export const newMemoryRepositoryMock = (): Mocked => { return { - search: jest.fn().mockResolvedValue([]), - get: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - getAssetIds: jest.fn().mockResolvedValue(new Set()), - addAssetIds: jest.fn(), - removeAssetIds: jest.fn(), + search: vitest.fn().mockResolvedValue([]), + get: vitest.fn(), + create: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), + getAssetIds: vitest.fn().mockResolvedValue(new Set()), + addAssetIds: vitest.fn(), + removeAssetIds: vitest.fn(), }; }; diff --git a/server/test/repositories/metadata.repository.mock.ts b/server/test/repositories/metadata.repository.mock.ts index ec21ab8c15..80d6bf121c 100644 --- a/server/test/repositories/metadata.repository.mock.ts +++ b/server/test/repositories/metadata.repository.mock.ts @@ -1,17 +1,18 @@ import { IMetadataRepository } from 'src/interfaces/metadata.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMetadataRepositoryMock = (): jest.Mocked => { +export const newMetadataRepositoryMock = (): Mocked => { return { - init: jest.fn(), - teardown: jest.fn(), - reverseGeocode: jest.fn(), - readTags: jest.fn(), - writeTags: jest.fn(), - extractBinaryTag: jest.fn(), - getCameraMakes: jest.fn(), - getCameraModels: jest.fn(), - getCities: jest.fn(), - getCountries: jest.fn(), - getStates: jest.fn(), + init: vitest.fn(), + teardown: vitest.fn(), + reverseGeocode: vitest.fn(), + readTags: vitest.fn(), + writeTags: vitest.fn(), + extractBinaryTag: vitest.fn(), + getCameraMakes: vitest.fn(), + getCameraModels: vitest.fn(), + getCities: vitest.fn(), + getCountries: vitest.fn(), + getStates: vitest.fn(), }; }; diff --git a/server/test/repositories/metric.repository.mock.ts b/server/test/repositories/metric.repository.mock.ts index 383845d345..e2c3e2aac1 100644 --- a/server/test/repositories/metric.repository.mock.ts +++ b/server/test/repositories/metric.repository.mock.ts @@ -1,30 +1,31 @@ import { IMetricRepository } from 'src/interfaces/metric.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMetricRepositoryMock = (): jest.Mocked => { +export const newMetricRepositoryMock = (): Mocked => { return { api: { - addToCounter: jest.fn(), - addToGauge: jest.fn(), - addToHistogram: jest.fn(), - configure: jest.fn(), + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), }, host: { - addToCounter: jest.fn(), - addToGauge: jest.fn(), - addToHistogram: jest.fn(), - configure: jest.fn(), + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), }, jobs: { - addToCounter: jest.fn(), - addToGauge: jest.fn(), - addToHistogram: jest.fn(), - configure: jest.fn(), + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), }, repo: { - addToCounter: jest.fn(), - addToGauge: jest.fn(), - addToHistogram: jest.fn(), - configure: jest.fn(), + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), }, }; }; diff --git a/server/test/repositories/move.repository.mock.ts b/server/test/repositories/move.repository.mock.ts index b7adec2a7b..1f982a048d 100644 --- a/server/test/repositories/move.repository.mock.ts +++ b/server/test/repositories/move.repository.mock.ts @@ -1,10 +1,11 @@ import { IMoveRepository } from 'src/interfaces/move.interface'; +import { Mocked, vitest } from 'vitest'; -export const newMoveRepositoryMock = (): jest.Mocked => { +export const newMoveRepositoryMock = (): Mocked => { return { - create: jest.fn(), - getByEntity: jest.fn(), - update: jest.fn(), - delete: jest.fn(), + create: vitest.fn(), + getByEntity: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), }; }; diff --git a/server/test/repositories/partner.repository.mock.ts b/server/test/repositories/partner.repository.mock.ts index 04370730b1..e16bb6ffdc 100644 --- a/server/test/repositories/partner.repository.mock.ts +++ b/server/test/repositories/partner.repository.mock.ts @@ -1,11 +1,12 @@ import { IPartnerRepository } from 'src/interfaces/partner.interface'; +import { Mocked, vitest } from 'vitest'; -export const newPartnerRepositoryMock = (): jest.Mocked => { +export const newPartnerRepositoryMock = (): Mocked => { return { - create: jest.fn(), - remove: jest.fn(), - getAll: jest.fn(), - get: jest.fn(), - update: jest.fn(), + create: vitest.fn(), + remove: vitest.fn(), + getAll: vitest.fn(), + get: vitest.fn(), + update: vitest.fn(), }; }; diff --git a/server/test/repositories/person.repository.mock.ts b/server/test/repositories/person.repository.mock.ts index 5b94fbc3d2..5909e3c967 100644 --- a/server/test/repositories/person.repository.mock.ts +++ b/server/test/repositories/person.repository.mock.ts @@ -1,32 +1,33 @@ import { IPersonRepository } from 'src/interfaces/person.interface'; +import { Mocked, vitest } from 'vitest'; -export const newPersonRepositoryMock = (): jest.Mocked => { +export const newPersonRepositoryMock = (): Mocked => { return { - getById: jest.fn(), - getAll: jest.fn(), - getAllForUser: jest.fn(), - getAssets: jest.fn(), - getAllWithoutFaces: jest.fn(), + getById: vitest.fn(), + getAll: vitest.fn(), + getAllForUser: vitest.fn(), + getAssets: vitest.fn(), + getAllWithoutFaces: vitest.fn(), - getByName: jest.fn(), + getByName: vitest.fn(), - create: jest.fn(), - update: jest.fn(), - deleteAll: jest.fn(), - delete: jest.fn(), - deleteAllFaces: jest.fn(), + create: vitest.fn(), + update: vitest.fn(), + deleteAll: vitest.fn(), + delete: vitest.fn(), + deleteAllFaces: vitest.fn(), - getStatistics: jest.fn(), - getAllFaces: jest.fn(), - getFacesByIds: jest.fn(), - getRandomFace: jest.fn(), + getStatistics: vitest.fn(), + getAllFaces: vitest.fn(), + getFacesByIds: vitest.fn(), + getRandomFace: vitest.fn(), - reassignFaces: jest.fn(), - createFaces: jest.fn(), - getFaces: jest.fn(), - reassignFace: jest.fn(), - getFaceById: jest.fn(), - getFaceByIdWithAssets: jest.fn(), - getNumberOfPeople: jest.fn(), + reassignFaces: vitest.fn(), + createFaces: vitest.fn(), + getFaces: vitest.fn(), + reassignFace: vitest.fn(), + getFaceById: vitest.fn(), + getFaceByIdWithAssets: vitest.fn(), + getNumberOfPeople: vitest.fn(), }; }; diff --git a/server/test/repositories/search.repository.mock.ts b/server/test/repositories/search.repository.mock.ts index 24e648ee2b..d43b2b9ce9 100644 --- a/server/test/repositories/search.repository.mock.ts +++ b/server/test/repositories/search.repository.mock.ts @@ -1,14 +1,15 @@ import { ISearchRepository } from 'src/interfaces/search.interface'; +import { Mocked, vitest } from 'vitest'; -export const newSearchRepositoryMock = (): jest.Mocked => { +export const newSearchRepositoryMock = (): Mocked => { return { - init: jest.fn(), - searchMetadata: jest.fn(), - searchSmart: jest.fn(), - searchFaces: jest.fn(), - upsert: jest.fn(), - searchPlaces: jest.fn(), - getAssetsByCity: jest.fn(), - deleteAllSearchEmbeddings: jest.fn(), + init: vitest.fn(), + searchMetadata: vitest.fn(), + searchSmart: vitest.fn(), + searchFaces: vitest.fn(), + upsert: vitest.fn(), + searchPlaces: vitest.fn(), + getAssetsByCity: vitest.fn(), + deleteAllSearchEmbeddings: vitest.fn(), }; }; diff --git a/server/test/repositories/shared-link.repository.mock.ts b/server/test/repositories/shared-link.repository.mock.ts index 2fcaf7aee4..251b38d5d7 100644 --- a/server/test/repositories/shared-link.repository.mock.ts +++ b/server/test/repositories/shared-link.repository.mock.ts @@ -1,12 +1,13 @@ import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface'; +import { Mocked, vitest } from 'vitest'; -export const newSharedLinkRepositoryMock = (): jest.Mocked => { +export const newSharedLinkRepositoryMock = (): Mocked => { return { - getAll: jest.fn(), - get: jest.fn(), - getByKey: jest.fn(), - create: jest.fn(), - remove: jest.fn(), - update: jest.fn(), + getAll: vitest.fn(), + get: vitest.fn(), + getByKey: vitest.fn(), + create: vitest.fn(), + remove: vitest.fn(), + update: vitest.fn(), }; }; diff --git a/server/test/repositories/storage.repository.mock.ts b/server/test/repositories/storage.repository.mock.ts index d5049999c7..615fd5d8c9 100644 --- a/server/test/repositories/storage.repository.mock.ts +++ b/server/test/repositories/storage.repository.mock.ts @@ -1,6 +1,7 @@ import { WatchOptions } from 'chokidar'; import { StorageCore } from 'src/cores/storage.core'; -import { IStorageRepository, StorageEventType, WatchEvents } from 'src/interfaces/storage.interface'; +import { IStorageRepository, WatchEvents } from 'src/interfaces/storage.interface'; +import { Mocked, vitest } from 'vitest'; interface MockWatcherOptions { items?: Array<{ event: 'change' | 'add' | 'unlink' | 'error'; value: string }>; @@ -13,19 +14,19 @@ export const makeMockWatcher = events.onReady?.(); for (const item of items || []) { switch (item.event) { - case StorageEventType.ADD: { + case 'add': { events.onAdd?.(item.value); break; } - case StorageEventType.CHANGE: { + case 'change': { events.onChange?.(item.value); break; } - case StorageEventType.UNLINK: { + case 'unlink': { events.onUnlink?.(item.value); break; } - case StorageEventType.ERROR: { + case 'error': { events.onError?.(new Error(item.value)); } } @@ -38,29 +39,29 @@ export const makeMockWatcher = return () => Promise.resolve(); }; -export const newStorageRepositoryMock = (reset = true): jest.Mocked => { +export const newStorageRepositoryMock = (reset = true): Mocked => { if (reset) { StorageCore.reset(); } return { - createZipStream: jest.fn(), - createReadStream: jest.fn(), - readFile: jest.fn(), - writeFile: jest.fn(), - unlink: jest.fn(), - unlinkDir: jest.fn().mockResolvedValue(true), - removeEmptyDirs: jest.fn(), - checkFileExists: jest.fn(), - mkdirSync: jest.fn(), - checkDiskUsage: jest.fn(), - readdir: jest.fn(), - stat: jest.fn(), - crawl: jest.fn(), - walk: jest.fn().mockImplementation(async function* () {}), - rename: jest.fn(), - copyFile: jest.fn(), - utimes: jest.fn(), - watch: jest.fn().mockImplementation(makeMockWatcher({})), + createZipStream: vitest.fn(), + createReadStream: vitest.fn(), + readFile: vitest.fn(), + writeFile: vitest.fn(), + unlink: vitest.fn(), + unlinkDir: vitest.fn().mockResolvedValue(true), + removeEmptyDirs: vitest.fn(), + checkFileExists: vitest.fn(), + mkdirSync: vitest.fn(), + checkDiskUsage: vitest.fn(), + readdir: vitest.fn(), + stat: vitest.fn(), + crawl: vitest.fn(), + walk: vitest.fn().mockImplementation(async function* () {}), + rename: vitest.fn(), + copyFile: vitest.fn(), + utimes: vitest.fn(), + watch: vitest.fn().mockImplementation(makeMockWatcher({})), }; }; diff --git a/server/test/repositories/system-config.repository.mock.ts b/server/test/repositories/system-config.repository.mock.ts index 0ef11ce18f..41135b7d74 100644 --- a/server/test/repositories/system-config.repository.mock.ts +++ b/server/test/repositories/system-config.repository.mock.ts @@ -1,16 +1,17 @@ import { SystemConfigCore } from 'src/cores/system-config.core'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; +import { Mocked, vitest } from 'vitest'; -export const newSystemConfigRepositoryMock = (reset = true): jest.Mocked => { +export const newSystemConfigRepositoryMock = (reset = true): Mocked => { if (reset) { SystemConfigCore.reset(); } return { - fetchStyle: jest.fn(), - load: jest.fn().mockResolvedValue([]), - readFile: jest.fn(), - saveAll: jest.fn().mockResolvedValue([]), - deleteKeys: jest.fn(), + fetchStyle: vitest.fn(), + load: vitest.fn().mockResolvedValue([]), + readFile: vitest.fn(), + saveAll: vitest.fn().mockResolvedValue([]), + deleteKeys: vitest.fn(), }; }; diff --git a/server/test/repositories/system-info.repository.mock.ts b/server/test/repositories/system-info.repository.mock.ts index bdc11f9d63..977d5dca2d 100644 --- a/server/test/repositories/system-info.repository.mock.ts +++ b/server/test/repositories/system-info.repository.mock.ts @@ -1,7 +1,8 @@ import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; +import { Mocked, vitest } from 'vitest'; -export const newServerInfoRepositoryMock = (): jest.Mocked => { +export const newServerInfoRepositoryMock = (): Mocked => { return { - getGitHubRelease: jest.fn(), + getGitHubRelease: vitest.fn(), }; }; diff --git a/server/test/repositories/system-metadata.repository.mock.ts b/server/test/repositories/system-metadata.repository.mock.ts index 5ffc5dd895..1044076ea8 100644 --- a/server/test/repositories/system-metadata.repository.mock.ts +++ b/server/test/repositories/system-metadata.repository.mock.ts @@ -1,8 +1,9 @@ import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; +import { Mocked, vitest } from 'vitest'; -export const newSystemMetadataRepositoryMock = (): jest.Mocked => { +export const newSystemMetadataRepositoryMock = (): Mocked => { return { - get: jest.fn(), - set: jest.fn(), + get: vitest.fn() as any, + set: vitest.fn(), }; }; diff --git a/server/test/repositories/tag.repository.mock.ts b/server/test/repositories/tag.repository.mock.ts index 0c31c546c2..a5123e0f36 100644 --- a/server/test/repositories/tag.repository.mock.ts +++ b/server/test/repositories/tag.repository.mock.ts @@ -1,16 +1,17 @@ import { ITagRepository } from 'src/interfaces/tag.interface'; +import { Mocked, vitest } from 'vitest'; -export const newTagRepositoryMock = (): jest.Mocked => { +export const newTagRepositoryMock = (): Mocked => { return { - getAll: jest.fn(), - getById: jest.fn(), - create: jest.fn(), - update: jest.fn(), - remove: jest.fn(), - hasAsset: jest.fn(), - hasName: jest.fn(), - getAssets: jest.fn(), - addAssets: jest.fn(), - removeAssets: jest.fn(), + getAll: vitest.fn(), + getById: vitest.fn(), + create: vitest.fn(), + update: vitest.fn(), + remove: vitest.fn(), + hasAsset: vitest.fn(), + hasName: vitest.fn(), + getAssets: vitest.fn(), + addAssets: vitest.fn(), + removeAssets: vitest.fn(), }; }; diff --git a/server/test/repositories/user-token.repository.mock.ts b/server/test/repositories/user-token.repository.mock.ts index b3fa7e73f1..f34e65b7f3 100644 --- a/server/test/repositories/user-token.repository.mock.ts +++ b/server/test/repositories/user-token.repository.mock.ts @@ -1,11 +1,12 @@ import { IUserTokenRepository } from 'src/interfaces/user-token.interface'; +import { Mocked, vitest } from 'vitest'; -export const newUserTokenRepositoryMock = (): jest.Mocked => { +export const newUserTokenRepositoryMock = (): Mocked => { return { - create: jest.fn(), - save: jest.fn(), - delete: jest.fn(), - getByToken: jest.fn(), - getAll: jest.fn(), + create: vitest.fn(), + save: vitest.fn(), + delete: vitest.fn(), + getByToken: vitest.fn(), + getAll: vitest.fn(), }; }; diff --git a/server/test/repositories/user.repository.mock.ts b/server/test/repositories/user.repository.mock.ts index 80d9a4cfd0..5f2e2f083e 100644 --- a/server/test/repositories/user.repository.mock.ts +++ b/server/test/repositories/user.repository.mock.ts @@ -1,25 +1,26 @@ import { UserCore } from 'src/cores/user.core'; import { IUserRepository } from 'src/interfaces/user.interface'; +import { Mocked, vitest } from 'vitest'; -export const newUserRepositoryMock = (reset = true): jest.Mocked => { +export const newUserRepositoryMock = (reset = true): Mocked => { if (reset) { UserCore.reset(); } return { - get: jest.fn(), - getAdmin: jest.fn(), - getByEmail: jest.fn(), - getByStorageLabel: jest.fn(), - getByOAuthId: jest.fn(), - getUserStats: jest.fn(), - getList: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - getDeletedUsers: jest.fn(), - hasAdmin: jest.fn(), - updateUsage: jest.fn(), - syncUsage: jest.fn(), + get: vitest.fn(), + getAdmin: vitest.fn(), + getByEmail: vitest.fn(), + getByStorageLabel: vitest.fn(), + getByOAuthId: vitest.fn(), + getUserStats: vitest.fn(), + getList: vitest.fn(), + create: vitest.fn(), + update: vitest.fn(), + delete: vitest.fn(), + getDeletedUsers: vitest.fn(), + hasAdmin: vitest.fn(), + updateUsage: vitest.fn(), + syncUsage: vitest.fn(), }; }; diff --git a/server/test/utils.ts b/server/test/utils.ts deleted file mode 100644 index c7732eabc1..0000000000 --- a/server/test/utils.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { INestApplication } from '@nestjs/common'; -import { Test } from '@nestjs/testing'; -import { DateTime } from 'luxon'; -import fs from 'node:fs'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { EventEmitter } from 'node:stream'; -import { AppTestModule } from 'src/app.module'; -import { dataSource } from 'src/database.config'; -import { IJobRepository, JobItem, JobItemHandler, QueueName } from 'src/interfaces/job.interface'; -import { IMediaRepository } from 'src/interfaces/media.interface'; -import { StorageEventType } from 'src/interfaces/storage.interface'; -import { MediaRepository } from 'src/repositories/media.repository'; -import { ApiService } from 'src/services/api.service'; -import { MicroservicesService } from 'src/services/microservices.service'; -import { EntityTarget, ObjectLiteral } from 'typeorm'; - -export const IMMICH_TEST_ASSET_PATH = process.env.IMMICH_TEST_ASSET_PATH as string; -export const IMMICH_TEST_ASSET_TEMP_PATH = join(tmpdir(), 'immich'); - -export const today = DateTime.fromObject({ year: 2023, month: 11, day: 3 }); -export const yesterday = today.minus({ days: 1 }); - -export interface ResetOptions { - entities?: EntityTarget[]; -} -export const db = { - reset: async (options?: ResetOptions) => { - if (!dataSource.isInitialized) { - await dataSource.initialize(); - } - await dataSource.transaction(async (em) => { - const entities = options?.entities || []; - const tableNames = - entities.length > 0 - ? entities.map((entity) => em.getRepository(entity).metadata.tableName) - : dataSource.entityMetadatas - .map((entity) => entity.tableName) - .filter((tableName) => !tableName.startsWith('geodata')); - - let deleteUsers = false; - for (const tableName of tableNames) { - if (tableName === 'users') { - deleteUsers = true; - continue; - } - await em.query(`DELETE FROM ${tableName} CASCADE;`); - } - if (deleteUsers) { - await em.query(`DELETE FROM "users" CASCADE;`); - } - - // Release all locks - await em.query('SELECT pg_advisory_unlock_all()'); - }); - }, - disconnect: async () => { - if (dataSource.isInitialized) { - await dataSource.destroy(); - } - }, -}; - -class JobMock implements IJobRepository { - private _handler: JobItemHandler = () => Promise.resolve(); - addHandler(_queueName: QueueName, _concurrency: number, handler: JobItemHandler) { - this._handler = handler; - } - addCronJob() {} - updateCronJob() {} - deleteCronJob() {} - validateCronExpression() {} - queue(item: JobItem) { - return this._handler(item); - } - queueAll(items: JobItem[]) { - return Promise.all(items.map((arg) => this._handler(arg))).then(() => {}); - } - async resume() {} - async empty() {} - async setConcurrency() {} - getQueueStatus() { - return Promise.resolve(null) as any; - } - getJobCounts() { - return Promise.resolve(null) as any; - } - async pause() {} - clear() { - return Promise.resolve([]); - } - async waitForQueueCompletion() {} -} - -class MediaMockRepository extends MediaRepository { - generateThumbhash() { - return Promise.resolve(Buffer.from('mock-thumbhash')); - } -} - -let app: INestApplication; - -export const testApp = { - create: async (): Promise => { - const moduleFixture = await Test.createTestingModule({ imports: [AppTestModule] }) - .overrideProvider(IJobRepository) - .useClass(JobMock) - .overrideProvider(IMediaRepository) - .useClass(MediaMockRepository) - .compile(); - - app = await moduleFixture.createNestApplication().init(); - await app.get(ApiService).init(); - await db.reset(); - await app.get(ApiService).init(); - await app.get(MicroservicesService).init(); - - return app; - }, - reset: async (options?: ResetOptions) => { - await db.reset(options); - }, - get: (member: any) => app.get(member), - teardown: async () => { - if (app) { - await app.get(MicroservicesService).teardown(); - await app.close(); - } - await db.disconnect(); - }, -}; - -export function waitForEvent(emitter: EventEmitter, event: string, times = 1): Promise { - const promises: Promise[] = []; - - for (let i = 1; i <= times; i++) { - promises.push( - new Promise((resolve, reject) => { - const success = (value: any) => { - emitter.off(StorageEventType.ERROR, fail); - resolve(value); - }; - const fail = (error: Error) => { - emitter.off(event, success); - reject(error); - }; - emitter.once(event, success); - emitter.once(StorageEventType.ERROR, fail); - }), - ); - } - return Promise.all(promises); -} - -const directoryExists = async (dirPath: string) => - await fs.promises - .access(dirPath) - .then(() => true) - .catch(() => false); - -export async function restoreTempFolder(): Promise { - if (await directoryExists(`${IMMICH_TEST_ASSET_TEMP_PATH}`)) { - // Temp directory exists, delete all files inside it - await fs.promises.rm(IMMICH_TEST_ASSET_TEMP_PATH, { recursive: true }); - } - // Create temp folder - await fs.promises.mkdir(IMMICH_TEST_ASSET_TEMP_PATH); -} diff --git a/server/tsconfig.json b/server/tsconfig.json index ce3edda395..2b7b09545b 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -17,10 +17,7 @@ "esModuleInterop": true, "preserveWatchOutput": true, "baseUrl": "./", + "types": ["vitest/globals"] }, - "exclude": [ - "dist", - "node_modules", - "upload" - ], -} \ No newline at end of file + "exclude": ["dist", "node_modules", "upload"] +} diff --git a/server/vitest.config.mjs b/server/vitest.config.mjs new file mode 100644 index 0000000000..192f2b8df8 --- /dev/null +++ b/server/vitest.config.mjs @@ -0,0 +1,15 @@ +import swc from 'unplugin-swc'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + root: './', + globals: true, + server: { + deps: { + fallbackCJS: true, + }, + }, + }, + plugins: [swc.vite()], +}); diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs index 535765f115..e29c4a9b48 100644 --- a/web/.eslintrc.cjs +++ b/web/.eslintrc.cjs @@ -50,6 +50,7 @@ module.exports = { 'unicorn/no-nested-ternary': 'off', 'unicorn/consistent-function-scoping': 'off', 'unicorn/prefer-top-level-await': 'off', + 'unicorn/import-style': 'off', // TODO: set recommended-type-checked and remove these rules '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/no-floating-promises': 'error', diff --git a/web/Dockerfile b/web/Dockerfile index c59b6c716d..8659c64277 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,4 +1,4 @@ -FROM node:iron-alpine3.18@sha256:fa5d3cf51725bd42d32e67917623038539dbe720dab082f590785c001eb4dfef +FROM node:iron-alpine3.18@sha256:3fb85a68652064ab109ed9730f45a3ede11f064afdd3ad9f96ef7e8a3c55f47e RUN apk add --no-cache tini USER node diff --git a/web/package-lock.json b/web/package-lock.json index a400311100..b5e3a6c2f9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -29,7 +29,7 @@ "@faker-js/faker": "^8.4.1", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/enhanced-img": "^0.1.8", + "@sveltejs/enhanced-img": "^0.2.0", "@sveltejs/kit": "^2.5.2", "@sveltejs/vite-plugin-svelte": "^3.0.2", "@testing-library/jest-dom": "^6.4.2", @@ -45,7 +45,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.35.1", - "eslint-plugin-unicorn": "^51.0.1", + "eslint-plugin-unicorn": "^52.0.0", "factory.ts": "^1.4.1", "postcss": "^8.4.35", "prettier": "^3.2.5", @@ -449,9 +449,9 @@ "dev": true }, "node_modules/@emnapi/runtime": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz", - "integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.1.1.tgz", + "integrity": "sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==", "dev": true, "optional": true, "dependencies": { @@ -842,9 +842,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -947,9 +947,9 @@ "dev": true }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz", - "integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz", + "integrity": "sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw==", "cpu": [ "arm64" ], @@ -969,13 +969,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.1" + "@img/sharp-libvips-darwin-arm64": "1.0.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz", - "integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz", + "integrity": "sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw==", "cpu": [ "x64" ], @@ -995,13 +995,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.1" + "@img/sharp-libvips-darwin-x64": "1.0.2" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==", "cpu": [ "arm64" ], @@ -1021,9 +1021,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz", - "integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz", + "integrity": "sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==", "cpu": [ "x64" ], @@ -1043,9 +1043,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz", - "integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz", + "integrity": "sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==", "cpu": [ "arm" ], @@ -1065,9 +1065,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz", - "integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz", + "integrity": "sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==", "cpu": [ "arm64" ], @@ -1087,9 +1087,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz", - "integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz", + "integrity": "sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==", "cpu": [ "s390x" ], @@ -1109,9 +1109,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz", - "integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz", + "integrity": "sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==", "cpu": [ "x64" ], @@ -1131,9 +1131,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz", - "integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz", + "integrity": "sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==", "cpu": [ "arm64" ], @@ -1153,9 +1153,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz", - "integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz", + "integrity": "sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==", "cpu": [ "x64" ], @@ -1175,9 +1175,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz", - "integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz", + "integrity": "sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w==", "cpu": [ "arm" ], @@ -1197,13 +1197,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.1" + "@img/sharp-libvips-linux-arm": "1.0.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz", - "integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz", + "integrity": "sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA==", "cpu": [ "arm64" ], @@ -1223,13 +1223,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.1" + "@img/sharp-libvips-linux-arm64": "1.0.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz", - "integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.3.tgz", + "integrity": "sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA==", "cpu": [ "s390x" ], @@ -1249,13 +1249,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.1" + "@img/sharp-libvips-linux-s390x": "1.0.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz", - "integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz", + "integrity": "sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g==", "cpu": [ "x64" ], @@ -1275,13 +1275,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.1" + "@img/sharp-libvips-linux-x64": "1.0.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz", - "integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.3.tgz", + "integrity": "sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A==", "cpu": [ "arm64" ], @@ -1301,13 +1301,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.1" + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz", - "integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz", + "integrity": "sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w==", "cpu": [ "x64" ], @@ -1327,20 +1327,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.1" + "@img/sharp-libvips-linuxmusl-x64": "1.0.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz", - "integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz", + "integrity": "sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ==", "cpu": [ "wasm32" ], "dev": true, "optional": true, "dependencies": { - "@emnapi/runtime": "^0.45.0" + "@emnapi/runtime": "^1.1.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0", @@ -1353,9 +1353,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz", - "integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz", + "integrity": "sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ==", "cpu": [ "ia32" ], @@ -1375,9 +1375,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz", - "integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz", + "integrity": "sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g==", "cpu": [ "x64" ], @@ -1800,9 +1800,9 @@ "dev": true }, "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz", + "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==" }, "node_modules/@sveltejs/adapter-static": { "version": "3.0.1", @@ -1814,14 +1814,14 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.1.9.tgz", - "integrity": "sha512-gUgaiG88P6moWcxZx4YrzMhAlw1TgggKRp7n9gdfCREDeXHysCd1l9GpQR3sh109SM3rNlkiaAzt+iPLT0aG1w==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.2.0.tgz", + "integrity": "sha512-W6wG0RxQYoL13LmUl8IBHeQatMXSd2ybrjg/WQuE5EoIJq+wUkf1hUDaMp9PHe4ubpnzWK/c0QaE5Ls+zjHimA==", "dev": true, "dependencies": { "magic-string": "^0.30.5", "svelte-parse-markup": "^0.1.2", - "vite-imagetools": "^6.2.8" + "vite-imagetools": "^7.0.1" } }, "node_modules/@sveltejs/kit": { @@ -1857,17 +1857,17 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.2.tgz", - "integrity": "sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.0.tgz", + "integrity": "sha512-sY6ncCvg+O3njnzbZexcVtUqOBE3iYmQPJ9y+yXSkOwG576QI/xJrBnQSRXFLGwJNBa0T78JEKg5cIR0WOAuUw==", "dev": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "svelte-hmr": "^0.15.3", + "magic-string": "^0.30.9", + "svelte-hmr": "^0.16.0", "vitefu": "^0.2.5" }, "engines": { @@ -2162,12 +2162,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2264,22 +2258,22 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz", - "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.6.0.tgz", + "integrity": "sha512-gKmTNwZnblUdnTIJu3e9kmeRRzV2j1a/LUO27KNNAnIC5zjy1aSvXSRp4rVNlmAoHlQ7HzX42NbKpcSr4jF80A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/type-utils": "7.4.0", - "@typescript-eslint/utils": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/type-utils": "7.6.0", + "@typescript-eslint/utils": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2332,15 +2326,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz", - "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.6.0.tgz", + "integrity": "sha512-usPMPHcwX3ZoPWnBnhhorc14NJw9J4HpSXQX4urF2TPKG0au0XhJoZyX62fmvdHONUkmyUe74Hzm1//XA+BoYg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4" }, "engines": { @@ -2360,13 +2354,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz", - "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.6.0.tgz", + "integrity": "sha512-ngttyfExA5PsHSx0rdFgnADMYQi+Zkeiv4/ZxGYUWd0nLs63Ha0ksmp8VMxAIC0wtCFxMos7Lt3PszJssG/E6w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0" + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2377,15 +2371,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz", - "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.6.0.tgz", + "integrity": "sha512-NxAfqAPNLG6LTmy7uZgpK8KcuiS2NZD/HlThPXQRGwz6u7MDBWRVliEEl1Gj6U7++kVJTpehkhZzCJLMK66Scw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "7.4.0", - "@typescript-eslint/utils": "7.4.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "@typescript-eslint/utils": "7.6.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2404,9 +2398,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz", - "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.6.0.tgz", + "integrity": "sha512-h02rYQn8J+MureCvHVVzhl69/GAfQGPQZmOMjG1KfCl7o3HtMSlPaPUAPu6lLctXI5ySRGIYk94clD/AUMCUgQ==", "dev": true, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2417,19 +2411,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz", - "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.6.0.tgz", + "integrity": "sha512-+7Y/GP9VuYibecrCQWSKgl3GvUM5cILRttpWtnAu8GNL9j11e4tbuGZmZjJ8ejnKYyBRb2ddGQ3rEFCq3QjMJw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/visitor-keys": "7.4.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/visitor-keys": "7.6.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2466,9 +2460,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -2502,18 +2496,18 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz", - "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.6.0.tgz", + "integrity": "sha512-x54gaSsRRI+Nwz59TXpCsr6harB98qjXYzsRxGqvA5Ue3kQH+FxS7FYU81g/omn22ML2pZJkisy6Q+ElK8pBCA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "7.4.0", - "@typescript-eslint/types": "7.4.0", - "@typescript-eslint/typescript-estree": "7.4.0", - "semver": "^7.5.4" + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.6.0", + "@typescript-eslint/types": "7.6.0", + "@typescript-eslint/typescript-estree": "7.6.0", + "semver": "^7.6.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2560,13 +2554,13 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz", - "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.6.0.tgz", + "integrity": "sha512-4eLB7t+LlNUmXzfOu1VAIAdkjbu5xNSerURS9X/S5TUKWFRpXRQZbmtPqgKmYx8bj3J0irtQXSiWAOY82v+cgw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.4.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.6.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2583,14 +2577,14 @@ "dev": true }, "node_modules/@vitest/browser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.4.0.tgz", - "integrity": "sha512-kC44DzuqPZZrqe2P7SX2a3zHDAt919WtpkUMAxzv9eP5uPfVXtpk2Ipms2NXJGY5190aJc1uY+ambfJ3rwDJRA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-1.5.0.tgz", + "integrity": "sha512-tJLV8j8sufAT2a3eONnfgIclaqx4RqC8sA3xH8uXwxFw7CNwWCPeJ0czVkUzCE/15bHRoGxwsHC+Ycch6zDb1Q==", "dev": true, "optional": true, "peer": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "magic-string": "^0.30.5", "sirv": "^2.0.4" }, @@ -2599,7 +2593,7 @@ }, "peerDependencies": { "playwright": "*", - "vitest": "1.4.0", + "vitest": "1.5.0", "webdriverio": "*" }, "peerDependenciesMeta": { @@ -2615,9 +2609,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.5.0.tgz", + "integrity": "sha512-1igVwlcqw1QUMdfcMlzzY4coikSIBN944pkueGi0pawrX5I5Z+9hxdTR+w3Sg6Q3eZhvdMAs8ZaF9JuTG1uYOQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -2632,24 +2626,23 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.4.0" + "vitest": "1.5.0" } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -2657,12 +2650,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2698,9 +2691,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2744,9 +2737,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2756,9 +2749,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2803,9 +2796,9 @@ "dev": true }, "node_modules/@zoom-image/core": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.33.2.tgz", - "integrity": "sha512-Nqg/JrvtaScXJ16L7dcPQjun3F0enajULtokYWy13VtETqqBOBqKDa3feTJH7JXrYvEs/w6e4AU56UvzGG1wXA==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@zoom-image/core/-/core-0.34.0.tgz", + "integrity": "sha512-2gvhcxJ5J3c4ZAhTRC9opNRnPTnseM5w6IU+SbSSUOT+MN6+0/XX6Qsyebl+ADXdrgOU5Nu8wGfzeCm1QuQSNQ==", "dependencies": { "@namnode/store": "^0.1.0" }, @@ -2815,11 +2808,11 @@ } }, "node_modules/@zoom-image/svelte": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.8.tgz", - "integrity": "sha512-z+xCyMHIeTmSYZYdDde/EAz08odlBWMv6jmHOcz95DMt3sJ/+vlVtUEMCzZfuK2KqV8v59EKZHAeutMzTx3QPg==", + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@zoom-image/svelte/-/svelte-0.2.10.tgz", + "integrity": "sha512-t/zzAX1T5kLtWKgXz3kOw09+bNVOZn9enLV4+GaUWJPE6PuWPRx7JAaKKDwA+1IVhyo+pDys+0zFf0Rsn2G4jA==", "dependencies": { - "@zoom-image/core": "0.33.2" + "@zoom-image/core": "0.34.0" }, "funding": { "type": "github", @@ -3514,7 +3507,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/cookie": { "version": "0.6.0", @@ -3787,9 +3782,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "engines": { "node": ">=8" @@ -4075,10 +4070,13 @@ } }, "node_modules/eslint-compat-utils": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", - "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", + "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, "engines": { "node": ">=12" }, @@ -4086,6 +4084,39 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-compat-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-compat-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/eslint-config-prettier": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", @@ -4099,23 +4130,23 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "2.35.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.35.1.tgz", - "integrity": "sha512-IF8TpLnROSGy98Z3NrsKXWDSCbNY2ReHDcrYTuXZMbfX7VmESISR78TWgO9zdg4Dht1X8coub5jKwHzP0ExRug==", + "version": "2.37.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.37.0.tgz", + "integrity": "sha512-H/2Gz7agYHEMEEzRuLYuCmAIdjuBnbhFG9hOK0yCdSBvvJGJMkjo+lR6j67OIvLOavgp4L7zA5LnDKi8WqdPhQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@jridgewell/sourcemap-codec": "^1.4.14", - "debug": "^4.3.1", - "eslint-compat-utils": "^0.1.2", + "@eslint-community/eslint-utils": "^4.4.0", + "@jridgewell/sourcemap-codec": "^1.4.15", + "debug": "^4.3.4", + "eslint-compat-utils": "^0.5.0", "esutils": "^2.0.3", - "known-css-properties": "^0.29.0", - "postcss": "^8.4.5", + "known-css-properties": "^0.30.0", + "postcss": "^8.4.38", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.11", - "semver": "^7.5.3", - "svelte-eslint-parser": ">=0.33.0 <1.0.0" + "postcss-selector-parser": "^6.0.16", + "semver": "^7.6.0", + "svelte-eslint-parser": ">=0.34.0 <1.0.0" }, "engines": { "node": "^14.17.0 || >=16.0.0" @@ -4124,8 +4155,8 @@ "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0-0", - "svelte": "^3.37.0 || ^4.0.0" + "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0", + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.95" }, "peerDependenciesMeta": { "svelte": { @@ -4146,9 +4177,9 @@ } }, "node_modules/eslint-plugin-svelte/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4167,9 +4198,9 @@ "dev": true }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "52.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-52.0.0.tgz", + "integrity": "sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -5090,24 +5121,24 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/imagetools-core": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-6.0.4.tgz", - "integrity": "sha512-N1qs5qn7u9nR3kboISkYuvJm8MohiphCfBa+wx1UOropVaFis9/mh6wuDPLHJNhl6/64C7q2Pch5NASVKAaSrg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-7.0.0.tgz", + "integrity": "sha512-6fYbD7u4VIOt6fqKrOlbF77JXgUVyUmEJIPlfYVTuR/S2Ig9cX3gukGiLEU0aSetcfE7CYnhLTPtTEu4mLwhCw==", "dev": true, "dependencies": { "sharp": "^0.33.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/import-fresh": { @@ -5855,9 +5886,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz", - "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz", + "integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==", "dev": true }, "node_modules/levn": { @@ -5979,9 +6010,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -6869,9 +6900,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -7508,43 +7539,43 @@ } }, "node_modules/sharp": { - "version": "0.33.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz", - "integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==", + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.3.tgz", + "integrity": "sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A==", "dev": true, "hasInstallScript": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.2", - "semver": "^7.5.4" + "detect-libc": "^2.0.3", + "semver": "^7.6.0" }, "engines": { - "libvips": ">=8.15.1", + "libvips": ">=8.15.2", "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.2", - "@img/sharp-darwin-x64": "0.33.2", - "@img/sharp-libvips-darwin-arm64": "1.0.1", - "@img/sharp-libvips-darwin-x64": "1.0.1", - "@img/sharp-libvips-linux-arm": "1.0.1", - "@img/sharp-libvips-linux-arm64": "1.0.1", - "@img/sharp-libvips-linux-s390x": "1.0.1", - "@img/sharp-libvips-linux-x64": "1.0.1", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.1", - "@img/sharp-libvips-linuxmusl-x64": "1.0.1", - "@img/sharp-linux-arm": "0.33.2", - "@img/sharp-linux-arm64": "0.33.2", - "@img/sharp-linux-s390x": "0.33.2", - "@img/sharp-linux-x64": "0.33.2", - "@img/sharp-linuxmusl-arm64": "0.33.2", - "@img/sharp-linuxmusl-x64": "0.33.2", - "@img/sharp-wasm32": "0.33.2", - "@img/sharp-win32-ia32": "0.33.2", - "@img/sharp-win32-x64": "0.33.2" + "@img/sharp-darwin-arm64": "0.33.3", + "@img/sharp-darwin-x64": "0.33.3", + "@img/sharp-libvips-darwin-arm64": "1.0.2", + "@img/sharp-libvips-darwin-x64": "1.0.2", + "@img/sharp-libvips-linux-arm": "1.0.2", + "@img/sharp-libvips-linux-arm64": "1.0.2", + "@img/sharp-libvips-linux-s390x": "1.0.2", + "@img/sharp-libvips-linux-x64": "1.0.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.2", + "@img/sharp-libvips-linuxmusl-x64": "1.0.2", + "@img/sharp-linux-arm": "0.33.3", + "@img/sharp-linux-arm64": "0.33.3", + "@img/sharp-linux-s390x": "0.33.3", + "@img/sharp-linux-x64": "0.33.3", + "@img/sharp-linuxmusl-arm64": "0.33.3", + "@img/sharp-linuxmusl-x64": "0.33.3", + "@img/sharp-wasm32": "0.33.3", + "@img/sharp-win32-ia32": "0.33.3", + "@img/sharp-win32-x64": "0.33.3" } }, "node_modules/sharp/node_modules/lru-cache": { @@ -8015,9 +8046,9 @@ } }, "node_modules/svelte": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz", - "integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==", + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.13.tgz", + "integrity": "sha512-jtVt2KXLbQnsWN93Zd7EVboNh8Tqexes4rZfXNP7nYRjd9+JjubTD8BXloUmU1OUYpc6pdd1aKBhCV+b2ZKoMg==", "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -8039,9 +8070,9 @@ } }, "node_modules/svelte-check": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.8.tgz", - "integrity": "sha512-rhXU7YCDtL+lq2gCqfJDXKTxJfSsCgcd08d7VWBFxTw6IWIbMWSaASbAOD3N0VV9TYSSLUqEBiratLd8WxAJJA==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.6.9.tgz", + "integrity": "sha512-hDQrk3L0osX07djQyMiXocKysTLfusqi8AriNcCiQxhQR49/LonYolcUGMtZ0fbUR8HTR198Prrgf52WWU9wEg==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", @@ -8061,16 +8092,16 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "0.33.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.33.0.tgz", - "integrity": "sha512-5awZ6Bs+Tb/zQwa41PSdcLynAVQTwW0HGyCBjtbAQ59taLZqDgQSMzRlDmapjZdDtzERm0oXDZNE0E+PKJ6ryg==", + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.34.1.tgz", + "integrity": "sha512-9+uLA1pqI9AZioKVGJzYYmlOZWxfoCXSbAM9iaNm7H01XlYlzRTtJfZgl9o3StQGN41PfGJIbkKkfk3e/pHFfA==", "dev": true, "dependencies": { - "eslint-scope": "^7.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "postcss": "^8.4.28", - "postcss-scss": "^4.0.7" + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "postcss": "^8.4.38", + "postcss-scss": "^4.0.9" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8079,7 +8110,7 @@ "url": "https://github.com/sponsors/ota-meshi" }, "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0" + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.94" }, "peerDependenciesMeta": { "svelte": { @@ -8088,9 +8119,9 @@ } }, "node_modules/svelte-hmr": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", - "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", "dev": true, "engines": { "node": "^12.20 || ^14.13.1 || >= 16" @@ -8111,9 +8142,9 @@ } }, "node_modules/svelte-maplibre": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.8.2.tgz", - "integrity": "sha512-l4FW7VE/1/uUUyk639gtvYyK0QbNw3roRq9ouxPDS+DAW8gwpxlnDwYTtjEO+Yq7TK6xOjimJtKOG3KES8e7Uw==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.8.3.tgz", + "integrity": "sha512-aC18gtP83Xa2f95Wi/Ku1sJ5Lhhg4L05AOq/IQQ5Gdq8KxglWou7CMHlJ6dJGx2nJpQxVtFQ+3dYPwGewYuOJw==", "dependencies": { "d3-geo": "^3.1.0", "just-compare": "^2.3.0", @@ -8386,9 +8417,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "engines": { "node": ">=14.0.0" @@ -8470,12 +8501,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -8527,9 +8558,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8652,20 +8683,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8677,9 +8694,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -8732,22 +8749,22 @@ } }, "node_modules/vite-imagetools": { - "version": "6.2.9", - "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-6.2.9.tgz", - "integrity": "sha512-C4ZYhgj2vAj43/TpZ06XlDNP0p/7LIeYbgUYr+xG44nM++4HGX6YZBKAYpiBNgiCFUTJ6eXkRppWBrfPMevgmg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-7.0.1.tgz", + "integrity": "sha512-23jnLhkTH0HR9Vd9LxMYnajOLeo0RJNEAHhtlsQP6kfPuOBoTzt54rWbEWB9jmhEXAOflLQpM+FrmilVPAoyGA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.5", - "imagetools-core": "^6.0.4" + "imagetools-core": "^7.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -8781,16 +8798,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -8802,9 +8819,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -8819,8 +8836,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/web/package.json b/web/package.json index 58bb670fcf..8418af55aa 100644 --- a/web/package.json +++ b/web/package.json @@ -25,7 +25,7 @@ "@faker-js/faker": "^8.4.1", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/enhanced-img": "^0.1.8", + "@sveltejs/enhanced-img": "^0.2.0", "@sveltejs/kit": "^2.5.2", "@sveltejs/vite-plugin-svelte": "^3.0.2", "@testing-library/jest-dom": "^6.4.2", @@ -41,7 +41,7 @@ "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.35.1", - "eslint-plugin-unicorn": "^51.0.1", + "eslint-plugin-unicorn": "^52.0.0", "factory.ts": "^1.4.1", "postcss": "^8.4.35", "prettier": "^3.2.5", diff --git a/web/src/lib/components/admin-page/delete-confirm-dialoge.svelte b/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte similarity index 97% rename from web/src/lib/components/admin-page/delete-confirm-dialoge.svelte rename to web/src/lib/components/admin-page/delete-confirm-dialogue.svelte index 3abc07502e..fb4fd35fc3 100644 --- a/web/src/lib/components/admin-page/delete-confirm-dialoge.svelte +++ b/web/src/lib/components/admin-page/delete-confirm-dialogue.svelte @@ -42,7 +42,8 @@ dispatch('cancel')} diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index 15b91c98b0..0d01e2c3e4 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -147,6 +147,7 @@ {#if confirmJob} (confirmJob = null)} diff --git a/web/src/lib/components/admin-page/restore-dialoge.svelte b/web/src/lib/components/admin-page/restore-dialogue.svelte similarity index 95% rename from web/src/lib/components/admin-page/restore-dialoge.svelte rename to web/src/lib/components/admin-page/restore-dialogue.svelte index b98932f829..44edb1e7aa 100644 --- a/web/src/lib/components/admin-page/restore-dialoge.svelte +++ b/web/src/lib/components/admin-page/restore-dialogue.svelte @@ -28,7 +28,8 @@ void; - +

Are you sure you want to disable all login methods? Login will be completely disabled.

diff --git a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte index 0ffbfa5620..bc91c2c993 100644 --- a/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte +++ b/web/src/lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte @@ -54,7 +54,7 @@ inputType={SettingInputFieldType.NUMBER} {disabled} label="CONSTANT RATE FACTOR (-crf)" - desc="Video quality level. Typical values are 23 for H.264, 28 for HEVC, and 31 for VP9. Lower is better, but takes longer to encode and produces larger files." + desc="Video quality level. Typical values are 23 for H.264, 28 for HEVC, 31 for VP9, and 35 for AV1. Lower is better, but produces larger files." bind:value={config.ffmpeg.crf} required={true} isEdited={config.ffmpeg.crf !== savedConfig.ffmpeg.crf} @@ -115,12 +115,13 @@ @@ -179,7 +181,7 @@ dispatch('reset', { ...detail, configKeys: ['ffmpeg'] })} on:save={() => dispatch('save', { ffmpeg: config.ffmpeg })} - showResetToDefault={!isEqual(savedConfig.ffmpeg, defaultConfig)} + showResetToDefault={!isEqual(savedConfig.ffmpeg, defaultConfig.ffmpeg)} {disabled} />
diff --git a/web/src/lib/components/admin-page/settings/image/image-settings.svelte b/web/src/lib/components/admin-page/settings/image/image-settings.svelte index 4240e6f9d4..5b984e2305 100644 --- a/web/src/lib/components/admin-page/settings/image/image-settings.svelte +++ b/web/src/lib/components/admin-page/settings/image/image-settings.svelte @@ -1,5 +1,5 @@ - dispatch('close')}> -
-
-
-
-

Options

+ dispatch('close')}> +
+
+

SETTINGS

+
+ {#if order} + + {/if} + dispatch('toggleEnableActivity')} + /> +
+
+
+
PEOPLE
+
+ +
- dispatch('close')} /> +
+
{user.name}
+
Owner
- -
-
-

SETTINGS

-
- {#if order} - - {/if} - dispatch('toggleEnableActivity')} - /> + {#each album.sharedUsers as user (user.id)} +
+
+
+
{user.name}
-
-
PEOPLE
-
- -
-
- -
-
{user.name}
-
Owner
-
- {#each album.sharedUsers as user (user.id)} -
-
- -
-
{user.name}
-
- {/each} -
-
-
+ {/each}
diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 749be7e750..ef2a9ce3fe 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -7,7 +7,6 @@ import EditAlbumForm from '$lib/components/forms/edit-album-form.svelte'; import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte'; - import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; import { NotificationType, notificationController, @@ -432,9 +431,12 @@ {#if allowEdit} {#if albumToEdit} - (albumToEdit = null)}> - (albumToEdit = null)} /> - + (albumToEdit = null)} + onClose={() => (albumToEdit = null)} + /> {/if} @@ -442,7 +444,7 @@ {#if showShareByURLModal} closeShareModal()} + onClose={() => closeShareModal()} on:created={() => albumToShare && handleSharedLinkCreated(albumToShare)} /> {:else} @@ -450,7 +452,7 @@ album={albumToShare} on:select={({ detail: users }) => handleAddUsers(users)} on:share={() => (showShareByURLModal = true)} - on:close={() => closeShareModal()} + onClose={() => closeShareModal()} /> {/if} {/if} @@ -458,7 +460,8 @@ {#if albumToDelete} (albumToDelete = null)} diff --git a/web/src/lib/components/album-page/share-info-modal.svelte b/web/src/lib/components/album-page/share-info-modal.svelte index b96974e0e3..32e7771f45 100644 --- a/web/src/lib/components/album-page/share-info-modal.svelte +++ b/web/src/lib/components/album-page/share-info-modal.svelte @@ -5,18 +5,18 @@ import { getContextMenuPosition } from '../../utils/context-menu'; import { handleError } from '../../utils/handle-error'; import CircleIconButton from '../elements/buttons/circle-icon-button.svelte'; - import BaseModal from '../shared-components/base-modal.svelte'; import ConfirmDialogue from '../shared-components/confirm-dialogue.svelte'; import ContextMenu from '../shared-components/context-menu/context-menu.svelte'; import MenuOption from '../shared-components/context-menu/menu-option.svelte'; import { NotificationType, notificationController } from '../shared-components/notification/notification'; import UserAvatar from '../shared-components/user-avatar.svelte'; + import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; export let album: AlbumResponseDto; + export let onClose: () => void; const dispatch = createEventDispatcher<{ remove: string; - close: void; }>(); let currentUser: UserResponseDto; @@ -66,7 +66,7 @@ {#if !selectedRemoveUser} - +
@@ -80,7 +80,7 @@
{#each album.sharedUsers as user}
@@ -116,12 +116,13 @@
{/each}
-
+ {/if} {#if selectedRemoveUser && selectedRemoveUser?.id === currentUser?.id} (selectedRemoveUser = null)} diff --git a/web/src/lib/components/album-page/user-selection-modal.svelte b/web/src/lib/components/album-page/user-selection-modal.svelte index d7b7cc5142..6d66fb9706 100644 --- a/web/src/lib/components/album-page/user-selection-modal.svelte +++ b/web/src/lib/components/album-page/user-selection-modal.svelte @@ -12,17 +12,17 @@ import { mdiCheck, mdiLink, mdiShareCircle } from '@mdi/js'; import { createEventDispatcher, onMount } from 'svelte'; import Button from '../elements/buttons/button.svelte'; - import BaseModal from '../shared-components/base-modal.svelte'; import UserAvatar from '../shared-components/user-avatar.svelte'; + import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte'; export let album: AlbumResponseDto; + export let onClose: () => void; let users: UserResponseDto[] = []; let selectedUsers: UserResponseDto[] = []; const dispatch = createEventDispatcher<{ select: UserResponseDto[]; share: void; - close: void; }>(); let sharedLinks: SharedLinkResponseDto[] = []; onMount(async () => { @@ -54,7 +54,7 @@ }; - + {#if selectedUsers.length > 0}

To

@@ -75,13 +75,13 @@
{#if users.length > 0} -

SUGGESTIONS

+

SUGGESTIONS

{#each users as user} {/if}
- + diff --git a/web/src/lib/components/asset-viewer/album-list-item.svelte b/web/src/lib/components/asset-viewer/album-list-item.svelte index d97a40ae48..7261b3910a 100644 --- a/web/src/lib/components/asset-viewer/album-list-item.svelte +++ b/web/src/lib/components/asset-viewer/album-list-item.svelte @@ -29,7 +29,7 @@ + {:else} +
+
+ +
+
+
+ {#each potentialMergePeople as person (person.id)} +
+ +
+ {/each} +
- -
- {#if !choosePersonToMerge} -
- -
-
- ([personMerge1, personMerge2] = [personMerge2, personMerge1])} - /> -
- - - {:else} -
-
- -
-
-
- {#each potentialMergePeople as person (person.id)} -
- -
- {/each} -
-
-
- {/if} -
- -
-

Are these the same person?

-
-
-

They will be merged together

-
-
- - -
-
+ {/if}
+ +
+

Are these the same person?

+
+
+

They will be merged together

+
+ + + +
diff --git a/web/src/lib/components/faces-page/set-birth-date-modal.svelte b/web/src/lib/components/faces-page/set-birth-date-modal.svelte index bfd71206c0..810ec9b073 100644 --- a/web/src/lib/components/faces-page/set-birth-date-modal.svelte +++ b/web/src/lib/components/faces-page/set-birth-date-modal.svelte @@ -3,7 +3,6 @@ import Button from '../elements/buttons/button.svelte'; import FullScreenModal from '../shared-components/full-screen-modal.svelte'; import { mdiCake } from '@mdi/js'; - import Icon from '$lib/components/elements/icon.svelte'; import DateInput from '../elements/date-input.svelte'; export let birthDate: string; @@ -21,36 +20,27 @@ }; - -
-
- -

Set date of birth

- -

- Date of birth is used to calculate the age of this person at the time of a photo. -

-
- -
handleSubmit()} autocomplete="off"> -
- -
-
- - -
-
+ +
+

+ Date of birth is used to calculate the age of this person at the time of a photo. +

+ +
handleSubmit()} autocomplete="off" id="set-birth-date-form"> +
+ +
+
+ + + +
diff --git a/web/src/lib/components/forms/api-key-form.svelte b/web/src/lib/components/forms/api-key-form.svelte index 15d2aefc66..8fcf547fe5 100644 --- a/web/src/lib/components/forms/api-key-form.svelte +++ b/web/src/lib/components/forms/api-key-form.svelte @@ -1,5 +1,4 @@ - -
-
- -

- {title} -

+ +
+
+ +
- - -
- - -
- -
- - -
-
-
+ + + + + diff --git a/web/src/lib/components/forms/api-key-secret.svelte b/web/src/lib/components/forms/api-key-secret.svelte index b5ed9550f6..247b65fff6 100644 --- a/web/src/lib/components/forms/api-key-secret.svelte +++ b/web/src/lib/components/forms/api-key-secret.svelte @@ -1,5 +1,4 @@ - -
-
- -

API Key

- -

- This value will only be shown once. Please be sure to copy it before closing the window. -

-
- -
- -