diff --git a/cli/package-lock.json b/cli/package-lock.json index 423a6d4143..2e63387946 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.0.5", + "version": "2.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.0.5", + "version": "2.0.6", "license": "MIT", "dependencies": { "axios": "^1.6.2", @@ -38,7 +38,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.2.2", "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-unicorn": "^49.0.0", + "eslint-plugin-unicorn": "^50.0.0", "immich": "file:../server", "jest": "^29.5.0", "jest-extended": "^4.0.0", @@ -66,7 +66,7 @@ "@nestjs/core": "^10.2.2", "@nestjs/platform-express": "^10.2.2", "@nestjs/platform-socket.io": "^10.2.2", - "@nestjs/schedule": "^3.0.3", + "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", @@ -90,7 +90,6 @@ "joi": "^17.10.0", "lodash": "^4.17.21", "luxon": "^3.4.2", - "mv": "^2.1.1", "nest-commander": "^3.11.1", "node-addon-api": "^7.0.0", "openid-client": "^5.4.3", @@ -121,10 +120,9 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/mv": "^2.1.2", "@types/node": "^20.5.7", "@types/sharp": "^0.31.1", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", @@ -886,9 +884,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -909,9 +907,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1510,19 +1508,11 @@ "node": ">=14" } }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "node_modules/@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, @@ -1635,9 +1625,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", "dev": true }, "node_modules/@types/cli-progress": { @@ -1703,9 +1693,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1740,9 +1730,9 @@ } }, "node_modules/@types/node": { - "version": "20.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", - "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "version": "20.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", + "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1809,16 +1799,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz", - "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz", + "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/type-utils": "6.13.1", - "@typescript-eslint/utils": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/type-utils": "6.16.0", + "@typescript-eslint/utils": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1844,13 +1834,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1861,9 +1851,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1874,16 +1864,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" }, @@ -1901,17 +1892,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "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": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" }, "engines": { @@ -1926,12 +1917,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1942,16 +1933,40 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", - "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4" }, "engines": { @@ -1971,13 +1986,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1988,9 +2003,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2001,16 +2016,17 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" }, @@ -2028,12 +2044,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2044,6 +2060,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", @@ -2062,13 +2102,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", - "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz", + "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/utils": "6.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2089,13 +2129,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2106,9 +2146,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2119,16 +2159,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" }, @@ -2146,17 +2187,17 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "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": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" }, "engines": { @@ -2171,12 +2212,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2187,6 +2228,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/types": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", @@ -2530,9 +2595,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", + "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -2695,15 +2760,6 @@ "tweetnacl": "^0.14.3" } }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2715,18 +2771,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2750,9 +2794,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "funding": [ { @@ -2769,9 +2813,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" }, "bin": { @@ -2863,21 +2907,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -2914,9 +2943,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001542", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001542.tgz", - "integrity": "sha512-UrtAXVcj1mvPBFQ4sKd38daP8dEcXXr5sQe6QNNinaPd0iA/cxg9/l3VrSdL73jgw5sKyuQ6jNgiKO12W3SsVA==", + "version": "1.0.30001572", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", + "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", "dev": true, "funding": [ { @@ -3156,6 +3185,19 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-js-compat": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", + "dev": true, + "dependencies": { + "browserslist": "^4.22.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3300,168 +3342,6 @@ "node": ">=0.10.0" } }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/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/default-browser/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/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "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/default-browser/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/default-browser/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/default-browser/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/default-browser/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/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3580,9 +3460,9 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/electron-to-chromium": { - "version": "1.4.538", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.538.tgz", - "integrity": "sha512-1a2m63NEookb1beNFTGDihgF3CKL7ksZ7PSA0VloON5DpTEhnOVgaDes8xkrDhkXRxlcN8JymQDGnv+Nn+uvhg==", + "version": "1.4.616", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", "dev": true }, "node_modules/emittery": { @@ -3642,15 +3522,15 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3697,9 +3577,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -3734,23 +3614,24 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -3763,15 +3644,17 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "49.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-49.0.0.tgz", - "integrity": "sha512-0fHEa/8Pih5cmzFW5L7xMEfUTvI9WKeQtjmKpTUmY+BiFCDxkxrTdnURJOHKykhtwIeyYsxnecbGvDCml++z4Q==", + "version": "50.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-50.0.1.tgz", + "integrity": "sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^3.8.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", "esquery": "^1.5.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", @@ -3790,7 +3673,22 @@ "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { - "eslint": ">=8.52.0" + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" } }, "node_modules/eslint-scope": { @@ -4318,9 +4216,9 @@ } }, "node_modules/globals": { - "version": "13.22.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", - "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -4540,21 +4438,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4593,24 +4476,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4641,33 +4506,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -5815,9 +5653,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "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/normalize-package-data": { @@ -5886,24 +5724,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6294,9 +6114,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -6645,21 +6465,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7064,13 +6869,13 @@ "dev": true }, "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -7180,18 +6985,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -7293,9 +7086,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -7402,9 +7195,9 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -7420,15 +7213,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -8352,9 +8136,9 @@ "dev": true }, "@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -8369,9 +8153,9 @@ } }, "@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true }, "@humanwhocodes/config-array": { @@ -8830,19 +8614,11 @@ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "optional": true }, - "@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - } + "@pkgr/core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", + "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "dev": true }, "@sinclair/typebox": { "version": "0.27.8", @@ -8949,9 +8725,9 @@ "dev": true }, "@types/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", "dev": true }, "@types/cli-progress": { @@ -9017,9 +8793,9 @@ } }, "@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "requires": { "expect": "^29.0.0", @@ -9054,9 +8830,9 @@ } }, "@types/node": { - "version": "20.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", - "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "version": "20.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", + "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -9125,16 +8901,16 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz", - "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz", + "integrity": "sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/type-utils": "6.13.1", - "@typescript-eslint/utils": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/type-utils": "6.16.0", + "@typescript-eslint/utils": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -9144,116 +8920,154 @@ }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" } }, "@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" } }, "@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "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": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, "@typescript-eslint/parser": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", - "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", + "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0", "debug": "^4.3.4" }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" } }, "@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" } }, "@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, @@ -9268,72 +9082,91 @@ } }, "@typescript-eslint/type-utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", - "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz", + "integrity": "sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.13.1", - "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/typescript-estree": "6.16.0", + "@typescript-eslint/utils": "6.16.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", - "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", + "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1" + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.0" } }, "@typescript-eslint/types": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", - "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", + "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", - "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", + "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/visitor-keys": "6.13.1", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/visitor-keys": "6.16.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" } }, "@typescript-eslint/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.16.0.tgz", + "integrity": "sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==", "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": "6.13.1", - "@typescript-eslint/types": "6.13.1", - "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/scope-manager": "6.16.0", + "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/typescript-estree": "6.16.0", "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", - "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", + "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", "dev": true, "requires": { - "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/types": "6.16.0", "eslint-visitor-keys": "^3.4.1" } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, @@ -9591,9 +9424,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz", + "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -9717,12 +9550,6 @@ "tweetnacl": "^0.14.3" } }, - "big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true - }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -9734,15 +9561,6 @@ "readable-stream": "^3.4.0" } }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "requires": { - "big-integer": "^1.6.44" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -9763,14 +9581,14 @@ } }, "browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", "update-browserslist-db": "^1.0.13" } }, @@ -9827,15 +9645,6 @@ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "requires": { - "run-applescript": "^5.0.0" - } - }, "byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -9860,9 +9669,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001542", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001542.tgz", - "integrity": "sha512-UrtAXVcj1mvPBFQ4sKd38daP8dEcXXr5sQe6QNNinaPd0iA/cxg9/l3VrSdL73jgw5sKyuQ6jNgiKO12W3SsVA==", + "version": "1.0.30001572", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", + "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", "dev": true }, "chai": { @@ -10034,6 +9843,15 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "core-js-compat": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", + "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", + "dev": true, + "requires": { + "browserslist": "^4.22.2" + } + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -10135,107 +9953,6 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "dependencies": { - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "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.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "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 - }, - "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 - }, - "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 - } - } - }, - "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } - }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -10329,9 +10046,9 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "electron-to-chromium": { - "version": "1.4.538", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.538.tgz", - "integrity": "sha512-1a2m63NEookb1beNFTGDihgF3CKL7ksZ7PSA0VloON5DpTEhnOVgaDes8xkrDhkXRxlcN8JymQDGnv+Nn+uvhg==", + "version": "1.4.616", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", "dev": true }, "emittery": { @@ -10376,15 +10093,15 @@ "dev": true }, "eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -10440,9 +10157,9 @@ } }, "eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "requires": {} }, @@ -10456,25 +10173,27 @@ } }, "eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.2.tgz", + "integrity": "sha512-dhlpWc9vOwohcWmClFcA+HjlvUpuyynYs0Rf+L/P6/0iQE6vlHW9l5bkfzN62/Stm9fbq8ku46qzde76T1xlSg==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" } }, "eslint-plugin-unicorn": { - "version": "49.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-49.0.0.tgz", - "integrity": "sha512-0fHEa/8Pih5cmzFW5L7xMEfUTvI9WKeQtjmKpTUmY+BiFCDxkxrTdnURJOHKykhtwIeyYsxnecbGvDCml++z4Q==", + "version": "50.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-50.0.1.tgz", + "integrity": "sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.22.20", "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^3.8.0", + "@eslint/eslintrc": "^2.1.4", + "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", + "core-js-compat": "^3.34.0", "esquery": "^1.5.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", @@ -10485,6 +10204,14 @@ "regjsparser": "^0.10.0", "semver": "^7.5.4", "strip-indent": "^3.0.0" + }, + "dependencies": { + "ci-info": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", + "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "dev": true + } } }, "eslint-scope": { @@ -10852,9 +10579,9 @@ } }, "globals": { - "version": "13.22.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", - "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -10942,7 +10669,7 @@ "@nestjs/core": "^10.2.2", "@nestjs/platform-express": "^10.2.2", "@nestjs/platform-socket.io": "^10.2.2", - "@nestjs/schedule": "^3.0.3", + "@nestjs/schedule": "^4.0.0", "@nestjs/schematics": "^10.0.2", "@nestjs/swagger": "^7.1.8", "@nestjs/testing": "^10.2.2", @@ -10963,10 +10690,9 @@ "@types/lodash": "^4.14.197", "@types/mock-fs": "^4.13.1", "@types/multer": "^1.4.7", - "@types/mv": "^2.1.2", "@types/node": "^20.5.7", "@types/sharp": "^0.31.1", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", @@ -10996,7 +10722,6 @@ "lodash": "^4.17.21", "luxon": "^3.4.2", "mock-fs": "^5.2.0", - "mv": "^2.1.1", "nest-commander": "^3.11.1", "node-addon-api": "^7.0.0", "openid-client": "^5.4.3", @@ -11095,12 +10820,6 @@ "has": "^1.0.3" } }, - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -11127,15 +10846,6 @@ "is-extglob": "^2.1.1" } }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "requires": { - "is-docker": "^3.0.0" - } - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -11154,23 +10864,6 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - }, - "dependencies": { - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - } - } - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -12064,9 +11757,9 @@ "dev": true }, "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "normalize-package-data": { @@ -12122,18 +11815,6 @@ "mimic-fn": "^2.1.0" } }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - } - }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -12417,9 +12098,9 @@ } }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "pure-rand": { @@ -12668,15 +12349,6 @@ } } }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - } - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12979,13 +12651,13 @@ "dev": true }, "synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", "dev": true, "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" } }, "tar-fs": { @@ -13081,12 +12753,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true - }, "tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -13147,9 +12813,9 @@ } }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -13218,9 +12884,9 @@ "dev": true }, "typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "undici-types": { @@ -13229,12 +12895,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, "update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", diff --git a/cli/package.json b/cli/package.json index 380a330749..b139950e65 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.0.5", + "version": "2.0.6", "description": "Command Line Interface (CLI) for Immich", "main": "dist/index.js", "bin": { @@ -38,7 +38,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-jest": "^27.2.2", "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-unicorn": "^49.0.0", + "eslint-plugin-unicorn": "^50.0.0", "immich": "file:../server", "jest": "^29.5.0", "jest-extended": "^4.0.0", diff --git a/cli/src/commands/upload.ts b/cli/src/commands/upload.ts index da3146f95e..c76c845f98 100644 --- a/cli/src/commands/upload.ts +++ b/cli/src/commands/upload.ts @@ -60,7 +60,7 @@ export default class Upload extends BaseCommand { for (const asset of assetsToUpload) { // Compute total size first - await asset.process(); + await asset.prepare(); totalSize += asset.fileSize; if (options.albumName) { diff --git a/cli/src/cores/models/asset.ts b/cli/src/cores/models/asset.ts index 378945e6d3..a3e9b8f8e7 100644 --- a/cli/src/cores/models/asset.ts +++ b/cli/src/cores/models/asset.ts @@ -1,18 +1,17 @@ -import * as fs from 'graceful-fs'; -import { basename } from 'node:path'; import crypto from 'crypto'; -import Os from 'os'; import FormData from 'form-data'; +import * as fs from 'graceful-fs'; +import { createReadStream } from 'node:fs'; +import { basename } from 'node:path'; +import Os from 'os'; export class Asset { readonly path: string; readonly deviceId!: string; - assetData?: fs.ReadStream; deviceAssetId?: string; fileCreatedAt?: string; fileModifiedAt?: string; - sidecarData?: fs.ReadStream; sidecarPath?: string; fileSize!: number; albumName?: string; @@ -21,32 +20,30 @@ export class Asset { this.path = path; } - async process() { + async prepare() { const stats = await fs.promises.stat(this.path); this.deviceAssetId = `${basename(this.path)}-${stats.size}`.replace(/\s+/g, ''); this.fileCreatedAt = stats.mtime.toISOString(); this.fileModifiedAt = stats.mtime.toISOString(); this.fileSize = stats.size; this.albumName = this.extractAlbumName(); - - this.assetData = this.getReadStream(this.path); - - // TODO: doesn't xmp replace the file extension? Will need investigation - const sideCarPath = `${this.path}.xmp`; - try { - fs.accessSync(sideCarPath, fs.constants.R_OK); - this.sidecarData = this.getReadStream(sideCarPath); - } catch (error) {} } getUploadFormData(): FormData { - if (!this.assetData) throw new Error('Asset data not set'); if (!this.deviceAssetId) throw new Error('Device asset id not set'); if (!this.fileCreatedAt) throw new Error('File created at not set'); if (!this.fileModifiedAt) throw new Error('File modified at not set'); + // TODO: doesn't xmp replace the file extension? Will need investigation + const sideCarPath = `${this.path}.xmp`; + let sidecarData: fs.ReadStream | undefined = undefined; + try { + fs.accessSync(sideCarPath, fs.constants.R_OK); + sidecarData = createReadStream(sideCarPath); + } catch (error) {} + const data: any = { - assetData: this.assetData as any, + assetData: createReadStream(this.path), deviceAssetId: this.deviceAssetId, deviceId: 'CLI', fileCreatedAt: this.fileCreatedAt, @@ -59,17 +56,13 @@ export class Asset { formData.append(prop, data[prop]); } - if (this.sidecarData) { - formData.append('sidecarData', this.sidecarData); + if (sidecarData) { + formData.append('sidecarData', sidecarData); } return formData; } - private getReadStream(path: string): fs.ReadStream { - return fs.createReadStream(path); - } - async delete(): Promise { return fs.promises.unlink(this.path); } diff --git a/docs/docs/features/facial-recognition.md b/docs/docs/features/facial-recognition.md index 2095df5874..7efc187afa 100644 --- a/docs/docs/features/facial-recognition.md +++ b/docs/docs/features/facial-recognition.md @@ -25,6 +25,6 @@ Additional actions you can do with a detected person are: - Merge two or more detected faces into one person - Hide face -It can be found from the app bar when you access the detial view of a person +It can be found from the app bar when you access the detail view of a person. diff --git a/docs/docs/features/hardware-transcoding.md b/docs/docs/features/hardware-transcoding.md index 2f4a3e4785..c02c569973 100644 --- a/docs/docs/features/hardware-transcoding.md +++ b/docs/docs/features/hardware-transcoding.md @@ -43,12 +43,28 @@ As this is a new feature, it is still experimental and may not work on all syste ## Setup +#### Initial Setup + 1. If you do not already have it, download the latest [`hwaccel.yml`][hw-file] file and ensure it's in the same folder as the `docker-compose.yml`. 2. Uncomment the lines that apply to your system and desired usage. 3. In the `docker-compose.yml` under `immich-microservices`, uncomment the lines relating to the `hwaccel.yml` file. 4. Redeploy the `immich-microservices` container with these updated settings. 5. In the Admin page under `FFmpeg settings`, change the hardware acceleration setting to the appropriate option and save. +#### All-In-One - Unraid Setup + +##### NVENC - NVIDIA GPUs + +- If you are using other backends. You will still need to implement [`hwaccel.yml`][hw-file] file into the `immich-microservices` service directly, please see the "Initial Setup" section above on how to do that. +- As of v1.92.0, steps 1 and 2 are no longer necessary. If your version of Immich is below that or missing the environment variables, please follow these steps. Otherwise, skip to step 3. +- Please note that`NVIDIA_DRIVER_CAPABILITIES` is no longer required to enter as a variable. + +1. Assuming you already have the Nvidia Driver Plugin installed on your Unraid Server. Please confirm that your Nvida GPU is showing up with its GPU ID in the Nvidia Driver Plugin. The ID will be `GPU-LONG_STRING_OF_CHARACTERS`. Copy the GPU ID. +2. In the Imagegenius/Immich Docker Container app, add two new variables: Key=`NVIDIA_VISIBLE_DEVICES` Value=`GPU-LONG_STRING_OF_CHARACTERS` and Key=`NVIDIA_DRIVER_CAPABILITIES` Value=`all` +3. While you are in the docker container app, change the Container from Basic Mode to Advanced Mode and add the following parameter to the Extra Parameters field: `--runtime=nvidia` +4. Restart the Imagegenius/Immich Docker Container app. +5. In the Admin page under FFmpeg settings, change the hardware acceleration setting to the appropriate option and save. + ## Tips - You may want to choose a slower preset than for software transcoding to maintain quality and efficiency diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index 1437fb8630..46a27b9006 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -12,6 +12,26 @@ Immich comes preconfigured with an upload library for each user. All assets uplo External libraries tracks assets stored outside of immich, i.e. in the file system. Immich will only read data from the files, and will not modify them in any way. Therefore, the delete button is disabled for external assets. When the external library is scanned, immich will read the metadata from the file and create an asset in the library for each image or video file. These items will then be shown in the main timeline, and they will look and behave like any other asset, including viewing on the map, adding to albums, etc. +# Step by Step Guide + +In order to create an external library, the administrator must: + +- Click on 'Administration' (top right). +- Click on 'Edit' next to the user. +- Set the external path - this would be the path that is exposed to the immich microservices service. +- Click 'Confirm'. +- Click into the user profile (top right) then click into 'Libraries'. +- Click 'Create External Library'. +- Next to the new library, click the three dots, then click 'Edit Import Paths'. +- Set up your path relative to the external path you set earlier - e.g., if you set the external path to be `/mnt/media/external` and your Import Paths to `/photos`, then you will end up with `/mnt/media/external/photos`. +- Click 'Save'. +- Click 'Scan All Libraries'. + +To confirm it's working, you can open up a Docker shell for the immich microservices, and you should see similar outputs to this: +`[Nest] 7 - 11/24/2023, 1:06:39 AM LOG [MediaService] Successfully generated WEBP image thumbnail for asset b6d37673-5931-4b25-8380-89135021f48b` + +# Scanning functionality + If a file is modified outside of Immich, the changes will not be reflected in immich until the library is scanned again. There are different ways to scan a library depending on the use case: - Scan Library Files: This is the default scan method and also the quickest. It will scan all files in the library and add new files to the library. It will notice if any files are missing (see below) but not check existing assets diff --git a/docs/docs/guides/remote-access.md b/docs/docs/guides/remote-access.md index fe98e12e44..fef680d268 100644 --- a/docs/docs/guides/remote-access.md +++ b/docs/docs/guides/remote-access.md @@ -42,7 +42,7 @@ If you are unable to open a port on your router for Wireguard or OpenVPN to your A reverse proxy is a service that sits between web servers and clients. A reverse proxy can either be hosted on the server itself or remotely. Clients can connect to the reverse proxy via https, and the proxy relays data to Immich. This setup makes most sense if you have your own domain and want to access your Immich instance just like any other website, from outside your LAN. You can also use a DDNS provider like DuckDNS or no-ip if you don't have a domain. This configuration allows the Immich Android and iphone apps to connect to your server without a VPN or tailscale app on the client side. -If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](https://immich.app/docs/administration/reverse-proxy). +If you're hosting your own reverse proxy, [Nginx](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/) is a great option. An example configuration for Nginx is provided [here](/docs/administration/reverse-proxy.md). You'll also need your own certificate to authenticate https connections. If you're making Immich publicly accesible, [Let's Encrypt](https://letsencrypt.org/) can provide a free certificate for your domain and is the recommended option. Alternatively, a [self-signed certificate](https://en.wikipedia.org/wiki/Self-signed_certificate) allows you to encrypt your connection to Immich, but it raises a security warning on the client's browser. diff --git a/docs/docs/install/config-file.md b/docs/docs/install/config-file.md index 4e81583d01..2a9650f5c8 100644 --- a/docs/docs/install/config-file.md +++ b/docs/docs/install/config-file.md @@ -142,4 +142,4 @@ So you can just grab it from there, paste it into a file and you're pretty much ### Step 2 - Specify the file location In your `.env` file, set the variable `IMMICH_CONFIG_FILE` to the path of your config. -For more information, refer to the [Environment Variables](https://docs.immich.app/docs/install/environment-variables) section. +For more information, refer to the [Environment Variables](/docs/install/environment-variables.md) section. diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index e2ae101f45..83364415b5 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -93,7 +93,7 @@ "backup_controller_page_status_off": "Automatic foreground backup is off", "backup_controller_page_status_on": "Automatic foreground backup is on", "backup_controller_page_storage_format": "{} of {} used", - "backup_controller_page_to_backup": "Albums to be backup", + "backup_controller_page_to_backup": "Albums to back up", "backup_controller_page_total": "Total", "backup_controller_page_total_sub": "All unique photos and videos from selected albums", "backup_controller_page_turn_off": "Turn off foreground backup", @@ -103,7 +103,7 @@ "backup_info_card_assets": "assets", "backup_manual_cancelled": "Cancelled", "backup_manual_failed": "Failed", - "backup_manual_in_progress": "Upload already in progress. Try after sometime", + "backup_manual_in_progress": "Upload already in progress. Try again after some time", "backup_manual_success": "Success", "backup_manual_title": "Upload status", "cache_settings_album_thumbnails": "Library page thumbnails ({} assets)", @@ -190,7 +190,7 @@ "home_page_delete_err_partner": "Can not delete partner assets, skipping", "home_page_favorite_err_local": "Can not favorite local assets yet, skipping", "home_page_favorite_err_partner": "Can not favorite partner assets yet, skipping", - "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).", + "home_page_first_time_notice": "If this is your first time using the app, please make sure to choose backup album(s) so that the timeline can populate photos and videos in the album(s).", "home_page_share_err_local": "Can not share local assets via link, skipping", "home_page_upload_err_limit": "Can only upload a maximum of 30 assets at a time, skipping", "image_viewer_page_state_provider_download_error": "Download Error", @@ -230,7 +230,7 @@ "login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL", "login_form_failed_get_oauth_server_disable": "OAuth feature is not available on this server", "login_form_failed_login": "Error logging you in, check server URL, email and password", - "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", + "login_form_handshake_exception": "There was a Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", "login_form_label_email": "Email", "login_form_label_password": "Password", "login_form_next_button": "Next", @@ -379,8 +379,8 @@ "shared_link_create_error": "Error while creating shared link", "shared_link_create_info": "Let anyone with the link see the selected photo(s)", "shared_link_create_submit_button": "Create link", - "shared_link_edit_allow_download": "Allow public user to download", - "shared_link_edit_allow_upload": "Allow public user to upload", + "shared_link_edit_allow_download": "Allow public users to download", + "shared_link_edit_allow_upload": "Allow public users to upload", "shared_link_edit_app_bar_title": "Edit link", "shared_link_edit_change_expiry": "Change expiration time", "shared_link_edit_description": "Description", @@ -430,7 +430,7 @@ "theme_setting_dark_mode_switch": "Dark mode", "theme_setting_image_viewer_quality_subtitle": "Adjust the quality of the detail image viewer", "theme_setting_image_viewer_quality_title": "Image viewer quality", - "theme_setting_system_theme_switch": "Automatic (Follow system setting)", + "theme_setting_system_theme_switch": "Automatic (follow system settings)", "theme_setting_theme_subtitle": "Choose the app's theme setting", "theme_setting_theme_title": "Theme", "theme_setting_three_stage_loading_subtitle": "Three-stage loading might increase the loading performance but causes significantly higher network load", @@ -461,4 +461,4 @@ "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 +} diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index 99660d6907..05c60ea28d 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -44,10 +44,7 @@ class BackupService { final String deviceId = Store.get(StoreKey.deviceId); try { - return await _apiService.assetApi.getUserAssetsByDeviceId(deviceId); - - // TODO! Start using this in 1.92.0 - // return await _apiService.assetApi.getAllUserAssetsByDeviceId(deviceId); + return await _apiService.assetApi.getAllUserAssetsByDeviceId(deviceId); } catch (e) { debugPrint('Error [getDeviceBackupAsset] ${e.toString()}'); return null; diff --git a/renovate.json b/renovate.json index 6f6bfce684..4fc8428a4d 100644 --- a/renovate.json +++ b/renovate.json @@ -33,6 +33,12 @@ "matchPackagePrefixes": ["exiftool"], "schedule": "on tuesday" }, + { + "groupName": "svelte", + "matchUpdateTypes": ["major"], + "matchPackagePrefixes": ["@sveltejs"], + "schedule": "on tuesday" + }, { "matchFileNames": ["web/**"], "groupName": "web", @@ -62,9 +68,7 @@ "versioning": "node" } ], - "ignorePaths": [ - "mobile/openapi/pubspec.yaml" - ], + "ignorePaths": ["mobile/openapi/pubspec.yaml"], "ignoreDeps": [ "http", "latlong2", diff --git a/server/package-lock.json b/server/package-lock.json index d3e951ba21..96b2a30651 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -17,7 +17,7 @@ "@nestjs/core": "^10.2.2", "@nestjs/platform-express": "^10.2.2", "@nestjs/platform-socket.io": "^10.2.2", - "@nestjs/schedule": "^3.0.3", + "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", @@ -73,7 +73,7 @@ "@types/multer": "^1.4.7", "@types/node": "^20.5.7", "@types/sharp": "^0.31.1", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", @@ -2466,11 +2466,11 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@nestjs/schedule": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-3.0.4.tgz", - "integrity": "sha512-uFJpuZsXfpvgx2y7/KrIZW9e1L68TLiwRodZ6+Gc8xqQiHSUzAVn+9F4YMxWFlHITZvvkjWziUFgRNCitDcTZQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.0.0.tgz", + "integrity": "sha512-zz4h54m/F/1qyQKvMJCRphmuwGqJltDAkFxUXCVqJBXEs5kbPt93Pza3heCQOcMH22MZNhGlc9DmDMLXVHmgVQ==", "dependencies": { - "cron": "2.4.3", + "cron": "3.1.3", "uuid": "9.0.1" }, "peerDependencies": { @@ -3336,9 +3336,9 @@ } }, "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "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/cors": { @@ -3522,6 +3522,12 @@ "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.5.tgz", "integrity": "sha512-1cyf6Ge/94zlaWIZA2ei1pE6SZ8xpad2hXaYa5JEFiaUH0YS494CZwyi4MXNpXD9oEuv6ZH0Bmh0e7F9sPhmZA==" }, + "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", @@ -3699,22 +3705,24 @@ "dev": true }, "node_modules/@types/superagent": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.19.tgz", - "integrity": "sha512-McM1mlc7PBZpCaw0fw/36uFqo0YeA6m8JqoyE4OfqXsZCIg0hPP2xdE6FM7r6fdprDZHlJwDpydUj1R++93hCA==", + "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": "*", + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", "@types/node": "*" } }, "node_modules/@types/supertest": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", - "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", + "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/superagent": "*" + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" } }, "node_modules/@types/through": { @@ -5610,12 +5618,12 @@ "devOptional": true }, "node_modules/cron": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/cron/-/cron-2.4.3.tgz", - "integrity": "sha512-YBvExkQYF7w0PxyeFLRyr817YVDhGxaCi5/uRRMqa4aWD3IFKRd+uNbpW1VWMdqQy8PZ7CElc+accXJcauPKzQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.3.tgz", + "integrity": "sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==", "dependencies": { "@types/luxon": "~3.3.0", - "luxon": "~3.3.0" + "luxon": "~3.4.0" } }, "node_modules/cron-parser": { @@ -5629,14 +5637,6 @@ "node": ">=12.0.0" } }, - "node_modules/cron/node_modules/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==", - "engines": { - "node": ">=12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -14601,11 +14601,11 @@ } }, "@nestjs/schedule": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-3.0.4.tgz", - "integrity": "sha512-uFJpuZsXfpvgx2y7/KrIZW9e1L68TLiwRodZ6+Gc8xqQiHSUzAVn+9F4YMxWFlHITZvvkjWziUFgRNCitDcTZQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.0.0.tgz", + "integrity": "sha512-zz4h54m/F/1qyQKvMJCRphmuwGqJltDAkFxUXCVqJBXEs5kbPt93Pza3heCQOcMH22MZNhGlc9DmDMLXVHmgVQ==", "requires": { - "cron": "2.4.3", + "cron": "3.1.3", "uuid": "9.0.1" }, "dependencies": { @@ -15254,9 +15254,9 @@ } }, "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "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/cors": { @@ -15440,6 +15440,12 @@ "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.5.tgz", "integrity": "sha512-1cyf6Ge/94zlaWIZA2ei1pE6SZ8xpad2hXaYa5JEFiaUH0YS494CZwyi4MXNpXD9oEuv6ZH0Bmh0e7F9sPhmZA==" }, + "@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", @@ -15604,22 +15610,24 @@ "dev": true }, "@types/superagent": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.19.tgz", - "integrity": "sha512-McM1mlc7PBZpCaw0fw/36uFqo0YeA6m8JqoyE4OfqXsZCIg0hPP2xdE6FM7r6fdprDZHlJwDpydUj1R++93hCA==", + "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": "*", + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", "@types/node": "*" } }, "@types/supertest": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", - "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", + "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/superagent": "*" + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" } }, "@types/through": { @@ -17029,19 +17037,12 @@ "devOptional": true }, "cron": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/cron/-/cron-2.4.3.tgz", - "integrity": "sha512-YBvExkQYF7w0PxyeFLRyr817YVDhGxaCi5/uRRMqa4aWD3IFKRd+uNbpW1VWMdqQy8PZ7CElc+accXJcauPKzQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.3.tgz", + "integrity": "sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==", "requires": { "@types/luxon": "~3.3.0", - "luxon": "~3.3.0" - }, - "dependencies": { - "luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==" - } + "luxon": "~3.4.0" } }, "cron-parser": { diff --git a/server/package.json b/server/package.json index 9db511eeac..bd895c140b 100644 --- a/server/package.json +++ b/server/package.json @@ -44,7 +44,7 @@ "@nestjs/core": "^10.2.2", "@nestjs/platform-express": "^10.2.2", "@nestjs/platform-socket.io": "^10.2.2", - "@nestjs/schedule": "^3.0.3", + "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.1.8", "@nestjs/typeorm": "^10.0.0", "@nestjs/websockets": "^10.2.2", @@ -100,7 +100,7 @@ "@types/multer": "^1.4.7", "@types/node": "^20.5.7", "@types/sharp": "^0.31.1", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", "@typescript-eslint/eslint-plugin": "^6.4.1", "@typescript-eslint/parser": "^6.4.1", diff --git a/server/src/domain/asset/asset.service.spec.ts b/server/src/domain/asset/asset.service.spec.ts index 3826ad969a..fb0bdc60b3 100644 --- a/server/src/domain/asset/asset.service.spec.ts +++ b/server/src/domain/asset/asset.service.spec.ts @@ -784,9 +784,9 @@ describe(AssetService.name, () => { await sut.deleteAll(authStub.user1, { ids: ['asset1', 'asset2'], force: true }); - expect(jobMock.queue.mock.calls).toEqual([ - [{ name: JobName.ASSET_DELETION, data: { id: 'asset1' } }], - [{ name: JobName.ASSET_DELETION, data: { id: 'asset2' } }], + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { name: JobName.ASSET_DELETION, data: { id: 'asset1' } }, + { name: JobName.ASSET_DELETION, data: { id: 'asset2' } }, ]); }); @@ -895,6 +895,7 @@ describe(AssetService.name, () => { await sut.handleAssetDeletion({ id: assetStub.external.id }); expect(jobMock.queue).not.toBeCalled(); + expect(jobMock.queueAll).not.toBeCalled(); expect(assetMock.remove).not.toBeCalled(); }); @@ -952,19 +953,21 @@ describe(AssetService.name, () => { it('should run the refresh metadata job', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REFRESH_METADATA }), - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.METADATA_EXTRACTION, data: { id: 'asset-1' } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.METADATA_EXTRACTION, data: { id: 'asset-1' } }]); }); it('should run the refresh thumbnails job', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REGENERATE_THUMBNAIL }), - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } }, + ]); }); it('should run the transcode video', async () => { accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1'])); await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.TRANSCODE_VIDEO }), - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.VIDEO_CONVERSION, data: { id: 'asset-1' } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.VIDEO_CONVERSION, data: { id: 'asset-1' } }]); }); }); diff --git a/server/src/domain/asset/asset.service.ts b/server/src/domain/asset/asset.service.ts index 84438f19d6..7ab77a5fcb 100644 --- a/server/src/domain/asset/asset.service.ts +++ b/server/src/domain/asset/asset.service.ts @@ -21,6 +21,7 @@ import { IStorageRepository, ISystemConfigRepository, ImmichReadStream, + JobItem, TimeBucketOptions, } from '../repositories'; import { StorageCore, StorageFolder } from '../storage'; @@ -449,9 +450,9 @@ export class AssetService { ); for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id } })), + ); } return true; @@ -504,9 +505,7 @@ export class AssetService { await this.access.requirePermission(auth, Permission.ASSET_DELETE, ids); if (force) { - for (const id of ids) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id } }); - } + await this.jobRepository.queueAll(ids.map((id) => ({ name: JobName.ASSET_DELETION, data: { id } }))); } else { await this.assetRepository.softDeleteAll(ids); this.communicationRepository.send(ClientEvent.ASSET_TRASH, auth.user.id, ids); @@ -529,9 +528,9 @@ export class AssetService { if (action == TrashAction.EMPTY_ALL) { for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id } })), + ); } return; } @@ -566,21 +565,25 @@ export class AssetService { async run(auth: AuthDto, dto: AssetJobsDto) { await this.access.requirePermission(auth, Permission.ASSET_UPDATE, dto.assetIds); + const jobs: JobItem[] = []; + for (const id of dto.assetIds) { switch (dto.name) { case AssetJobName.REFRESH_METADATA: - await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id } }); + jobs.push({ name: JobName.METADATA_EXTRACTION, data: { id } }); break; case AssetJobName.REGENERATE_THUMBNAIL: - await this.jobRepository.queue({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id } }); + jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id } }); break; case AssetJobName.TRANSCODE_VIDEO: - await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: { id } }); + jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id } }); break; } } + + await this.jobRepository.queueAll(jobs); } private async updateMetadata(dto: ISidecarWriteJob) { diff --git a/server/src/domain/job/job.service.spec.ts b/server/src/domain/job/job.service.spec.ts index 1e5da85729..6dbbd9b600 100644 --- a/server/src/domain/job/job.service.spec.ts +++ b/server/src/domain/job/job.service.spec.ts @@ -55,12 +55,12 @@ describe(JobService.name, () => { it('should run the scheduled jobs', async () => { await sut.handleNightlyJobs(); - expect(jobMock.queue.mock.calls).toEqual([ - [{ name: JobName.ASSET_DELETION_CHECK }], - [{ name: JobName.USER_DELETE_CHECK }], - [{ name: JobName.PERSON_CLEANUP }], - [{ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }], - [{ name: JobName.CLEAN_OLD_AUDIT_LOGS }], + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { name: JobName.ASSET_DELETION_CHECK }, + { name: JobName.USER_DELETE_CHECK }, + { name: JobName.PERSON_CLEANUP }, + { name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }, + { name: JobName.CLEAN_OLD_AUDIT_LOGS }, ]); }); }); @@ -138,6 +138,7 @@ describe(JobService.name, () => { ).rejects.toBeInstanceOf(BadRequestException); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); it('should handle a start video conversion command', async () => { @@ -204,6 +205,7 @@ describe(JobService.name, () => { ).rejects.toBeInstanceOf(BadRequestException); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); }); @@ -276,18 +278,18 @@ describe(JobService.name, () => { item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } }, jobs: [ JobName.GENERATE_WEBP_THUMBNAIL, + JobName.GENERATE_THUMBHASH_THUMBNAIL, JobName.ENCODE_CLIP, JobName.RECOGNIZE_FACES, - JobName.GENERATE_THUMBHASH_THUMBNAIL, ], }, { item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } }, jobs: [ JobName.GENERATE_WEBP_THUMBNAIL, + JobName.GENERATE_THUMBHASH_THUMBNAIL, JobName.ENCODE_CLIP, JobName.RECOGNIZE_FACES, - JobName.GENERATE_THUMBHASH_THUMBNAIL, JobName.VIDEO_CONVERSION, ], }, @@ -295,9 +297,9 @@ describe(JobService.name, () => { item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } }, jobs: [ JobName.GENERATE_WEBP_THUMBNAIL, - JobName.RECOGNIZE_FACES, JobName.GENERATE_THUMBHASH_THUMBNAIL, JobName.ENCODE_CLIP, + JobName.RECOGNIZE_FACES, JobName.VIDEO_CONVERSION, ], }, @@ -327,9 +329,15 @@ describe(JobService.name, () => { await jobMock.addHandler.mock.calls[0][2](item); await asyncTick(3); - expect(jobMock.queue).toHaveBeenCalledTimes(jobs.length); - for (const jobName of jobs) { - expect(jobMock.queue).toHaveBeenCalledWith({ name: jobName, data: expect.anything() }); + if (jobs.length > 1) { + expect(jobMock.queueAll).toHaveBeenCalledWith( + jobs.map((jobName) => ({ name: jobName, data: expect.anything() })), + ); + } else { + expect(jobMock.queue).toHaveBeenCalledTimes(jobs.length); + for (const jobName of jobs) { + expect(jobMock.queue).toHaveBeenCalledWith({ name: jobName, data: expect.anything() }); + } } }); @@ -338,7 +346,7 @@ describe(JobService.name, () => { await jobMock.addHandler.mock.calls[0][2](item); await asyncTick(3); - expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); } diff --git a/server/src/domain/job/job.service.ts b/server/src/domain/job/job.service.ts index f622dbdf20..3f418cf335 100644 --- a/server/src/domain/job/job.service.ts +++ b/server/src/domain/job/job.service.ts @@ -158,11 +158,13 @@ export class JobService { } async handleNightlyJobs() { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION_CHECK }); - await this.jobRepository.queue({ name: JobName.USER_DELETE_CHECK }); - await this.jobRepository.queue({ name: JobName.PERSON_CLEANUP }); - await this.jobRepository.queue({ name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }); - await this.jobRepository.queue({ name: JobName.CLEAN_OLD_AUDIT_LOGS }); + await this.jobRepository.queueAll([ + { name: JobName.ASSET_DELETION_CHECK }, + { name: JobName.USER_DELETE_CHECK }, + { name: JobName.PERSON_CLEANUP }, + { name: JobName.QUEUE_GENERATE_THUMBNAILS, data: { force: false } }, + { name: JobName.CLEAN_OLD_AUDIT_LOGS }, + ]); } /** @@ -210,19 +212,23 @@ export class JobService { break; case JobName.GENERATE_JPEG_THUMBNAIL: { - await this.jobRepository.queue({ name: JobName.GENERATE_WEBP_THUMBNAIL, data: item.data }); - await this.jobRepository.queue({ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: item.data }); - await this.jobRepository.queue({ name: JobName.ENCODE_CLIP, data: item.data }); - await this.jobRepository.queue({ name: JobName.RECOGNIZE_FACES, data: item.data }); + const jobs: JobItem[] = [ + { name: JobName.GENERATE_WEBP_THUMBNAIL, data: item.data }, + { name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: item.data }, + { name: JobName.ENCODE_CLIP, data: item.data }, + { name: JobName.RECOGNIZE_FACES, data: item.data }, + ]; const [asset] = await this.assetRepository.getByIds([item.data.id]); if (asset) { if (asset.type === AssetType.VIDEO) { - await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: item.data }); + jobs.push({ name: JobName.VIDEO_CONVERSION, data: item.data }); } else if (asset.livePhotoVideoId) { - await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); + jobs.push({ name: JobName.VIDEO_CONVERSION, data: { id: asset.livePhotoVideoId } }); } } + + await this.jobRepository.queueAll(jobs); break; } diff --git a/server/src/domain/library/library.service.spec.ts b/server/src/domain/library/library.service.spec.ts index d8fac9bf7e..e52d4cdc4b 100644 --- a/server/src/domain/library/library.service.spec.ts +++ b/server/src/domain/library/library.service.spec.ts @@ -135,18 +135,16 @@ describe(LibraryService.name, () => { await sut.handleQueueAssetRefresh(mockLibraryJob); - expect(jobMock.queue.mock.calls).toEqual([ - [ - { - name: JobName.LIBRARY_SCAN_ASSET, - data: { - id: libraryStub.externalLibrary1.id, - ownerId: libraryStub.externalLibrary1.owner.id, - assetPath: '/data/user1/photo.jpg', - force: false, - }, + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.LIBRARY_SCAN_ASSET, + data: { + id: libraryStub.externalLibrary1.id, + ownerId: libraryStub.externalLibrary1.owner.id, + assetPath: '/data/user1/photo.jpg', + force: false, }, - ], + }, ]); }); @@ -420,6 +418,7 @@ describe(LibraryService.name, () => { await expect(sut.handleAssetRefresh(mockLibraryJob)).resolves.toBe(true); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); it('should import an asset when mtime differs from db asset', async () => { @@ -468,6 +467,7 @@ describe(LibraryService.name, () => { expect(assetMock.save).toHaveBeenCalledWith({ id: assetStub.image.id, isOffline: true }); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); it('should online a previously-offline asset', async () => { @@ -607,6 +607,7 @@ describe(LibraryService.name, () => { ); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(libraryMock.softDelete).not.toHaveBeenCalled(); }); @@ -953,9 +954,9 @@ describe(LibraryService.name, () => { libraryMock.getAllDeleted.mockResolvedValue([libraryStub.uploadLibrary1, libraryStub.externalLibrary1]); await expect(sut.handleQueueCleanup()).resolves.toBe(true); - expect(jobMock.queue.mock.calls).toEqual([ - [{ name: JobName.LIBRARY_DELETE, data: { id: libraryStub.uploadLibrary1.id } }], - [{ name: JobName.LIBRARY_DELETE, data: { id: libraryStub.externalLibrary1.id } }], + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { name: JobName.LIBRARY_DELETE, data: { id: libraryStub.uploadLibrary1.id } }, + { name: JobName.LIBRARY_DELETE, data: { id: libraryStub.externalLibrary1.id } }, ]); }); }); @@ -1101,16 +1102,16 @@ describe(LibraryService.name, () => { data: {}, }, ], - [ - { - name: JobName.LIBRARY_SCAN, - data: { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: true, - refreshAllFiles: false, - }, + ]); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.LIBRARY_SCAN, + data: { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: true, + refreshAllFiles: false, }, - ], + }, ]); }); @@ -1126,16 +1127,16 @@ describe(LibraryService.name, () => { data: {}, }, ], - [ - { - name: JobName.LIBRARY_SCAN, - data: { - id: libraryStub.externalLibrary1.id, - refreshModifiedFiles: false, - refreshAllFiles: true, - }, + ]); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.LIBRARY_SCAN, + data: { + id: libraryStub.externalLibrary1.id, + refreshModifiedFiles: false, + refreshAllFiles: true, }, - ], + }, ]); }); }); @@ -1147,13 +1148,11 @@ describe(LibraryService.name, () => { await expect(sut.handleOfflineRemoval({ id: libraryStub.externalLibrary1.id })).resolves.toBe(true); - expect(jobMock.queue.mock.calls).toEqual([ - [ - { - name: JobName.ASSET_DELETION, - data: { id: assetStub.image1.id, fromExternal: true }, - }, - ], + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.ASSET_DELETION, + data: { id: assetStub.image1.id, fromExternal: true }, + }, ]); }); }); diff --git a/server/src/domain/library/library.service.ts b/server/src/domain/library/library.service.ts index ac4bd065d9..cdd5140f1b 100644 --- a/server/src/domain/library/library.service.ts +++ b/server/src/domain/library/library.service.ts @@ -94,9 +94,9 @@ export class LibraryService { async handleQueueCleanup(): Promise { this.logger.debug('Cleaning up any pending library deletions'); const pendingDeletion = await this.repository.getAllDeleted(); - for (const libraryToDelete of pendingDeletion) { - await this.jobRepository.queue({ name: JobName.LIBRARY_DELETE, data: { id: libraryToDelete.id } }); - } + await this.jobRepository.queueAll( + pendingDeletion.map((libraryToDelete) => ({ name: JobName.LIBRARY_DELETE, data: { id: libraryToDelete.id } })), + ); return true; } @@ -160,9 +160,9 @@ export class LibraryService { // TODO use pagination const assetIds = await this.repository.getAssetIds(job.id, true); this.logger.debug(`Will delete ${assetIds.length} asset(s) in library ${job.id}`); - for (const assetId of assetIds) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: assetId, fromExternal: true } }); - } + await this.jobRepository.queueAll( + assetIds.map((assetId) => ({ name: JobName.ASSET_DELETION, data: { id: assetId, fromExternal: true } })), + ); if (assetIds.length === 0) { this.logger.log(`Deleting library ${job.id}`); @@ -333,16 +333,16 @@ export class LibraryService { // Queue all library refresh const libraries = await this.repository.getAll(true, LibraryType.EXTERNAL); - for (const library of libraries) { - await this.jobRepository.queue({ + await this.jobRepository.queueAll( + libraries.map((library) => ({ name: JobName.LIBRARY_SCAN, data: { id: library.id, refreshModifiedFiles: !job.force, refreshAllFiles: job.force ?? false, }, - }); - } + })), + ); return true; } @@ -353,9 +353,9 @@ export class LibraryService { for await (const assets of assetPagination) { this.logger.debug(`Removing ${assets.length} offline assets`); - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.id, fromExternal: true } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.ASSET_DELETION, data: { id: asset.id, fromExternal: true } })), + ); } return true; @@ -411,16 +411,17 @@ export class LibraryService { this.logger.debug(`Will import ${filteredPaths.length} new asset(s)`); } - for (const assetPath of filteredPaths) { - const libraryJobData: ILibraryFileJob = { - id: job.id, - assetPath: path.normalize(assetPath), - ownerId: library.ownerId, - force: job.refreshAllFiles ?? false, - }; - - await this.jobRepository.queue({ name: JobName.LIBRARY_SCAN_ASSET, data: libraryJobData }); - } + await this.jobRepository.queueAll( + filteredPaths.map((assetPath) => ({ + name: JobName.LIBRARY_SCAN_ASSET, + data: { + id: job.id, + assetPath: path.normalize(assetPath), + ownerId: library.ownerId, + force: job.refreshAllFiles ?? false, + }, + })), + ); } await this.repository.update({ id: job.id, refreshedAt: new Date() }); diff --git a/server/src/domain/media/media.service.spec.ts b/server/src/domain/media/media.service.spec.ts index 2ab3def4f1..abc4fab583 100644 --- a/server/src/domain/media/media.service.spec.ts +++ b/server/src/domain/media/media.service.spec.ts @@ -77,17 +77,21 @@ describe(MediaService.name, () => { expect(assetMock.getAll).toHaveBeenCalled(); expect(assetMock.getWithout).not.toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_JPEG_THUMBNAIL, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_JPEG_THUMBNAIL, + data: { id: assetStub.image.id }, + }, + ]); expect(personMock.getAll).toHaveBeenCalled(); expect(personMock.getAllWithoutThumbnail).not.toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_PERSON_THUMBNAIL, - data: { id: personStub.newThumbnail.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_PERSON_THUMBNAIL, + data: { id: personStub.newThumbnail.id }, + }, + ]); }); it('should queue all people with missing thumbnail path', async () => { @@ -106,12 +110,14 @@ describe(MediaService.name, () => { expect(personMock.getAll).not.toHaveBeenCalled(); expect(personMock.getAllWithoutThumbnail).toHaveBeenCalled(); expect(personMock.getRandomFace).toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_PERSON_THUMBNAIL, - data: { - id: personStub.newThumbnail.id, + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_PERSON_THUMBNAIL, + data: { + id: personStub.newThumbnail.id, + }, }, - }); + ]); }); it('should queue all assets with missing resize path', async () => { @@ -125,10 +131,12 @@ describe(MediaService.name, () => { expect(assetMock.getAll).not.toHaveBeenCalled(); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_JPEG_THUMBNAIL, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_JPEG_THUMBNAIL, + data: { id: assetStub.image.id }, + }, + ]); expect(personMock.getAll).not.toHaveBeenCalled(); expect(personMock.getAllWithoutThumbnail).toHaveBeenCalled(); @@ -145,10 +153,12 @@ describe(MediaService.name, () => { expect(assetMock.getAll).not.toHaveBeenCalled(); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_WEBP_THUMBNAIL, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_WEBP_THUMBNAIL, + data: { id: assetStub.image.id }, + }, + ]); expect(personMock.getAll).not.toHaveBeenCalled(); expect(personMock.getAllWithoutThumbnail).toHaveBeenCalled(); @@ -165,10 +175,12 @@ describe(MediaService.name, () => { expect(assetMock.getAll).not.toHaveBeenCalled(); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_THUMBHASH_THUMBNAIL, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_THUMBHASH_THUMBNAIL, + data: { id: assetStub.image.id }, + }, + ]); expect(personMock.getAll).not.toHaveBeenCalled(); expect(personMock.getAllWithoutThumbnail).toHaveBeenCalled(); @@ -388,10 +400,12 @@ describe(MediaService.name, () => { expect(assetMock.getAll).toHaveBeenCalledWith({ skip: 0, take: 1000 }, { type: AssetType.VIDEO }); expect(assetMock.getWithout).not.toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.VIDEO_CONVERSION, - data: { id: assetStub.video.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.video.id }, + }, + ]); }); it('should queue all video assets without encoded videos', async () => { @@ -404,10 +418,12 @@ describe(MediaService.name, () => { expect(assetMock.getAll).not.toHaveBeenCalled(); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.ENCODED_VIDEO); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.VIDEO_CONVERSION, - data: { id: assetStub.video.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.VIDEO_CONVERSION, + data: { id: assetStub.video.id }, + }, + ]); }); }); diff --git a/server/src/domain/media/media.service.ts b/server/src/domain/media/media.service.ts index 6f16a7b66e..f3f0ccd1d3 100644 --- a/server/src/domain/media/media.service.ts +++ b/server/src/domain/media/media.service.ts @@ -21,6 +21,7 @@ import { IPersonRepository, IStorageRepository, ISystemConfigRepository, + JobItem, VideoCodecHWConfig, VideoStreamInfo, WithoutProperty, @@ -74,22 +75,27 @@ export class MediaService { }); for await (const assets of assetPagination) { + const jobs: JobItem[] = []; + for (const asset of assets) { if (!asset.resizePath || force) { - await this.jobRepository.queue({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: asset.id } }); + jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: asset.id } }); continue; } if (!asset.webpPath) { - await this.jobRepository.queue({ name: JobName.GENERATE_WEBP_THUMBNAIL, data: { id: asset.id } }); + jobs.push({ name: JobName.GENERATE_WEBP_THUMBNAIL, data: { id: asset.id } }); } if (!asset.thumbhash) { - await this.jobRepository.queue({ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: { id: asset.id } }); + jobs.push({ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: { id: asset.id } }); } } + + await this.jobRepository.queueAll(jobs); } const people = force ? await this.personRepository.getAll() : await this.personRepository.getAllWithoutThumbnail(); + const jobs: JobItem[] = []; for (const person of people) { if (!person.faceAssetId) { const face = await this.personRepository.getRandomFace(person.id); @@ -100,9 +106,11 @@ export class MediaService { await this.personRepository.update({ id: person.id, faceAssetId: face.assetId }); } - await this.jobRepository.queue({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } }); + jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: person.id } }); } + await this.jobRepository.queueAll(jobs); + return true; } @@ -118,15 +126,15 @@ export class MediaService { } for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.MIGRATE_ASSET, data: { id: asset.id } })), + ); } const people = await this.personRepository.getAll(); - for (const person of people) { - await this.jobRepository.queue({ name: JobName.MIGRATE_PERSON, data: { id: person.id } }); - } + await this.jobRepository.queueAll( + people.map((person) => ({ name: JobName.MIGRATE_PERSON, data: { id: person.id } })), + ); return true; } @@ -224,9 +232,9 @@ export class MediaService { }); for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.VIDEO_CONVERSION, data: { id: asset.id } })), + ); } return true; diff --git a/server/src/domain/metadata/metadata.service.spec.ts b/server/src/domain/metadata/metadata.service.spec.ts index fd2a29d45c..94bdce2562 100644 --- a/server/src/domain/metadata/metadata.service.spec.ts +++ b/server/src/domain/metadata/metadata.service.spec.ts @@ -208,10 +208,12 @@ describe(MetadataService.name, () => { await expect(sut.handleQueueMetadataExtraction({ force: false })).resolves.toBe(true); expect(assetMock.getWithout).toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.METADATA_EXTRACTION, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.METADATA_EXTRACTION, + data: { id: assetStub.image.id }, + }, + ]); }); it('should queue metadata extraction for all assets', async () => { @@ -219,10 +221,12 @@ describe(MetadataService.name, () => { await expect(sut.handleQueueMetadataExtraction({ force: true })).resolves.toBe(true); expect(assetMock.getAll).toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.METADATA_EXTRACTION, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.METADATA_EXTRACTION, + data: { id: assetStub.image.id }, + }, + ]); }); }); @@ -320,6 +324,7 @@ describe(MetadataService.name, () => { expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.livePhotoMotionAsset.id]); expect(storageMock.writeFile).not.toHaveBeenCalled(); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(assetMock.save).not.toHaveBeenCalledWith( expect.objectContaining({ assetType: AssetType.VIDEO, isVisible: false }), ); @@ -512,10 +517,12 @@ describe(MetadataService.name, () => { expect(assetMock.getWith).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithProperty.SIDECAR); expect(assetMock.getWithout).not.toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.SIDECAR_SYNC, - data: { id: assetStub.sidecar.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.SIDECAR_SYNC, + data: { id: assetStub.sidecar.id }, + }, + ]); }); it('should queue assets without sidecar files', async () => { @@ -525,10 +532,12 @@ describe(MetadataService.name, () => { expect(assetMock.getWithout).toHaveBeenCalledWith({ take: 1000, skip: 0 }, WithoutProperty.SIDECAR); expect(assetMock.getWith).not.toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.SIDECAR_DISCOVERY, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.SIDECAR_DISCOVERY, + data: { id: assetStub.image.id }, + }, + ]); }); }); diff --git a/server/src/domain/metadata/metadata.service.ts b/server/src/domain/metadata/metadata.service.ts index 544813d508..111b201ac1 100644 --- a/server/src/domain/metadata/metadata.service.ts +++ b/server/src/domain/metadata/metadata.service.ts @@ -196,9 +196,9 @@ export class MetadataService { }); for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.METADATA_EXTRACTION, data: { id: asset.id } })), + ); } return true; @@ -264,10 +264,12 @@ export class MetadataService { }); for await (const assets of assetPagination) { - for (const asset of assets) { - const name = force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY; - await this.jobRepository.queue({ name, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ + name: force ? JobName.SIDECAR_SYNC : JobName.SIDECAR_DISCOVERY, + data: { id: asset.id }, + })), + ); } return true; diff --git a/server/src/domain/person/person.service.spec.ts b/server/src/domain/person/person.service.spec.ts index 4ca718e417..ba576bc454 100644 --- a/server/src/domain/person/person.service.spec.ts +++ b/server/src/domain/person/person.service.spec.ts @@ -286,6 +286,7 @@ describe(PersonService.name, () => { expect(personMock.getById).toHaveBeenCalledWith('person-1'); expect(personMock.update).toHaveBeenCalledWith({ id: 'person-1', birthDate: new Date('1976-06-30') }); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(accessMock.person.checkOwnerAccess).toHaveBeenCalledWith(authStub.admin.user.id, new Set(['person-1'])); }); @@ -403,6 +404,7 @@ describe(PersonService.name, () => { }), ).rejects.toBeInstanceOf(BadRequestException); expect(jobMock.queue).not.toHaveBeenCalledWith(); + expect(jobMock.queueAll).not.toHaveBeenCalledWith(); }); it('should reassign a face', async () => { accessMock.person.checkOwnerAccess.mockResolvedValue(new Set([personStub.withName.id])); @@ -417,10 +419,12 @@ describe(PersonService.name, () => { }), ).resolves.toEqual([personStub.noName]); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_PERSON_THUMBNAIL, - data: { id: personStub.newThumbnail.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_PERSON_THUMBNAIL, + data: { id: personStub.newThumbnail.id }, + }, + ]); }); }); @@ -452,10 +456,12 @@ describe(PersonService.name, () => { it('should change person feature photo', async () => { personMock.getRandomFace.mockResolvedValue(faceStub.primaryFace1); await sut.createNewFeaturePhoto([personStub.newThumbnail.id]); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.GENERATE_PERSON_THUMBNAIL, - data: { id: personStub.newThumbnail.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.GENERATE_PERSON_THUMBNAIL, + data: { id: personStub.newThumbnail.id }, + }, + ]); }); }); @@ -480,6 +486,7 @@ describe(PersonService.name, () => { }); expect(jobMock.queue).not.toHaveBeenCalledWith(); + expect(jobMock.queueAll).not.toHaveBeenCalledWith(); }); it('should fail if user has not the correct permissions on the asset', async () => { @@ -495,6 +502,7 @@ describe(PersonService.name, () => { ).rejects.toBeInstanceOf(BadRequestException); expect(jobMock.queue).not.toHaveBeenCalledWith(); + expect(jobMock.queueAll).not.toHaveBeenCalledWith(); }); }); @@ -542,7 +550,9 @@ describe(PersonService.name, () => { await sut.handlePersonCleanup(); - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.PERSON_DELETE, data: { id: personStub.noName.id } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { name: JobName.PERSON_DELETE, data: { id: personStub.noName.id } }, + ]); }); }); @@ -552,6 +562,7 @@ describe(PersonService.name, () => { await expect(sut.handleQueueRecognizeFaces({})).resolves.toBe(true); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(configMock.load).toHaveBeenCalled(); }); @@ -563,10 +574,12 @@ describe(PersonService.name, () => { await sut.handleQueueRecognizeFaces({}); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.FACES); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.RECOGNIZE_FACES, - data: { id: assetStub.image.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.RECOGNIZE_FACES, + data: { id: assetStub.image.id }, + }, + ]); }); it('should queue all assets', async () => { @@ -580,14 +593,18 @@ describe(PersonService.name, () => { await sut.handleQueueRecognizeFaces({ force: true }); expect(assetMock.getAll).toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.RECOGNIZE_FACES, - data: { id: assetStub.image.id }, - }); - expect(jobMock.queue).toHaveBeenCalledWith({ - name: JobName.PERSON_DELETE, - data: { id: personStub.withName.id }, - }); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.RECOGNIZE_FACES, + data: { id: assetStub.image.id }, + }, + ]); + expect(jobMock.queueAll).toHaveBeenCalledWith([ + { + name: JobName.PERSON_DELETE, + data: { id: personStub.withName.id }, + }, + ]); }); }); @@ -644,6 +661,7 @@ describe(PersonService.name, () => { ); expect(personMock.createFace).not.toHaveBeenCalled(); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); expect(assetMock.upsertJobStatus).toHaveBeenCalledWith({ assetId: assetStub.image.id, diff --git a/server/src/domain/person/person.service.ts b/server/src/domain/person/person.service.ts index d63eda3cea..928685da08 100644 --- a/server/src/domain/person/person.service.ts +++ b/server/src/domain/person/person.service.ts @@ -22,6 +22,7 @@ import { ISmartInfoRepository, IStorageRepository, ISystemConfigRepository, + JobItem, UpdateFacesData, WithoutProperty, } from '../repositories'; @@ -153,6 +154,8 @@ export class PersonService { this.logger.debug( `Changing feature photos for ${changeFeaturePhoto.length} ${changeFeaturePhoto.length > 1 ? 'people' : 'person'}`, ); + + const jobs: JobItem[] = []; for (const personId of changeFeaturePhoto) { const assetFace = await this.repository.getRandomFace(personId); @@ -161,15 +164,11 @@ export class PersonService { id: personId, faceAssetId: assetFace.id, }); - - await this.jobRepository.queue({ - name: JobName.GENERATE_PERSON_THUMBNAIL, - data: { - id: personId, - }, - }); + jobs.push({ name: JobName.GENERATE_PERSON_THUMBNAIL, data: { id: personId } }); } } + + await this.jobRepository.queueAll(jobs); } async getById(auth: AuthDto, id: string): Promise { @@ -270,8 +269,10 @@ export class PersonService { const people = await this.repository.getAllWithoutFaces(); for (const person of people) { this.logger.debug(`Person ${person.name || person.id} no longer has any faces, deleting.`); - await this.jobRepository.queue({ name: JobName.PERSON_DELETE, data: { id: person.id } }); } + await this.jobRepository.queueAll( + people.map((person) => ({ name: JobName.PERSON_DELETE, data: { id: person.id } })), + ); return true; } @@ -290,16 +291,16 @@ export class PersonService { if (force) { const people = await this.repository.getAll(); - for (const person of people) { - await this.jobRepository.queue({ name: JobName.PERSON_DELETE, data: { id: person.id } }); - } + await this.jobRepository.queueAll( + people.map((person) => ({ name: JobName.PERSON_DELETE, data: { id: person.id } })), + ); this.logger.debug(`Deleted ${people.length} people`); } for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.RECOGNIZE_FACES, data: { id: asset.id } }); - } + await this.jobRepository.queueAll( + assets.map((asset) => ({ name: JobName.RECOGNIZE_FACES, data: { id: asset.id } })), + ); } return true; @@ -333,7 +334,7 @@ export class PersonService { for (const { embedding, ...rest } of faces) { const matches = await this.smartInfoRepository.searchFaces({ - ownerId: asset.ownerId, + userIds: [asset.ownerId], embedding, numResults: 1, maxDistance: machineLearning.facialRecognition.maxDistance, diff --git a/server/src/domain/repositories/asset.repository.ts b/server/src/domain/repositories/asset.repository.ts index 48f83de37b..296d4f40cf 100644 --- a/server/src/domain/repositories/asset.repository.ts +++ b/server/src/domain/repositories/asset.repository.ts @@ -199,5 +199,5 @@ export interface IAssetRepository { search(options: AssetSearchOptions): Promise; getAssetIdByCity(userId: string, options: AssetExploreFieldOptions): Promise>; getAssetIdByTag(userId: string, options: AssetExploreFieldOptions): Promise>; - searchMetadata(query: string, userId: string, options: MetadataSearchOptions): Promise; + searchMetadata(query: string, userIds: string[], options: MetadataSearchOptions): Promise; } diff --git a/server/src/domain/repositories/job.repository.ts b/server/src/domain/repositories/job.repository.ts index bd6982ca19..faa78adb06 100644 --- a/server/src/domain/repositories/job.repository.ts +++ b/server/src/domain/repositories/job.repository.ts @@ -103,6 +103,7 @@ export interface IJobRepository { deleteCronJob(name: string): void; setConcurrency(queueName: QueueName, concurrency: number): void; queue(item: JobItem): Promise; + queueAll(items: JobItem[]): Promise; pause(name: QueueName): Promise; resume(name: QueueName): Promise; empty(name: QueueName): Promise; diff --git a/server/src/domain/repositories/smart-info.repository.ts b/server/src/domain/repositories/smart-info.repository.ts index f26061f1b0..c35ec1d84c 100644 --- a/server/src/domain/repositories/smart-info.repository.ts +++ b/server/src/domain/repositories/smart-info.repository.ts @@ -5,7 +5,7 @@ export const ISmartInfoRepository = 'ISmartInfoRepository'; export type Embedding = number[]; export interface EmbeddingSearch { - ownerId: string; + userIds: string[]; embedding: Embedding; numResults: number; maxDistance?: number; diff --git a/server/src/domain/search/search.service.spec.ts b/server/src/domain/search/search.service.spec.ts index c6fe2abf66..9541d8f1d4 100644 --- a/server/src/domain/search/search.service.spec.ts +++ b/server/src/domain/search/search.service.spec.ts @@ -4,6 +4,7 @@ import { authStub, newAssetRepositoryMock, newMachineLearningRepositoryMock, + newPartnerRepositoryMock, newPersonRepositoryMock, newSmartInfoRepositoryMock, newSystemConfigRepositoryMock, @@ -13,6 +14,7 @@ import { mapAsset } from '../asset'; import { IAssetRepository, IMachineLearningRepository, + IPartnerRepository, IPersonRepository, ISmartInfoRepository, ISystemConfigRepository, @@ -29,6 +31,7 @@ describe(SearchService.name, () => { let machineMock: jest.Mocked; let personMock: jest.Mocked; let smartInfoMock: jest.Mocked; + let partnerMock: jest.Mocked; beforeEach(() => { assetMock = newAssetRepositoryMock(); @@ -36,7 +39,8 @@ describe(SearchService.name, () => { machineMock = newMachineLearningRepositoryMock(); personMock = newPersonRepositoryMock(); smartInfoMock = newSmartInfoRepositoryMock(); - sut = new SearchService(configMock, machineMock, personMock, smartInfoMock, assetMock); + partnerMock = newPartnerRepositoryMock(); + sut = new SearchService(configMock, machineMock, personMock, smartInfoMock, assetMock, partnerMock); }); it('should work', () => { @@ -87,6 +91,7 @@ describe(SearchService.name, () => { it('should search by metadata if `clip` option is false', async () => { const dto: SearchDto = { q: 'test query', clip: false }; assetMock.searchMetadata.mockResolvedValueOnce([assetStub.image]); + partnerMock.getAll.mockResolvedValueOnce([]); const expectedResponse = { albums: { total: 0, @@ -105,7 +110,7 @@ describe(SearchService.name, () => { const result = await sut.search(authStub.user1, dto); expect(result).toEqual(expectedResponse); - expect(assetMock.searchMetadata).toHaveBeenCalledWith(dto.q, authStub.user1.user.id, { numResults: 250 }); + expect(assetMock.searchMetadata).toHaveBeenCalledWith(dto.q, [authStub.user1.user.id], { numResults: 250 }); expect(smartInfoMock.searchCLIP).not.toHaveBeenCalled(); }); @@ -114,6 +119,7 @@ describe(SearchService.name, () => { const embedding = [1, 2, 3]; smartInfoMock.searchCLIP.mockResolvedValueOnce([assetStub.image]); machineMock.encodeText.mockResolvedValueOnce(embedding); + partnerMock.getAll.mockResolvedValueOnce([]); const expectedResponse = { albums: { total: 0, @@ -133,7 +139,7 @@ describe(SearchService.name, () => { expect(result).toEqual(expectedResponse); expect(smartInfoMock.searchCLIP).toHaveBeenCalledWith({ - ownerId: authStub.user1.user.id, + userIds: [authStub.user1.user.id], embedding, numResults: 100, }); diff --git a/server/src/domain/search/search.service.ts b/server/src/domain/search/search.service.ts index 0bceb43578..ef5a42fe46 100644 --- a/server/src/domain/search/search.service.ts +++ b/server/src/domain/search/search.service.ts @@ -7,6 +7,7 @@ import { PersonResponseDto } from '../person'; import { IAssetRepository, IMachineLearningRepository, + IPartnerRepository, IPersonRepository, ISmartInfoRepository, ISystemConfigRepository, @@ -28,6 +29,7 @@ export class SearchService { @Inject(IPersonRepository) private personRepository: IPersonRepository, @Inject(ISmartInfoRepository) private smartInfoRepository: ISmartInfoRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository, + @Inject(IPartnerRepository) private partnerRepository: IPartnerRepository, ) { this.configCore = SystemConfigCore.create(configRepository); } @@ -64,6 +66,7 @@ export class SearchService { throw new Error('CLIP is not enabled'); } const strategy = dto.clip ? SearchStrategy.CLIP : SearchStrategy.TEXT; + const userIds = await this.getUserIdsToSearch(auth); let assets: AssetEntity[] = []; @@ -74,10 +77,10 @@ export class SearchService { { text: query }, machineLearning.clip, ); - assets = await this.smartInfoRepository.searchCLIP({ ownerId: auth.user.id, embedding, numResults: 100 }); + assets = await this.smartInfoRepository.searchCLIP({ userIds: userIds, embedding, numResults: 100 }); break; case SearchStrategy.TEXT: - assets = await this.assetRepository.searchMetadata(query, auth.user.id, { numResults: 250 }); + assets = await this.assetRepository.searchMetadata(query, userIds, { numResults: 250 }); default: break; } @@ -97,4 +100,14 @@ export class SearchService { }, }; } + + private async getUserIdsToSearch(auth: AuthDto): Promise { + const userIds: string[] = [auth.user.id]; + const partners = await this.partnerRepository.getAll(auth.user.id); + const partnersIds = partners + .filter((partner) => partner.sharedBy && partner.inTimeline) + .map((partner) => partner.sharedById); + userIds.push(...partnersIds); + return userIds; + } } diff --git a/server/src/domain/smart-info/smart-info.service.spec.ts b/server/src/domain/smart-info/smart-info.service.spec.ts index 9686e6ac63..7dd21d7a53 100644 --- a/server/src/domain/smart-info/smart-info.service.spec.ts +++ b/server/src/domain/smart-info/smart-info.service.spec.ts @@ -69,7 +69,7 @@ describe(SmartInfoService.name, () => { await sut.handleQueueEncodeClip({ force: false }); - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } }]); expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.CLIP_ENCODING); }); @@ -81,7 +81,7 @@ describe(SmartInfoService.name, () => { await sut.handleQueueEncodeClip({ force: true }); - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.ENCODE_CLIP, data: { id: assetStub.image.id } }]); expect(assetMock.getAll).toHaveBeenCalled(); }); }); diff --git a/server/src/domain/smart-info/smart-info.service.ts b/server/src/domain/smart-info/smart-info.service.ts index 5f6cf9b40f..0a7876cd52 100644 --- a/server/src/domain/smart-info/smart-info.service.ts +++ b/server/src/domain/smart-info/smart-info.service.ts @@ -64,9 +64,7 @@ export class SmartInfoService { }); for await (const assets of assetPagination) { - for (const asset of assets) { - await this.jobRepository.queue({ name: JobName.ENCODE_CLIP, data: { id: asset.id } }); - } + await this.jobRepository.queueAll(assets.map((asset) => ({ name: JobName.ENCODE_CLIP, data: { id: asset.id } }))); } return true; diff --git a/server/src/domain/user/user.core.ts b/server/src/domain/user/user.core.ts index 240869b863..b19ee5e84f 100644 --- a/server/src/domain/user/user.core.ts +++ b/server/src/domain/user/user.core.ts @@ -97,7 +97,7 @@ export class UserCore { payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS); } if (payload.storageLabel) { - payload.storageLabel = sanitize(payload.storageLabel); + payload.storageLabel = sanitize(payload.storageLabel.replace(/\./g, '')); } const userEntity = await this.userRepository.create(payload); await this.libraryRepository.create({ diff --git a/server/src/domain/user/user.service.spec.ts b/server/src/domain/user/user.service.spec.ts index 5410beb10f..45fe4a9025 100644 --- a/server/src/domain/user/user.service.spec.ts +++ b/server/src/domain/user/user.service.spec.ts @@ -342,7 +342,7 @@ describe(UserService.name, () => { userMock.update.mockResolvedValue({ ...userStub.admin, profileImagePath: file.path }); await sut.createProfileImage(authStub.admin, file); - await expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.DELETE_FILES, data: { files } }]]); + expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.DELETE_FILES, data: { files } }]]); }); it('should not delete the profile image if it has not been set', async () => { @@ -352,6 +352,7 @@ describe(UserService.name, () => { await sut.createProfileImage(authStub.admin, file); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); }); @@ -361,6 +362,7 @@ describe(UserService.name, () => { await expect(sut.deleteProfileImage(authStub.admin)).rejects.toBeInstanceOf(BadRequestException); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).not.toHaveBeenCalled(); }); it('should delete the profile image if user has one', async () => { @@ -368,7 +370,7 @@ describe(UserService.name, () => { const files = [userStub.profilePath.profileImagePath]; await sut.deleteProfileImage(authStub.admin); - await expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.DELETE_FILES, data: { files } }]]); + expect(jobMock.queue.mock.calls).toEqual([[{ name: JobName.DELETE_FILES, data: { files } }]]); }); }); @@ -456,6 +458,7 @@ describe(UserService.name, () => { expect(userMock.getDeletedUsers).toHaveBeenCalled(); expect(jobMock.queue).not.toHaveBeenCalled(); + expect(jobMock.queueAll).toHaveBeenCalledWith([]); }); it('should queue user ready for deletion', async () => { @@ -465,7 +468,7 @@ describe(UserService.name, () => { await sut.handleUserDeleteCheck(); expect(userMock.getDeletedUsers).toHaveBeenCalled(); - expect(jobMock.queue).toHaveBeenCalledWith({ name: JobName.USER_DELETION, data: { id: user.id } }); + expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.USER_DELETION, data: { id: user.id } }]); }); }); diff --git a/server/src/domain/user/user.service.ts b/server/src/domain/user/user.service.ts index 85380ca2a1..8185a83c64 100644 --- a/server/src/domain/user/user.service.ts +++ b/server/src/domain/user/user.service.ts @@ -129,12 +129,11 @@ export class UserService { async handleUserDeleteCheck() { const users = await this.userRepository.getDeletedUsers(); - for (const user of users) { - if (this.isReadyForDeletion(user)) { - await this.jobRepository.queue({ name: JobName.USER_DELETION, data: { id: user.id } }); - } - } - + await this.jobRepository.queueAll( + users.flatMap((user) => + this.isReadyForDeletion(user) ? [{ name: JobName.USER_DELETION, data: { id: user.id } }] : [], + ), + ); return true; } diff --git a/server/src/infra/repositories/asset.repository.ts b/server/src/infra/repositories/asset.repository.ts index ca3ca685a7..e56a2827bc 100644 --- a/server/src/infra/repositories/asset.repository.ts +++ b/server/src/infra/repositories/asset.repository.ts @@ -804,10 +804,14 @@ export class AssetRepository implements IAssetRepository { return builder; } - @GenerateSql({ params: [DummyValue.STRING, DummyValue.UUID, { numResults: 250 }] }) - async searchMetadata(query: string, ownerId: string, { numResults }: MetadataSearchOptions): Promise { + @GenerateSql({ params: [DummyValue.STRING, [DummyValue.UUID], { numResults: 250 }] }) + async searchMetadata( + query: string, + userIds: string[], + { numResults }: MetadataSearchOptions, + ): Promise { const rows = await this.getBuilder({ - userIds: [ownerId], + userIds: userIds, exifInfo: false, isArchived: false, }) diff --git a/server/src/infra/repositories/job.repository.ts b/server/src/infra/repositories/job.repository.ts index 61238fac78..9152120cdd 100644 --- a/server/src/infra/repositories/job.repository.ts +++ b/server/src/infra/repositories/job.repository.ts @@ -34,7 +34,7 @@ export class JobRepository implements IJobRepository { } addCronJob(name: string, expression: string, onTick: () => void, start = true): void { - const job = new CronJob( + const job = new CronJob( expression, onTick, // function to run onComplete @@ -116,12 +116,31 @@ export class JobRepository implements IJobRepository { ) as unknown as Promise; } - async queue(item: JobItem): Promise { - const jobName = item.name; - const jobData = (item as { data?: any })?.data || {}; - const jobOptions = this.getJobOptions(item) || undefined; + async queueAll(items: JobItem[]): Promise { + if (!items.length) { + return; + } - await this.getQueue(JOBS_TO_QUEUE[jobName]).add(jobName, jobData, jobOptions); + const itemsByQueue = items.reduce>((acc, item) => { + const queueName = JOBS_TO_QUEUE[item.name]; + acc[queueName] = acc[queueName] || []; + acc[queueName].push(item); + return acc; + }, {}); + + for (const [queueName, items] of Object.entries(itemsByQueue)) { + const queue = this.getQueue(queueName as QueueName); + const jobs = items.map((item) => ({ + name: item.name, + data: (item as { data?: any })?.data || {}, + options: this.getJobOptions(item) || undefined, + })); + await queue.addBulk(jobs); + } + } + + async queue(item: JobItem): Promise { + await this.queueAll([item]); } private getJobOptions(item: JobItem): JobsOptions | null { diff --git a/server/src/infra/repositories/smart-info.repository.ts b/server/src/infra/repositories/smart-info.repository.ts index dc7d5a2db6..ae8bea2d09 100644 --- a/server/src/infra/repositories/smart-info.repository.ts +++ b/server/src/infra/repositories/smart-info.repository.ts @@ -41,9 +41,9 @@ export class SmartInfoRepository implements ISmartInfoRepository { } @GenerateSql({ - params: [{ ownerId: DummyValue.UUID, embedding: Array.from({ length: 512 }, Math.random), numResults: 100 }], + params: [{ userIds: [DummyValue.UUID], embedding: Array.from({ length: 512 }, Math.random), numResults: 100 }], }) - async searchCLIP({ ownerId, embedding, numResults }: EmbeddingSearch): Promise { + async searchCLIP({ userIds, embedding, numResults }: EmbeddingSearch): Promise { if (!isValidInteger(numResults, { min: 1 })) { throw new Error(`Invalid value for 'numResults': ${numResults}`); } @@ -55,13 +55,13 @@ export class SmartInfoRepository implements ISmartInfoRepository { results = await manager .createQueryBuilder(AssetEntity, 'a') .innerJoin('a.smartSearch', 's') - .where('a.ownerId = :ownerId') + .where('a.ownerId IN (:...userIds )') .andWhere('a.isVisible = true') .andWhere('a.isArchived = false') .andWhere('a.fileCreatedAt < NOW()') .leftJoinAndSelect('a.exifInfo', 'e') .orderBy('s.embedding <=> :embedding') - .setParameters({ ownerId, embedding: asVector(embedding) }) + .setParameters({ userIds, embedding: asVector(embedding) }) .limit(numResults) .getMany(); }); @@ -72,14 +72,14 @@ export class SmartInfoRepository implements ISmartInfoRepository { @GenerateSql({ params: [ { - ownerId: DummyValue.UUID, + userIds: [DummyValue.UUID], embedding: Array.from({ length: 512 }, Math.random), numResults: 100, maxDistance: 0.6, }, ], }) - async searchFaces({ ownerId, embedding, numResults, maxDistance }: EmbeddingSearch): Promise { + async searchFaces({ userIds, embedding, numResults, maxDistance }: EmbeddingSearch): Promise { if (!isValidInteger(numResults, { min: 1 })) { throw new Error(`Invalid value for 'numResults': ${numResults}`); } @@ -91,9 +91,9 @@ export class SmartInfoRepository implements ISmartInfoRepository { .createQueryBuilder(AssetFaceEntity, 'faces') .select('1 + (faces.embedding <=> :embedding)', 'distance') .innerJoin('faces.asset', 'asset') - .where('asset.ownerId = :ownerId') + .where('asset.ownerId IN (:...userIds )') .orderBy('1 + (faces.embedding <=> :embedding)') - .setParameters({ ownerId, embedding: asVector(embedding) }) + .setParameters({ userIds, embedding: asVector(embedding) }) .limit(numResults); this.faceColumns.forEach((col) => cte.addSelect(`faces.${col}`, col)); diff --git a/server/src/infra/sql/smart.info.repository.sql b/server/src/infra/sql/smart.info.repository.sql index b2cb551a61..56a4738281 100644 --- a/server/src/infra/sql/smart.info.repository.sql +++ b/server/src/infra/sql/smart.info.repository.sql @@ -69,7 +69,7 @@ FROM LEFT JOIN "exif" "e" ON "e"."assetId" = "a"."id" WHERE ( - "a"."ownerId" = $1 + "a"."ownerId" IN ($1) AND "a"."isVisible" = true AND "a"."isArchived" = false AND "a"."fileCreatedAt" < NOW() @@ -103,7 +103,7 @@ WITH INNER JOIN "assets" "asset" ON "asset"."id" = "faces"."assetId" AND ("asset"."deletedAt" IS NULL) WHERE - "asset"."ownerId" = $2 + "asset"."ownerId" IN ($2) ORDER BY 1 + ("faces"."embedding" <= > $3) ASC LIMIT diff --git a/server/test/repositories/job.repository.mock.ts b/server/test/repositories/job.repository.mock.ts index 967c6a8040..977e73f837 100644 --- a/server/test/repositories/job.repository.mock.ts +++ b/server/test/repositories/job.repository.mock.ts @@ -11,6 +11,7 @@ export const newJobRepositoryMock = (): jest.Mocked => { 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(), diff --git a/server/test/test-utils.ts b/server/test/test-utils.ts index 0a64132e41..5356d83bff 100644 --- a/server/test/test-utils.ts +++ b/server/test/test-utils.ts @@ -77,6 +77,7 @@ export const testApp = { deleteCronJob: jest.fn(), validateCronExpression: jest.fn(), queue: (item: JobItem) => jobs && _handler(item), + queueAll: (items: JobItem[]) => jobs && Promise.all(items.map(_handler)).then(() => Promise.resolve()), resume: jest.fn(), empty: jest.fn(), setConcurrency: jest.fn(), diff --git a/web/package-lock.json b/web/package-lock.json index 998b483f37..25fc901079 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -21,7 +21,6 @@ "luxon": "^3.2.1", "maplibre-gl": "^3.6.0", "socket.io-client": "^4.6.1", - "svelte-loading-spinners": "^0.3.4", "svelte-local-storage-store": "^0.6.0", "svelte-maplibre": "^0.7.0", "thumbhash": "^0.1.1" @@ -6862,11 +6861,6 @@ "svelte": "^3.19.0 || ^4.0.0" } }, - "node_modules/svelte-loading-spinners": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/svelte-loading-spinners/-/svelte-loading-spinners-0.3.4.tgz", - "integrity": "sha512-vKaW71QMCBcTNijAGc0mUl8k3DQ66iYmp6MB8BMGCXyWk82bTrcLy8FOnSm9fE+8q6TwzD6PLUoYFHt0II93Xw==" - }, "node_modules/svelte-local-storage-store": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/svelte-local-storage-store/-/svelte-local-storage-store-0.6.4.tgz", diff --git a/web/package.json b/web/package.json index 862ef621b7..d2a66a5f27 100644 --- a/web/package.json +++ b/web/package.json @@ -67,7 +67,6 @@ "luxon": "^3.2.1", "maplibre-gl": "^3.6.0", "socket.io-client": "^4.6.1", - "svelte-loading-spinners": "^0.3.4", "svelte-local-storage-store": "^0.6.0", "svelte-maplibre": "^0.7.0", "thumbhash": "^0.1.1" diff --git a/web/src/lib/components/shared-components/loading-spinner.svelte b/web/src/lib/components/shared-components/loading-spinner.svelte index a285f5b480..7835e17310 100644 --- a/web/src/lib/components/shared-components/loading-spinner.svelte +++ b/web/src/lib/components/shared-components/loading-spinner.svelte @@ -1,7 +1,13 @@ + +
{library.name} {#if totalCount[index] == undefined} - + {:else} diff --git a/web/src/routes/(user)/share/[key]/+page.svelte b/web/src/routes/(user)/share/[key]/+page.svelte index 9a4160ebfb..d45d1b06fc 100644 --- a/web/src/routes/(user)/share/[key]/+page.svelte +++ b/web/src/routes/(user)/share/[key]/+page.svelte @@ -58,8 +58,10 @@ Please enter the password to view this page.
- - +
+ + +