diff --git a/cli/Dockerfile b/cli/Dockerfile
index b112382cbb..c4b99869c6 100644
--- a/cli/Dockerfile
+++ b/cli/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:22.10.0-alpine3.20@sha256:fc95a044b87e95507c60c1f8c829e5d98ddf46401034932499db370c494ef0ff AS core
+FROM node:22.11.0-alpine3.20@sha256:f265794478aa0b1a23d85a492c8311ed795bc527c3fe7e43453b3c872dcd71a3 AS core
WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
diff --git a/cli/package-lock.json b/cli/package-lock.json
index 7f691935da..fe18b93b18 100644
--- a/cli/package-lock.json
+++ b/cli/package-lock.json
@@ -24,7 +24,7 @@
"@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitest/coverage-v8": "^2.0.5",
@@ -59,7 +59,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"typescript": "^5.3.3"
}
},
@@ -1378,9 +1378,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.8.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz",
- "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/cli/package.json b/cli/package.json
index 7e1eaa8d1c..0f9356b4bf 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -20,7 +20,7 @@
"@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitest/coverage-v8": "^2.0.5",
diff --git a/e2e/package-lock.json b/e2e/package-lock.json
index 24f3bfdeee..bb46924e3e 100644
--- a/e2e/package-lock.json
+++ b/e2e/package-lock.json
@@ -15,7 +15,7 @@
"@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@types/oidc-provider": "^8.5.1",
"@types/pg": "^8.11.0",
"@types/pngjs": "^6.0.4",
@@ -64,7 +64,7 @@
"@types/cli-progress": "^3.11.0",
"@types/lodash-es": "^4.17.12",
"@types/mock-fs": "^4.13.1",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitest/coverage-v8": "^2.0.5",
@@ -99,7 +99,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"typescript": "^5.3.3"
}
},
@@ -1613,9 +1613,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "22.8.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz",
- "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3294,9 +3294,9 @@
}
},
"node_modules/exiftool-vendored": {
- "version": "28.6.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.6.0.tgz",
- "integrity": "sha512-Cx8/8ov1tKEacHhsi7FNYdisIhKq/SeQfprYSpYzwBuJwkPmCV8w7tTIvUJRQX9rvopXhBA4eBf1FPXqTZW5vA==",
+ "version": "28.7.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.7.0.tgz",
+ "integrity": "sha512-0zoq6kBS1yPjzJs+p0qZDinWEA72PTKoRk5ETYKfmeRcZAkhv83Y3HCpbb/LdgJJywfm8BcIJGezrBHvL7dVnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3307,14 +3307,14 @@
"luxon": "^3.5.0"
},
"optionalDependencies": {
- "exiftool-vendored.exe": "12.97.0",
- "exiftool-vendored.pl": "12.97.0"
+ "exiftool-vendored.exe": "12.99.0",
+ "exiftool-vendored.pl": "12.99.0"
}
},
"node_modules/exiftool-vendored.exe": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.97.0.tgz",
- "integrity": "sha512-+HxyFigEJOtwRjP7PhEslhZKuVW2V0hvmHPHtbVtNKGfAUGcfc95xNTjASQfKJvc+9ZuvzdEBPkEQmyA/ZYdIw==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.99.0.tgz",
+ "integrity": "sha512-ffpJHCzC9OYJqw4JlPNtCwRy02jwhmnSJEF/QqEjpuIWDEnlRBQP/yWRh1Nw21K1R4FB4yG5PlCgEDu09VQz/w==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -3323,9 +3323,9 @@
]
},
"node_modules/exiftool-vendored.pl": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.97.0.tgz",
- "integrity": "sha512-mXe9JEH3csfyPWcC7+H6IpfaokDMMr4S45n7MtiobGPdeeh+kFnf1SQ9cxg4sF403P6IKVeYYPbzgKMlpro9eQ==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.99.0.tgz",
+ "integrity": "sha512-qRVEPQxtoerXF+izJ0O7jGAr5o0Uyvnyu7ao5DTKzF+V7Fv3SurE0l43oCeZPFKo/Ld4V7vEylhFCm4IHVZKWA==",
"dev": true,
"license": "MIT",
"optional": true,
diff --git a/e2e/package.json b/e2e/package.json
index 86488e8a70..42043fd6b2 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -25,7 +25,7 @@
"@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@types/oidc-provider": "^8.5.1",
"@types/pg": "^8.11.0",
"@types/pngjs": "^6.0.4",
diff --git a/machine-learning/poetry.lock b/machine-learning/poetry.lock
index a2da285750..32a2b73ffc 100644
--- a/machine-learning/poetry.lock
+++ b/machine-learning/poetry.lock
@@ -946,13 +946,13 @@ tqdm = ["tqdm"]
[[package]]
name = "ftfy"
-version = "6.3.0"
+version = "6.3.1"
description = "Fixes mojibake and other problems with Unicode, after the fact"
optional = false
python-versions = ">=3.9"
files = [
- {file = "ftfy-6.3.0-py3-none-any.whl", hash = "sha256:17aca296801f44142e3ff2c16f93fbf6a87609ebb3704a9a41dd5d4903396caf"},
- {file = "ftfy-6.3.0.tar.gz", hash = "sha256:1c7d6418e72b25a7760feb150acf574b86924dbb2e95b32c0b3abbd1ba3d7ad6"},
+ {file = "ftfy-6.3.1-py3-none-any.whl", hash = "sha256:7c70eb532015cd2f9adb53f101fb6c7945988d023a085d127d1573dc49dd0083"},
+ {file = "ftfy-6.3.1.tar.gz", hash = "sha256:9b3c3d90f84fb267fe64d375a07b7f8912d817cf86009ae134aa03e1819506ec"},
]
[package.dependencies]
@@ -1609,13 +1609,13 @@ test = ["pytest (>=7.4)", "pytest-cov (>=4.1)"]
[[package]]
name = "locust"
-version = "2.32.0"
+version = "2.32.1"
description = "Developer-friendly load testing framework"
optional = false
python-versions = ">=3.9"
files = [
- {file = "locust-2.32.0-py3-none-any.whl", hash = "sha256:e004514332b8631ca91382d11d224baee4ced040c5f5c8b2233800ebcbc73c0e"},
- {file = "locust-2.32.0.tar.gz", hash = "sha256:d8f7f5d9d4e801b2e7b0ee3f31109333673da744ccedf85e7da0151f2d263dd9"},
+ {file = "locust-2.32.1-py3-none-any.whl", hash = "sha256:3fb5548b4f2b6477fa5229ee55ac3dddbae56e86c3430bf2ba3fee358eb7e7bb"},
+ {file = "locust-2.32.1.tar.gz", hash = "sha256:8c3b1094dbf20860fd2f6e26b68f0c6064dc28054f4462664389d102fce1448b"},
]
[package.dependencies]
@@ -2749,13 +2749,13 @@ cli = ["click (>=5.0)"]
[[package]]
name = "python-multipart"
-version = "0.0.12"
+version = "0.0.17"
description = "A streaming multipart parser for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "python_multipart-0.0.12-py3-none-any.whl", hash = "sha256:43dcf96cf65888a9cd3423544dd0d75ac10f7aa0c3c28a175bbcd00c9ce1aebf"},
- {file = "python_multipart-0.0.12.tar.gz", hash = "sha256:045e1f98d719c1ce085ed7f7e1ef9d8ccc8c02ba02b5566d5f7521410ced58cb"},
+ {file = "python_multipart-0.0.17-py3-none-any.whl", hash = "sha256:15dc4f487e0a9476cc1201261188ee0940165cffc94429b6fc565c4d3045cb5d"},
+ {file = "python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538"},
]
[[package]]
diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json
index 74bac2b924..53a2e76cb4 100644
--- a/open-api/typescript-sdk/package-lock.json
+++ b/open-api/typescript-sdk/package-lock.json
@@ -12,7 +12,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"typescript": "^5.3.3"
}
},
@@ -22,9 +22,9 @@
"integrity": "sha512-8tKiYffhwTGHSHYGnZ3oneLGCjX0po/XAXQ5Ng9fqKkvIdl/xz8+Vh8i+6xjzZqvZ2pLVpUcuSfnvNI/x67L0g=="
},
"node_modules/@types/node": {
- "version": "22.8.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz",
- "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json
index 726c63da8e..9df9068897 100644
--- a/open-api/typescript-sdk/package.json
+++ b/open-api/typescript-sdk/package.json
@@ -19,7 +19,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"typescript": "^5.3.3"
},
"repository": {
diff --git a/server/Dockerfile b/server/Dockerfile
index f14178dd9f..f0223cdc5d 100644
--- a/server/Dockerfile
+++ b/server/Dockerfile
@@ -25,7 +25,7 @@ COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img
COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl
# web build
-FROM node:22.10.0-alpine3.20@sha256:fc95a044b87e95507c60c1f8c829e5d98ddf46401034932499db370c494ef0ff AS web
+FROM node:22.11.0-alpine3.20@sha256:f265794478aa0b1a23d85a492c8311ed795bc527c3fe7e43453b3c872dcd71a3 AS web
WORKDIR /usr/src/open-api/typescript-sdk
COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./
diff --git a/server/package-lock.json b/server/package-lock.json
index eb428e2eac..85b08523d6 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -83,7 +83,7 @@
"@types/lodash": "^4.14.197",
"@types/mock-fs": "^4.13.1",
"@types/multer": "^1.4.7",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@types/nodemailer": "^6.4.14",
"@types/picomatch": "^3.0.0",
"@types/pngjs": "^6.0.5",
@@ -5110,9 +5110,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.8.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz",
- "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dependencies": {
"undici-types": "~6.19.8"
}
@@ -8236,9 +8236,9 @@
}
},
"node_modules/exiftool-vendored": {
- "version": "28.6.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.6.0.tgz",
- "integrity": "sha512-Cx8/8ov1tKEacHhsi7FNYdisIhKq/SeQfprYSpYzwBuJwkPmCV8w7tTIvUJRQX9rvopXhBA4eBf1FPXqTZW5vA==",
+ "version": "28.7.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.7.0.tgz",
+ "integrity": "sha512-0zoq6kBS1yPjzJs+p0qZDinWEA72PTKoRk5ETYKfmeRcZAkhv83Y3HCpbb/LdgJJywfm8BcIJGezrBHvL7dVnQ==",
"dependencies": {
"@photostructure/tz-lookup": "^11.0.0",
"@types/luxon": "^3.4.2",
@@ -8247,23 +8247,23 @@
"luxon": "^3.5.0"
},
"optionalDependencies": {
- "exiftool-vendored.exe": "12.97.0",
- "exiftool-vendored.pl": "12.97.0"
+ "exiftool-vendored.exe": "12.99.0",
+ "exiftool-vendored.pl": "12.99.0"
}
},
"node_modules/exiftool-vendored.exe": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.97.0.tgz",
- "integrity": "sha512-+HxyFigEJOtwRjP7PhEslhZKuVW2V0hvmHPHtbVtNKGfAUGcfc95xNTjASQfKJvc+9ZuvzdEBPkEQmyA/ZYdIw==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.99.0.tgz",
+ "integrity": "sha512-ffpJHCzC9OYJqw4JlPNtCwRy02jwhmnSJEF/QqEjpuIWDEnlRBQP/yWRh1Nw21K1R4FB4yG5PlCgEDu09VQz/w==",
"optional": true,
"os": [
"win32"
]
},
"node_modules/exiftool-vendored.pl": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.97.0.tgz",
- "integrity": "sha512-mXe9JEH3csfyPWcC7+H6IpfaokDMMr4S45n7MtiobGPdeeh+kFnf1SQ9cxg4sF403P6IKVeYYPbzgKMlpro9eQ==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.99.0.tgz",
+ "integrity": "sha512-qRVEPQxtoerXF+izJ0O7jGAr5o0Uyvnyu7ao5DTKzF+V7Fv3SurE0l43oCeZPFKo/Ld4V7vEylhFCm4IHVZKWA==",
"optional": true,
"os": [
"!win32"
@@ -18258,9 +18258,9 @@
}
},
"@types/node": {
- "version": "22.8.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz",
- "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==",
+ "version": "22.9.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+ "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"requires": {
"undici-types": "~6.19.8"
}
@@ -20579,29 +20579,29 @@
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
"exiftool-vendored": {
- "version": "28.6.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.6.0.tgz",
- "integrity": "sha512-Cx8/8ov1tKEacHhsi7FNYdisIhKq/SeQfprYSpYzwBuJwkPmCV8w7tTIvUJRQX9rvopXhBA4eBf1FPXqTZW5vA==",
+ "version": "28.7.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-28.7.0.tgz",
+ "integrity": "sha512-0zoq6kBS1yPjzJs+p0qZDinWEA72PTKoRk5ETYKfmeRcZAkhv83Y3HCpbb/LdgJJywfm8BcIJGezrBHvL7dVnQ==",
"requires": {
"@photostructure/tz-lookup": "^11.0.0",
"@types/luxon": "^3.4.2",
"batch-cluster": "^13.0.0",
- "exiftool-vendored.exe": "12.97.0",
- "exiftool-vendored.pl": "12.97.0",
+ "exiftool-vendored.exe": "12.99.0",
+ "exiftool-vendored.pl": "12.99.0",
"he": "^1.2.0",
"luxon": "^3.5.0"
}
},
"exiftool-vendored.exe": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.97.0.tgz",
- "integrity": "sha512-+HxyFigEJOtwRjP7PhEslhZKuVW2V0hvmHPHtbVtNKGfAUGcfc95xNTjASQfKJvc+9ZuvzdEBPkEQmyA/ZYdIw==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.99.0.tgz",
+ "integrity": "sha512-ffpJHCzC9OYJqw4JlPNtCwRy02jwhmnSJEF/QqEjpuIWDEnlRBQP/yWRh1Nw21K1R4FB4yG5PlCgEDu09VQz/w==",
"optional": true
},
"exiftool-vendored.pl": {
- "version": "12.97.0",
- "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.97.0.tgz",
- "integrity": "sha512-mXe9JEH3csfyPWcC7+H6IpfaokDMMr4S45n7MtiobGPdeeh+kFnf1SQ9cxg4sF403P6IKVeYYPbzgKMlpro9eQ==",
+ "version": "12.99.0",
+ "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.99.0.tgz",
+ "integrity": "sha512-qRVEPQxtoerXF+izJ0O7jGAr5o0Uyvnyu7ao5DTKzF+V7Fv3SurE0l43oCeZPFKo/Ld4V7vEylhFCm4IHVZKWA==",
"optional": true
},
"express": {
diff --git a/server/package.json b/server/package.json
index 8d3515b8da..a19dd626ad 100644
--- a/server/package.json
+++ b/server/package.json
@@ -108,7 +108,7 @@
"@types/lodash": "^4.14.197",
"@types/mock-fs": "^4.13.1",
"@types/multer": "^1.4.7",
- "@types/node": "^22.8.1",
+ "@types/node": "^22.8.5",
"@types/nodemailer": "^6.4.14",
"@types/picomatch": "^3.0.0",
"@types/pngjs": "^6.0.5",
diff --git a/server/src/app.module.ts b/server/src/app.module.ts
index 436b0f1252..7b9098c010 100644
--- a/server/src/app.module.ts
+++ b/server/src/app.module.ts
@@ -6,6 +6,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { ClsModule } from 'nestjs-cls';
import { OpenTelemetryModule } from 'nestjs-otel';
import { commands } from 'src/commands';
+import { IWorker } from 'src/constants';
import { controllers } from 'src/controllers';
import { entities } from 'src/entities';
import { ImmichWorker } from 'src/enum';
@@ -57,12 +58,9 @@ const imports = [
TypeOrmModule.forFeature(entities),
];
-abstract class BaseModule implements OnModuleInit, OnModuleDestroy {
- private get worker() {
- return this.getWorker();
- }
-
+class BaseModule implements OnModuleInit, OnModuleDestroy {
constructor(
+ @Inject(IWorker) private worker: ImmichWorker,
@Inject(ILoggerRepository) logger: ILoggerRepository,
@Inject(IEventRepository) private eventRepository: IEventRepository,
@Inject(IJobRepository) private jobRepository: IJobRepository,
@@ -71,8 +69,6 @@ abstract class BaseModule implements OnModuleInit, OnModuleDestroy {
logger.setAppName(this.worker);
}
- abstract getWorker(): ImmichWorker;
-
async onModuleInit() {
this.telemetryRepository.setup({ repositories: repositories.map(({ useClass }) => useClass) });
@@ -94,23 +90,15 @@ abstract class BaseModule implements OnModuleInit, OnModuleDestroy {
@Module({
imports: [...imports, ScheduleModule.forRoot()],
controllers: [...controllers],
- providers: [...common, ...middleware],
+ providers: [...common, ...middleware, { provide: IWorker, useValue: ImmichWorker.API }],
})
-export class ApiModule extends BaseModule {
- getWorker() {
- return ImmichWorker.API;
- }
-}
+export class ApiModule extends BaseModule {}
@Module({
imports: [...imports],
- providers: [...common, SchedulerRegistry],
+ providers: [...common, { provide: IWorker, useValue: ImmichWorker.MICROSERVICES }, SchedulerRegistry],
})
-export class MicroservicesModule extends BaseModule {
- getWorker() {
- return ImmichWorker.MICROSERVICES;
- }
-}
+export class MicroservicesModule extends BaseModule {}
@Module({
imports: [...imports],
diff --git a/server/src/bin/healthcheck.ts b/server/src/bin/healthcheck.ts
index 6de58c2002..2613ee6a19 100644
--- a/server/src/bin/healthcheck.ts
+++ b/server/src/bin/healthcheck.ts
@@ -3,7 +3,7 @@ import { ImmichWorker } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository';
const main = async () => {
- const { workers, port } = new ConfigRepository().getEnv();
+ const { host, workers, port } = new ConfigRepository().getEnv();
if (!workers.includes(ImmichWorker.API)) {
process.exit();
}
@@ -11,7 +11,7 @@ const main = async () => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 2000);
try {
- const response = await fetch(`http://localhost:${port}/api/server/ping`, {
+ const response = await fetch(`http://${host || 'localhost'}:${port}/api/server/ping`, {
signal: controller.signal,
});
diff --git a/server/src/constants.ts b/server/src/constants.ts
index e99970723a..0b9f525c71 100644
--- a/server/src/constants.ts
+++ b/server/src/constants.ts
@@ -13,6 +13,8 @@ export const ADDED_IN_PREFIX = 'This property was added in ';
export const SALT_ROUNDS = 10;
+export const IWorker = 'IWorker';
+
const { version } = JSON.parse(readFileSync('./package.json', 'utf8'));
export const serverVersion = new SemVer(version);
diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts
index a312050f08..124e45b62c 100644
--- a/server/src/services/base.service.ts
+++ b/server/src/services/base.service.ts
@@ -1,9 +1,10 @@
-import { BadRequestException, Inject } from '@nestjs/common';
+import { BadRequestException, Inject, Optional } from '@nestjs/common';
import sanitize from 'sanitize-filename';
import { SystemConfig } from 'src/config';
-import { SALT_ROUNDS } from 'src/constants';
+import { IWorker, SALT_ROUNDS } from 'src/constants';
import { StorageCore } from 'src/cores/storage.core';
import { UserEntity } from 'src/entities/user.entity';
+import { ImmichWorker } from 'src/enum';
import { IAccessRepository } from 'src/interfaces/access.interface';
import { IActivityRepository } from 'src/interfaces/activity.interface';
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
@@ -49,6 +50,7 @@ export class BaseService {
protected storageCore: StorageCore;
constructor(
+ @Inject(IWorker) @Optional() protected worker: ImmichWorker | undefined,
@Inject(ILoggerRepository) protected logger: ILoggerRepository,
@Inject(IAccessRepository) protected accessRepository: IAccessRepository,
@Inject(IActivityRepository) protected activityRepository: IActivityRepository,
diff --git a/server/src/services/duplicate.service.spec.ts b/server/src/services/duplicate.service.spec.ts
index 095d53dde6..75af1ef6f1 100644
--- a/server/src/services/duplicate.service.spec.ts
+++ b/server/src/services/duplicate.service.spec.ts
@@ -31,11 +31,23 @@ describe(SearchService.name, () => {
describe('getDuplicates', () => {
it('should get duplicates', async () => {
- assetMock.getDuplicates.mockResolvedValue([assetStub.hasDupe]);
+ assetMock.getDuplicates.mockResolvedValue([assetStub.hasDupe, assetStub.hasDupe]);
await expect(sut.getDuplicates(authStub.admin)).resolves.toEqual([
- { duplicateId: assetStub.hasDupe.duplicateId, assets: [expect.objectContaining({ id: assetStub.hasDupe.id })] },
+ {
+ duplicateId: assetStub.hasDupe.duplicateId,
+ assets: [
+ expect.objectContaining({ id: assetStub.hasDupe.id }),
+ expect.objectContaining({ id: assetStub.hasDupe.id }),
+ ],
+ },
]);
});
+
+ it('should update assets with duplicateId', async () => {
+ assetMock.getDuplicates.mockResolvedValue([assetStub.hasDupe]);
+ await expect(sut.getDuplicates(authStub.admin)).resolves.toEqual([]);
+ expect(assetMock.updateAll).toHaveBeenCalledWith([assetStub.hasDupe.id], { duplicateId: null });
+ });
});
describe('handleQueueSearchDuplicates', () => {
diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts
index 2fac7fcd3e..0d91df5790 100644
--- a/server/src/services/duplicate.service.ts
+++ b/server/src/services/duplicate.service.ts
@@ -16,8 +16,24 @@ import { usePagination } from 'src/utils/pagination';
export class DuplicateService extends BaseService {
async getDuplicates(auth: AuthDto): Promise {
const res = await this.assetRepository.getDuplicates({ userIds: [auth.user.id] });
-
- return mapDuplicateResponse(res.map((a) => mapAsset(a, { auth, withStack: true })));
+ const uniqueAssetIds: string[] = [];
+ const duplicates = mapDuplicateResponse(res.map((a) => mapAsset(a, { auth, withStack: true }))).filter(
+ (duplicate) => {
+ if (duplicate.assets.length === 1) {
+ uniqueAssetIds.push(duplicate.assets[0].id);
+ return false;
+ }
+ return true;
+ },
+ );
+ if (uniqueAssetIds.length > 0) {
+ try {
+ await this.assetRepository.updateAll(uniqueAssetIds, { duplicateId: null });
+ } catch (error: any) {
+ this.logger.error(`Failed to remove duplicateId from assets: ${error.message}`);
+ }
+ }
+ return duplicates;
}
@OnJob({ name: JobName.QUEUE_DUPLICATE_DETECTION, queue: QueueName.DUPLICATE_DETECTION })
diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts
index 03e89b07ec..0e7607f27a 100644
--- a/server/src/services/job.service.spec.ts
+++ b/server/src/services/job.service.spec.ts
@@ -18,7 +18,9 @@ describe(JobService.name, () => {
let telemetryMock: Mocked;
beforeEach(() => {
- ({ sut, assetMock, jobMock, loggerMock, telemetryMock } = newTestService(JobService));
+ ({ sut, assetMock, jobMock, loggerMock, telemetryMock } = newTestService(JobService, {
+ worker: ImmichWorker.MICROSERVICES,
+ }));
});
it('should work', () => {
@@ -27,7 +29,6 @@ describe(JobService.name, () => {
describe('onConfigUpdate', () => {
it('should update concurrency', () => {
- sut.onBootstrap(ImmichWorker.MICROSERVICES);
sut.onConfigUpdate({ oldConfig: defaults, newConfig: defaults });
expect(jobMock.setConcurrency).toHaveBeenCalledTimes(15);
diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts
index ff5ddbf006..946d9b1c91 100644
--- a/server/src/services/job.service.ts
+++ b/server/src/services/job.service.ts
@@ -38,16 +38,9 @@ const asJobItem = (dto: JobCreateDto): JobItem => {
@Injectable()
export class JobService extends BaseService {
- private isMicroservices = false;
-
- @OnEvent({ name: 'app.bootstrap' })
- onBootstrap(app: ArgOf<'app.bootstrap'>) {
- this.isMicroservices = app === ImmichWorker.MICROSERVICES;
- }
-
@OnEvent({ name: 'config.update', server: true })
onConfigUpdate({ newConfig: config }: ArgOf<'config.update'>) {
- if (!this.isMicroservices) {
+ if (this.worker !== ImmichWorker.MICROSERVICES) {
return;
}
diff --git a/server/test/utils.ts b/server/test/utils.ts
index 09c86d1afb..a5a6119033 100644
--- a/server/test/utils.ts
+++ b/server/test/utils.ts
@@ -1,6 +1,7 @@
import { ChildProcessWithoutNullStreams } from 'node:child_process';
import { Writable } from 'node:stream';
import { PNG } from 'pngjs';
+import { ImmichWorker } from 'src/enum';
import { IMetadataRepository } from 'src/interfaces/metadata.interface';
import { BaseService } from 'src/services/base.service';
import { newAccessRepositoryMock } from 'test/repositories/access.repository.mock';
@@ -44,8 +45,9 @@ import { newViewRepositoryMock } from 'test/repositories/view.repository.mock';
import { Readable } from 'typeorm/platform/PlatformTools';
import { Mocked, vitest } from 'vitest';
-type RepositoryOverrides = {
- metadataRepository: IMetadataRepository;
+type Overrides = {
+ worker?: ImmichWorker;
+ metadataRepository?: IMetadataRepository;
};
type BaseServiceArgs = ConstructorParameters;
type Constructor> = {
@@ -54,9 +56,11 @@ type Constructor> = {
export const newTestService = (
Service: Constructor,
- overrides?: RepositoryOverrides,
+ overrides?: Overrides,
) => {
- const { metadataRepository } = overrides || {};
+ const { metadataRepository, worker: workerOverride } = overrides || {};
+
+ const worker = workerOverride || ImmichWorker.API;
const accessMock = newAccessRepositoryMock();
const loggerMock = newLoggerRepositoryMock();
@@ -98,6 +102,7 @@ export const newTestService = (
const viewMock = newViewRepositoryMock();
const sut = new Service(
+ worker,
loggerMock,
accessMock,
activityMock,
diff --git a/web/Dockerfile b/web/Dockerfile
index 674fafcda9..7e4d9769be 100644
--- a/web/Dockerfile
+++ b/web/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:22.10.0-alpine3.20@sha256:fc95a044b87e95507c60c1f8c829e5d98ddf46401034932499db370c494ef0ff
+FROM node:22.11.0-alpine3.20@sha256:f265794478aa0b1a23d85a492c8311ed795bc527c3fe7e43453b3c872dcd71a3
RUN apk add --no-cache tini
USER node
diff --git a/web/package-lock.json b/web/package-lock.json
index 5185a235db..a24de1cd75 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -23,9 +23,9 @@
"justified-layout": "^4.1.0",
"lodash-es": "^4.17.21",
"luxon": "^3.4.4",
- "socket.io-client": "^4.7.4",
+ "socket.io-client": "~4.7.5",
"svelte-gestures": "^5.0.4",
- "svelte-i18n": "^4.0.0",
+ "svelte-i18n": "^4.0.1",
"svelte-local-storage-store": "^0.6.4",
"svelte-maplibre": "^0.9.13",
"thumbhash": "^0.1.1"
@@ -35,12 +35,12 @@
"@eslint/js": "^9.8.0",
"@faker-js/faker": "^9.0.0",
"@socket.io/component-emitter": "^3.1.0",
- "@sveltejs/adapter-static": "^3.0.1",
+ "@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/enhanced-img": "^0.3.0",
- "@sveltejs/kit": "^2.5.18",
- "@sveltejs/vite-plugin-svelte": "^3.1.2",
+ "@sveltejs/kit": "^2.7.2",
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@testing-library/jest-dom": "^6.4.2",
- "@testing-library/svelte": "^5.2.0",
+ "@testing-library/svelte": "^5.2.4",
"@testing-library/user-event": "^14.5.2",
"@types/dom-to-image": "^2.6.7",
"@types/justified-layout": "^4.1.4",
@@ -63,7 +63,7 @@
"prettier-plugin-sort-json": "^4.0.0",
"prettier-plugin-svelte": "^3.2.6",
"rollup-plugin-visualizer": "^5.12.0",
- "svelte": "^4.2.19",
+ "svelte": "^5.1.5",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.1",
"tslib": "^2.6.2",
@@ -80,7 +80,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
- "@types/node": "^22.8.0",
+ "@types/node": "^22.8.1",
"typescript": "^5.3.3"
}
},
@@ -1994,43 +1994,42 @@
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz",
- "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0.tgz",
+ "integrity": "sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0",
- "debug": "^4.3.4",
+ "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
+ "debug": "^4.3.7",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
- "magic-string": "^0.30.10",
- "svelte-hmr": "^0.16.0",
- "vitefu": "^0.2.5"
+ "magic-string": "^0.30.12",
+ "vitefu": "^1.0.3"
},
"engines": {
- "node": "^18.0.0 || >=20"
+ "node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
- "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz",
- "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz",
+ "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "debug": "^4.3.4"
+ "debug": "^4.3.7"
},
"engines": {
- "node": "^18.0.0 || >=20"
+ "node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
- "@sveltejs/vite-plugin-svelte": "^3.0.0",
- "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
+ "svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
@@ -2230,7 +2229,6 @@
"resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.2.4.tgz",
"integrity": "sha512-EFdy73+lULQgMJ1WolAymrxWWrPv9DWyDuDFKKlUip2PA/EXuHptzfYOKWljccFWDKhhGOu3dqNmoc2f/h/Ecg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@testing-library/dom": "^10.0.0"
},
@@ -2797,6 +2795,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-typescript": {
+ "version": "1.4.13",
+ "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
+ "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": ">=8.9.0"
+ }
+ },
"node_modules/agent-base": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
@@ -2883,6 +2890,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "dev": true,
"dependencies": {
"dequal": "^2.0.3"
}
@@ -2960,11 +2968,12 @@
}
},
"node_modules/axobject-query": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
- "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
- "dependencies": {
- "dequal": "^2.0.3"
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
}
},
"node_modules/balanced-match": {
@@ -3271,18 +3280,6 @@
"node": ">=6"
}
},
- "node_modules/code-red": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
- "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.4.15",
- "@types/estree": "^1.0.1",
- "acorn": "^8.10.0",
- "estree-walker": "^3.0.3",
- "periscopic": "^3.1.0"
- }
- },
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -3404,18 +3401,6 @@
"node": ">= 8"
}
},
- "node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
"node_modules/css.escape": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
@@ -3505,11 +3490,12 @@
}
},
"node_modules/debug": {
- "version": "4.3.6",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
- "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -3656,22 +3642,23 @@
"dev": true
},
"node_modules/engine.io-client": {
- "version": "6.6.1",
- "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
- "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
+ "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
- "xmlhttprequest-ssl": "~2.1.1"
+ "xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-parser": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
- "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "license": "MIT",
"engines": {
"node": ">=10.0.0"
}
@@ -4151,8 +4138,7 @@
"node_modules/esm-env": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz",
- "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==",
- "dev": true
+ "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA=="
},
"node_modules/esniff": {
"version": "2.0.1",
@@ -4197,6 +4183,16 @@
"node": ">=0.10"
}
},
+ "node_modules/esrap": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.2.tgz",
+ "integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15",
+ "@types/estree": "^1.0.1"
+ }
+ },
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -4222,6 +4218,7 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
"dependencies": {
"@types/estree": "^1.0.0"
}
@@ -4962,6 +4959,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
+ "license": "MIT",
"dependencies": {
"@types/estree": "*"
}
@@ -5329,9 +5327,9 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.11",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
- "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "version": "0.30.12",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
+ "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
@@ -5475,11 +5473,6 @@
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
}
},
- "node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
- },
"node_modules/memoizee": {
"version": "0.4.17",
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz",
@@ -5601,9 +5594,10 @@
}
},
"node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
@@ -5943,16 +5937,6 @@
"pbf": "bin/pbf"
}
},
- "node_modules/periscopic": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
- "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
- "dependencies": {
- "@types/estree": "^1.0.0",
- "estree-walker": "^3.0.0",
- "is-reference": "^3.0.0"
- }
- },
"node_modules/picocolors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
@@ -6860,14 +6844,14 @@
}
},
"node_modules/socket.io-client": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
- "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
+ "version": "4.7.5",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
+ "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
"license": "MIT",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
- "engine.io-client": "~6.6.1",
+ "engine.io-client": "~6.5.2",
"socket.io-parser": "~4.2.4"
},
"engines": {
@@ -6930,6 +6914,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -7176,28 +7161,27 @@
}
},
"node_modules/svelte": {
- "version": "4.2.19",
- "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz",
- "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==",
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.5.tgz",
+ "integrity": "sha512-AyYondx6wS0g8mmBMfwJVnOYYBswjBv6L4bc99awfbET2KozWvVwxe8NSN7fhx7Pgr7pOfOXIv7K8+Impc0OoQ==",
"license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.15",
- "@jridgewell/trace-mapping": "^0.3.18",
- "@types/estree": "^1.0.1",
- "acorn": "^8.9.0",
- "aria-query": "^5.3.0",
- "axobject-query": "^4.0.0",
- "code-red": "^1.0.3",
- "css-tree": "^2.3.1",
- "estree-walker": "^3.0.3",
- "is-reference": "^3.0.1",
+ "@ampproject/remapping": "^2.3.0",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@types/estree": "^1.0.5",
+ "acorn": "^8.12.1",
+ "acorn-typescript": "^1.4.13",
+ "aria-query": "^5.3.1",
+ "axobject-query": "^4.1.0",
+ "esm-env": "^1.0.0",
+ "esrap": "^1.2.2",
+ "is-reference": "^3.0.2",
"locate-character": "^3.0.0",
- "magic-string": "^0.30.4",
- "periscopic": "^3.1.0"
+ "magic-string": "^0.30.11",
+ "zimmerframe": "^1.1.2"
},
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/svelte-check": {
@@ -7318,18 +7302,6 @@
"integrity": "sha512-kElJnoZrQtlkXE0O/RcKioz9NP0Sxx05j31ohyosNkydo6NOEsZB85mhoaCxOQNjxN+QPumYWfmIUsznYFjihA==",
"license": "MIT"
},
- "node_modules/svelte-hmr": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
- "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==",
- "dev": true,
- "engines": {
- "node": "^12.20 || ^14.13.1 || >= 16"
- },
- "peerDependencies": {
- "svelte": "^3.19.0 || ^4.0.0"
- }
- },
"node_modules/svelte-i18n": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.1.tgz",
@@ -7821,6 +7793,15 @@
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
}
},
+ "node_modules/svelte/node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -8420,12 +8401,17 @@
}
},
"node_modules/vitefu": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
- "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.3.tgz",
+ "integrity": "sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==",
"dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "tests/deps/*",
+ "tests/projects/*"
+ ],
"peerDependencies": {
- "vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0"
},
"peerDependenciesMeta": {
"vite": {
@@ -8757,9 +8743,9 @@
"peer": true
},
"node_modules/xmlhttprequest-ssl": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
- "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+ "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
@@ -8820,6 +8806,12 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zimmerframe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
+ "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
+ "license": "MIT"
}
}
}
diff --git a/web/package.json b/web/package.json
index 103f4535f9..b67f1cf471 100644
--- a/web/package.json
+++ b/web/package.json
@@ -8,7 +8,7 @@
"build:stats": "BUILD_STATS=true vite build",
"package": "svelte-kit package",
"preview": "vite preview",
- "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings",
+ "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore'",
"check:typescript": "tsc --noEmit",
"check:watch": "npm run check:svelte -- --watch",
"check:code": "npm run format && npm run lint && npm run check:svelte && npm run check:typescript",
@@ -27,12 +27,12 @@
"@eslint/js": "^9.8.0",
"@faker-js/faker": "^9.0.0",
"@socket.io/component-emitter": "^3.1.0",
- "@sveltejs/adapter-static": "^3.0.1",
+ "@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/enhanced-img": "^0.3.0",
- "@sveltejs/kit": "^2.5.18",
- "@sveltejs/vite-plugin-svelte": "^3.1.2",
+ "@sveltejs/kit": "^2.7.2",
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
"@testing-library/jest-dom": "^6.4.2",
- "@testing-library/svelte": "^5.2.0",
+ "@testing-library/svelte": "^5.2.4",
"@testing-library/user-event": "^14.5.2",
"@types/dom-to-image": "^2.6.7",
"@types/justified-layout": "^4.1.4",
@@ -55,7 +55,7 @@
"prettier-plugin-sort-json": "^4.0.0",
"prettier-plugin-svelte": "^3.2.6",
"rollup-plugin-visualizer": "^5.12.0",
- "svelte": "^4.2.19",
+ "svelte": "^5.1.5",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.1",
"tslib": "^2.6.2",
@@ -79,9 +79,9 @@
"justified-layout": "^4.1.0",
"lodash-es": "^4.17.21",
"luxon": "^3.4.4",
- "socket.io-client": "^4.7.4",
+ "socket.io-client": "~4.7.5",
"svelte-gestures": "^5.0.4",
- "svelte-i18n": "^4.0.0",
+ "svelte-i18n": "^4.0.1",
"svelte-local-storage-store": "^0.6.4",
"svelte-maplibre": "^0.9.13",
"thumbhash": "^0.1.1"
diff --git a/web/src/lib/__mocks__/animate.mock.ts b/web/src/lib/__mocks__/animate.mock.ts
new file mode 100644
index 0000000000..5f0d367d86
--- /dev/null
+++ b/web/src/lib/__mocks__/animate.mock.ts
@@ -0,0 +1,17 @@
+import { tick } from 'svelte';
+import { vi } from 'vitest';
+
+export const getAnimateMock = () =>
+ vi.fn().mockImplementation(() => {
+ let onfinish: (() => void) | null = null;
+ void tick().then(() => onfinish?.());
+
+ return {
+ set onfinish(fn: () => void) {
+ onfinish = fn;
+ },
+ cancel() {
+ onfinish = null;
+ },
+ };
+ });
diff --git a/web/src/lib/components/admin-page/jobs/job-tile.svelte b/web/src/lib/components/admin-page/jobs/job-tile.svelte
index b0af3a710f..81c23e927b 100644
--- a/web/src/lib/components/admin-page/jobs/job-tile.svelte
+++ b/web/src/lib/components/admin-page/jobs/job-tile.svelte
@@ -14,14 +14,14 @@
mdiPlay,
mdiSelectionSearch,
} from '@mdi/js';
- import { type ComponentType } from 'svelte';
+ import { type Component } from 'svelte';
import { t } from 'svelte-i18n';
import JobTileButton from './job-tile-button.svelte';
import JobTileStatus from './job-tile-status.svelte';
export let title: string;
export let subtitle: string | undefined;
- export let description: ComponentType | undefined;
+ export let description: Component | undefined;
export let jobCounts: JobCountsDto;
export let queueStatus: QueueStatusDto;
export let icon: string;
diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
index 8702a1e933..67d672d398 100644
--- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
+++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
@@ -19,7 +19,7 @@
mdiTagFaces,
mdiVideo,
} from '@mdi/js';
- import type { ComponentType } from 'svelte';
+ import type { Component } from 'svelte';
import JobTile from './job-tile.svelte';
import StorageMigrationDescription from './storage-migration-description.svelte';
import { dialogController } from '$lib/components/shared-components/dialog/dialog';
@@ -30,7 +30,7 @@
interface JobDetails {
title: string;
subtitle?: string;
- description?: ComponentType;
+ description?: Component;
allText?: string;
refreshText?: string;
missingText: string;
@@ -56,6 +56,7 @@
await handleCommand(jobId, dto);
};
+ // svelte-ignore reactive_declaration_non_reactive_property
$: jobDetails = >>{
[JobName.ThumbnailGeneration]: {
icon: mdiFileJpgBox,
diff --git a/web/src/lib/components/album-page/albums-controls.svelte b/web/src/lib/components/album-page/albums-controls.svelte
index 14faee5e7f..34563eddd3 100644
--- a/web/src/lib/components/album-page/albums-controls.svelte
+++ b/web/src/lib/components/album-page/albums-controls.svelte
@@ -87,6 +87,7 @@
}
}
+ // svelte-ignore reactive_declaration_non_reactive_property
$: {
if (selectedGroupOption.id === AlbumGroupBy.None) {
groupIcon = mdiFolderRemoveOutline;
@@ -96,8 +97,10 @@
}
}
+ // svelte-ignore reactive_declaration_non_reactive_property
$: sortIcon = $albumViewSettings.sortOrder === SortOrder.Desc ? mdiArrowDownThin : mdiArrowUpThin;
+ // svelte-ignore reactive_declaration_non_reactive_property
$: albumFilterNames = ((): Record => {
return {
[AlbumFilter.All]: $t('all'),
@@ -106,6 +109,7 @@
};
})();
+ // svelte-ignore reactive_declaration_non_reactive_property
$: albumSortByNames = ((): Record => {
return {
[AlbumSortBy.Title]: $t('sort_title'),
@@ -117,6 +121,7 @@
};
})();
+ // svelte-ignore reactive_declaration_non_reactive_property
$: albumGroupByNames = ((): Record => {
return {
[AlbumGroupBy.None]: $t('group_no'),
diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte
index a13e37c2b5..3858dd23b7 100644
--- a/web/src/lib/components/album-page/albums-list.svelte
+++ b/web/src/lib/components/album-page/albums-list.svelte
@@ -135,6 +135,7 @@
let isOpen = false;
// Step 1: Filter between Owned and Shared albums, or both.
+ // svelte-ignore reactive_declaration_non_reactive_property
$: {
switch (userSettings.filter) {
case AlbumFilter.Owned: {
diff --git a/web/src/lib/components/album-page/albums-table-header.svelte b/web/src/lib/components/album-page/albums-table-header.svelte
index 2c396bebed..84e32b82f5 100644
--- a/web/src/lib/components/album-page/albums-table-header.svelte
+++ b/web/src/lib/components/album-page/albums-table-header.svelte
@@ -13,7 +13,7 @@
$albumViewSettings.sortOrder = option.defaultOrder;
}
};
-
+ // svelte-ignore reactive_declaration_non_reactive_property
$: albumSortByNames = ((): Record => {
return {
[AlbumSortBy.Title]: $t('sort_title'),
diff --git a/web/src/lib/components/asset-viewer/activity-viewer.svelte b/web/src/lib/components/asset-viewer/activity-viewer.svelte
index 3a02454315..4f4fdb2649 100644
--- a/web/src/lib/components/asset-viewer/activity-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/activity-viewer.svelte
@@ -293,7 +293,7 @@
class="h-[18px] {disabled
? 'cursor-not-allowed'
: ''} w-full max-h-56 pr-2 items-center overflow-y-auto leading-4 outline-none resize-none bg-gray-200"
- />
+ >
{#if isSendingMessage}
diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
index c82d9f9659..a25ea6bf90 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
+++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
@@ -15,13 +15,31 @@ describe('AssetViewerNavBar component', () => {
showShareButton: false,
onZoomImage: () => {},
onCopyImage: () => {},
+ onAction: () => {},
+ onRunJob: () => {},
+ onPlaySlideshow: () => {},
+ onShowDetail: () => {},
+ onClose: () => {},
};
+ beforeAll(() => {
+ Element.prototype.animate = vi.fn().mockImplementation(() => ({
+ cancel: () => {},
+ }));
+ vi.stubGlobal(
+ 'ResizeObserver',
+ vi.fn(() => ({ observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn() })),
+ );
+ });
+
afterEach(() => {
- vi.resetAllMocks();
resetSavedUser();
});
+ afterAll(() => {
+ vi.restoreAllMocks();
+ });
+
it('shows back button', () => {
const asset = assetFactory.build({ isTrashed: false });
const { getByTitle } = render(AssetViewerNavBar, { asset, ...additionalProps });
diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
index 8ef4e86115..d142c43f20 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte
@@ -61,6 +61,7 @@
const sharedLink = getSharedLink();
$: isOwner = $user && asset.ownerId === $user?.id;
+ // svelte-ignore reactive_declaration_non_reactive_property
$: showDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
// $: showEditorButton =
// isOwner &&
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte
index 7686494aa7..4caeab6dd4 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte
@@ -598,7 +598,7 @@
{#if stackedAsset.id == asset.id}
{/if}
diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte
index 88ea98778f..ab84896b7b 100644
--- a/web/src/lib/components/asset-viewer/detail-panel.svelte
+++ b/web/src/lib/components/asset-viewer/detail-panel.svelte
@@ -154,7 +154,7 @@
{#if $user?.isAdmin}
-
{$t('admin.asset_offline_description')}
+ {$t('admin.asset_offline_description')}
{:else}
{$t('asset_offline_description')}
{/if}
@@ -345,43 +345,45 @@
{/if}
- {#if asset.exifInfo?.fileSizeInByte}
-
-
+
+
-
-
- {asset.originalFileName}
- {#if isOwner}
-
- {/if}
+
+
+ {asset.originalFileName}
+ {#if isOwner}
+
+ {/if}
+
+ {#if showAssetPath}
+
+ {asset.originalPath}
+ {/if}
+ {#if (asset.exifInfo?.exifImageHeight && asset.exifInfo?.exifImageWidth) || asset.exifInfo?.fileSizeInByte}
- {#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
+ {#if asset.exifInfo?.exifImageHeight && asset.exifInfo?.exifImageWidth}
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
{getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)} MP
+ {@const { width, height } = getDimensions(asset.exifInfo)}
+
{width} x {height}
{/if}
- {@const { width, height } = getDimensions(asset.exifInfo)}
-
{width} x {height}
{/if}
-
{getByteUnitString(asset.exifInfo.fileSizeInByte, $locale)}
+ {#if asset.exifInfo?.fileSizeInByte}
+
{getByteUnitString(asset.exifInfo.fileSizeInByte, $locale)}
+ {/if}
- {#if showAssetPath}
-
- {asset.originalPath}
-
- {/if}
-
+ {/if}
- {/if}
+
{#if asset.exifInfo?.make || asset.exifInfo?.model || asset.exifInfo?.fNumber}
diff --git a/web/src/lib/components/asset-viewer/download-panel.svelte b/web/src/lib/components/asset-viewer/download-panel.svelte
index 333ee0c13b..db46e1eff0 100644
--- a/web/src/lib/components/asset-viewer/download-panel.svelte
+++ b/web/src/lib/components/asset-viewer/download-panel.svelte
@@ -32,7 +32,7 @@
diff --git a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte
index 1adef32735..78d5ca26e0 100644
--- a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte
+++ b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte
@@ -23,6 +23,7 @@
export let onClose: () => void;
let selectedType: string = editTypes[0].name;
+ // svelte-ignore reactive_declaration_non_reactive_property
$: selectedTypeObj = editTypes.find((t) => t.name === selectedType) || editTypes[0];
setTimeout(() => {
diff --git a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte
index 53a78a346c..1745cd66b6 100644
--- a/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte
+++ b/web/src/lib/components/asset-viewer/photo-sphere-viewer-adapter.svelte
@@ -55,4 +55,4 @@
});
-
+
diff --git a/web/src/lib/components/asset-viewer/photo-viewer.spec.ts b/web/src/lib/components/asset-viewer/photo-viewer.spec.ts
index e64dc680a9..e1372e37da 100644
--- a/web/src/lib/components/asset-viewer/photo-viewer.spec.ts
+++ b/web/src/lib/components/asset-viewer/photo-viewer.spec.ts
@@ -1,3 +1,4 @@
+import { getAnimateMock } from '$lib/__mocks__/animate.mock';
import PhotoViewer from '$lib/components/asset-viewer/photo-viewer.svelte';
import * as utils from '$lib/utils';
import { AssetMediaSize } from '@immich/sdk';
@@ -24,6 +25,10 @@ describe('PhotoViewer component', () => {
getAssetThumbnailUrlSpy = vi.spyOn(utils, 'getAssetThumbnailUrl');
});
+ beforeEach(() => {
+ Element.prototype.animate = getAnimateMock();
+ });
+
afterEach(() => {
vi.resetAllMocks();
});
diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte
index 4157c558d2..d7595f6b7e 100644
--- a/web/src/lib/components/asset-viewer/photo-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte
@@ -193,7 +193,7 @@
+ >
{/each}
{/if}
diff --git a/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts b/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts
index 2525b86160..e14628a42f 100644
--- a/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts
+++ b/web/src/lib/components/assets/thumbnail/__test__/image-thumbnail.spec.ts
@@ -3,9 +3,9 @@ import { render } from '@testing-library/svelte';
describe('ImageThumbnail component', () => {
beforeAll(() => {
- Object.defineProperty(HTMLImageElement.prototype, 'complete', {
- value: true,
- });
+ Element.prototype.animate = vi.fn().mockImplementation(() => ({
+ cancel: () => {},
+ }));
});
it('shows thumbhash while image is loading', () => {
diff --git a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte
index 662209544a..282d152e90 100644
--- a/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte
+++ b/web/src/lib/components/assets/thumbnail/image-thumbnail.svelte
@@ -96,5 +96,5 @@
class:rounded-full={circle}
draggable="false"
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
- />
+ >
{/if}
diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte
index ac67605fc6..4c2cf74518 100644
--- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte
+++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte
@@ -218,6 +218,7 @@
href={currentUrlReplaceAssetId(asset.id)}
on:click={(evt) => evt.preventDefault()}
tabindex={0}
+ aria-label="Thumbnail URL"
>
{/if}
@@ -255,12 +256,12 @@
+ >
+ >
{#if !isSharedLink() && asset.isFavorite}
@@ -339,7 +340,7 @@
class="absolute top-0 h-full w-full bg-immich-primary opacity-40"
in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }}
- />
+ >
{/if}
{/if}
diff --git a/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte b/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte
index 5cac0b1945..14f99ac331 100644
--- a/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte
+++ b/web/src/lib/components/assets/thumbnail/video-thumbnail.svelte
@@ -113,7 +113,10 @@
}}
on:timeupdate={({ currentTarget }) => {
const remaining = currentTarget.duration - currentTarget.currentTime;
- remainingSeconds = Math.min(Math.ceil(remaining), durationInSeconds);
+ remainingSeconds = Math.min(
+ Math.ceil(Number.isNaN(remaining) ? Number.POSITIVE_INFINITY : remaining),
+ durationInSeconds,
+ );
}}
- />
+ >
{/if}
diff --git a/web/src/lib/components/elements/dropdown.svelte b/web/src/lib/components/elements/dropdown.svelte
index 0016bbe538..80689ef1fe 100644
--- a/web/src/lib/components/elements/dropdown.svelte
+++ b/web/src/lib/components/elements/dropdown.svelte
@@ -102,7 +102,7 @@
{renderedOption.title}
{:else}
-
+
{renderedOption.title}
diff --git a/web/src/lib/components/elements/slider.svelte b/web/src/lib/components/elements/slider.svelte
index efe67fda9c..4c19696372 100644
--- a/web/src/lib/components/elements/slider.svelte
+++ b/web/src/lib/components/elements/slider.svelte
@@ -28,11 +28,11 @@
{#if disabled}
+ >
{:else}
+ >
{/if}
diff --git a/web/src/lib/components/faces-page/face-thumbnail.svelte b/web/src/lib/components/faces-page/face-thumbnail.svelte
index f30029ac8f..cce91b4669 100644
--- a/web/src/lib/components/faces-page/face-thumbnail.svelte
+++ b/web/src/lib/components/faces-page/face-thumbnail.svelte
@@ -36,14 +36,14 @@
class:hover:opacity-100={selectable}
class:rounded-full={circle}
class:rounded-lg={!circle}
- />
+ >
{#if selected}
+ >
{/if}
{#if person.name}
diff --git a/web/src/lib/components/faces-page/manage-people-visibility.svelte b/web/src/lib/components/faces-page/manage-people-visibility.svelte
index a48fd6bf74..90e20a1e5b 100644
--- a/web/src/lib/components/faces-page/manage-people-visibility.svelte
+++ b/web/src/lib/components/faces-page/manage-people-visibility.svelte
@@ -44,6 +44,8 @@
return personIsHidden;
};
+ // svelte-ignore reactive_declaration_non_reactive_property
+ // svelte-ignore reactive_declaration_module_script_dependency
$: toggleButtonOptions = ((): Record => {
return {
[ToggleVisibility.HIDE_ALL]: { icon: mdiEyeOff, label: $t('hide_all_people') },
diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte
index 9cb3078ec0..52daa36a99 100644
--- a/web/src/lib/components/faces-page/merge-face-selector.svelte
+++ b/web/src/lib/components/faces-page/merge-face-selector.svelte
@@ -102,7 +102,7 @@
{:else}
{$t('merge_people')}
{/if}
-
+