mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73f53935ca | |||
| 00dae6ac38 | |||
| 5a8fd40dc5 | |||
| 813d684aaa | |||
| 644f705be1 | |||
| f3e4bcc733 | |||
| 9a0c17fdb8 | |||
| b7c4497dfd | |||
| 9c227aeaf5 |
+207
-100
@@ -8,6 +8,99 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|
||||||
|
x-e2e-server-job: &e2e-server-job
|
||||||
|
needs: [pre-job, build-e2e-images]
|
||||||
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).server == true || fromJSON(needs.pre-job.outputs.should_run).cli == true }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./e2e
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
|
|
||||||
|
x-e2e-web-job: &e2e-web-job
|
||||||
|
needs: [pre-job, build-e2e-images]
|
||||||
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).web == true }}
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./e2e
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
|
|
||||||
|
x-e2e-steps:
|
||||||
|
token: &e2e-token-step
|
||||||
|
id: token
|
||||||
|
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
checkout: &e2e-checkout-step
|
||||||
|
name: Checkout code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
submodules: 'recursive'
|
||||||
|
token: ${{ steps.token.outputs.token }}
|
||||||
|
setup-pnpm: &e2e-setup-pnpm-step
|
||||||
|
name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
|
||||||
|
setup-node: &e2e-setup-node-step
|
||||||
|
name: Setup Node
|
||||||
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
|
with:
|
||||||
|
node-version-file: './e2e/.nvmrc'
|
||||||
|
cache: 'pnpm'
|
||||||
|
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||||
|
setup-sdk: &e2e-setup-sdk-step
|
||||||
|
name: Run setup typescript-sdk
|
||||||
|
run: pnpm install --frozen-lockfile && pnpm build
|
||||||
|
working-directory: ./open-api/typescript-sdk
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
setup-web: &e2e-setup-web-step
|
||||||
|
name: Run setup web
|
||||||
|
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
||||||
|
working-directory: ./web
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
setup-cli: &e2e-setup-cli-step
|
||||||
|
name: Run setup cli
|
||||||
|
run: pnpm install --frozen-lockfile && pnpm build
|
||||||
|
working-directory: ./cli
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
install-deps: &e2e-install-deps-step
|
||||||
|
name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
install-playwright: &e2e-install-playwright-step
|
||||||
|
name: Install Playwright Browsers
|
||||||
|
run: pnpm exec playwright install chromium --only-shell
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
download-images: &e2e-download-images-step
|
||||||
|
name: Download Docker images
|
||||||
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||||
|
with:
|
||||||
|
name: docker-images-${{ matrix.runner }}
|
||||||
|
path: e2e
|
||||||
|
load-images: &e2e-load-images-step
|
||||||
|
name: Load Docker images
|
||||||
|
run: docker load < docker-images.tar.gz
|
||||||
|
docker-compose-up: &e2e-docker-compose-up-step
|
||||||
|
name: Start Docker Compose
|
||||||
|
run: docker compose up -d --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
capture-docker-logs: &e2e-capture-docker-logs-step
|
||||||
|
name: Capture Docker logs
|
||||||
|
if: always()
|
||||||
|
run: docker compose logs --no-color > docker-compose-logs.txt
|
||||||
|
working-directory: ./e2e
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-job:
|
pre-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -397,10 +490,10 @@ jobs:
|
|||||||
- name: Run medium tests
|
- name: Run medium tests
|
||||||
run: pnpm test:medium
|
run: pnpm test:medium
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
e2e-tests-server-cli:
|
build-e2e-images:
|
||||||
name: End-to-End Tests (Server & CLI)
|
name: Build E2E Docker Images
|
||||||
needs: pre-job
|
needs: pre-job
|
||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).server == true || fromJSON(needs.pre-job.outputs.should_run).cli == true }}
|
if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).server == true || fromJSON(needs.pre-job.outputs.should_run).cli == true || fromJSON(needs.pre-job.outputs.should_run).web == true }}
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -411,111 +504,86 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- *e2e-token-step
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
- *e2e-checkout-step
|
||||||
|
- name: Build Docker images
|
||||||
|
run: docker compose build
|
||||||
|
- name: Save Docker images
|
||||||
|
run: docker save immich-server:latest immich-e2e-e2e-auth-server | gzip > docker-images.tar.gz
|
||||||
|
- name: Upload Docker images
|
||||||
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
name: docker-images-${{ matrix.runner }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
path: e2e/docker-images.tar.gz
|
||||||
|
retention-days: 1
|
||||||
- name: Checkout code
|
e2e-tests-server-cli:
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
<<: *e2e-server-job
|
||||||
with:
|
name: End-to-End Tests (Server & CLI)
|
||||||
persist-credentials: false
|
steps:
|
||||||
submodules: 'recursive'
|
- *e2e-token-step
|
||||||
token: ${{ steps.token.outputs.token }}
|
- *e2e-checkout-step
|
||||||
- name: Setup pnpm
|
- *e2e-setup-pnpm-step
|
||||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
|
- *e2e-setup-node-step
|
||||||
- name: Setup Node
|
- *e2e-setup-sdk-step
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
- *e2e-setup-web-step
|
||||||
with:
|
- *e2e-setup-cli-step
|
||||||
node-version-file: './e2e/.nvmrc'
|
- *e2e-install-deps-step
|
||||||
cache: 'pnpm'
|
- *e2e-download-images-step
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
- *e2e-load-images-step
|
||||||
- name: Run setup typescript-sdk
|
- *e2e-docker-compose-up-step
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Run setup web
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
|
||||||
working-directory: ./web
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Run setup cli
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
|
||||||
working-directory: ./cli
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Start Docker Compose
|
|
||||||
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Run e2e tests (api & cli)
|
- name: Run e2e tests (api & cli)
|
||||||
env:
|
env:
|
||||||
VITEST_DISABLE_DOCKER_SETUP: true
|
VITEST_DISABLE_DOCKER_SETUP: true
|
||||||
run: pnpm test
|
run: pnpm test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run e2e tests (maintenance)
|
- *e2e-capture-docker-logs-step
|
||||||
env:
|
|
||||||
VITEST_DISABLE_DOCKER_SETUP: true
|
|
||||||
run: pnpm test:maintenance
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Capture Docker logs
|
|
||||||
if: always()
|
|
||||||
run: docker compose logs --no-color > docker-compose-logs.txt
|
|
||||||
working-directory: ./e2e
|
|
||||||
- name: Archive Docker logs
|
- name: Archive Docker logs
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: e2e-server-docker-logs-${{ matrix.runner }}
|
name: e2e-server-docker-logs-${{ matrix.runner }}
|
||||||
path: e2e/docker-compose-logs.txt
|
path: e2e/docker-compose-logs.txt
|
||||||
e2e-tests-web:
|
e2e-tests-server-maintenance:
|
||||||
name: End-to-End Tests (Web)
|
<<: *e2e-server-job
|
||||||
needs: pre-job
|
name: End-to-End Tests (Server Maintenance)
|
||||||
if: ${{ fromJSON(needs.pre-job.outputs.should_run).e2e == true || fromJSON(needs.pre-job.outputs.should_run).web == true }}
|
|
||||||
runs-on: ${{ matrix.runner }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./e2e
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- *e2e-token-step
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@05e16407c0a5492138bb38139c9d9bf067b40886 # create-workflow-token-action-v1.0.1
|
- *e2e-checkout-step
|
||||||
|
- *e2e-setup-pnpm-step
|
||||||
|
- *e2e-setup-node-step
|
||||||
|
- *e2e-setup-sdk-step
|
||||||
|
- *e2e-setup-web-step
|
||||||
|
- *e2e-setup-cli-step
|
||||||
|
- *e2e-install-deps-step
|
||||||
|
- *e2e-download-images-step
|
||||||
|
- *e2e-load-images-step
|
||||||
|
- *e2e-docker-compose-up-step
|
||||||
|
- name: Run e2e tests (maintenance)
|
||||||
|
env:
|
||||||
|
VITEST_DISABLE_DOCKER_SETUP: true
|
||||||
|
run: pnpm test:maintenance
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
- *e2e-capture-docker-logs-step
|
||||||
|
- name: Archive Docker logs
|
||||||
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
|
if: always()
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
name: e2e-server-maintenance-docker-logs-${{ matrix.runner }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
path: e2e/docker-compose-logs.txt
|
||||||
|
e2e-tests-web:
|
||||||
- name: Checkout code
|
<<: *e2e-web-job
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
name: End-to-End Tests (Web)
|
||||||
with:
|
steps:
|
||||||
persist-credentials: false
|
- *e2e-token-step
|
||||||
submodules: 'recursive'
|
- *e2e-checkout-step
|
||||||
token: ${{ steps.token.outputs.token }}
|
- *e2e-setup-pnpm-step
|
||||||
- name: Setup pnpm
|
- *e2e-setup-node-step
|
||||||
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
|
- *e2e-setup-sdk-step
|
||||||
- name: Setup Node
|
- *e2e-install-deps-step
|
||||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
- *e2e-install-playwright-step
|
||||||
with:
|
- *e2e-download-images-step
|
||||||
node-version-file: './e2e/.nvmrc'
|
- *e2e-load-images-step
|
||||||
cache: 'pnpm'
|
- *e2e-docker-compose-up-step
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Install Playwright Browsers
|
|
||||||
run: pnpm exec playwright install chromium --only-shell
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Docker build
|
|
||||||
run: docker compose up -d --build --renew-anon-volumes --force-recreate --remove-orphans --wait --wait-timeout 300
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
- name: Run e2e tests (web)
|
- name: Run e2e tests (web)
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||||
@@ -527,6 +595,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: e2e-web-test-results-${{ matrix.runner }}
|
name: e2e-web-test-results-${{ matrix.runner }}
|
||||||
path: e2e/playwright-report/
|
path: e2e/playwright-report/
|
||||||
|
- *e2e-capture-docker-logs-step
|
||||||
|
- name: Archive Docker logs
|
||||||
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: e2e-web-docker-logs-${{ matrix.runner }}
|
||||||
|
path: e2e/docker-compose-logs.txt
|
||||||
|
e2e-tests-web-ui:
|
||||||
|
<<: *e2e-web-job
|
||||||
|
name: End-to-End Tests (Web UI)
|
||||||
|
steps:
|
||||||
|
- *e2e-token-step
|
||||||
|
- *e2e-checkout-step
|
||||||
|
- *e2e-setup-pnpm-step
|
||||||
|
- *e2e-setup-node-step
|
||||||
|
- *e2e-setup-sdk-step
|
||||||
|
- *e2e-install-deps-step
|
||||||
|
- *e2e-install-playwright-step
|
||||||
|
- *e2e-download-images-step
|
||||||
|
- *e2e-load-images-step
|
||||||
|
- *e2e-docker-compose-up-step
|
||||||
- name: Run ui tests (web)
|
- name: Run ui tests (web)
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||||
@@ -538,6 +627,27 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: e2e-ui-test-results-${{ matrix.runner }}
|
name: e2e-ui-test-results-${{ matrix.runner }}
|
||||||
path: e2e/playwright-report/
|
path: e2e/playwright-report/
|
||||||
|
- *e2e-capture-docker-logs-step
|
||||||
|
- name: Archive Docker logs
|
||||||
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: e2e-web-ui-docker-logs-${{ matrix.runner }}
|
||||||
|
path: e2e/docker-compose-logs.txt
|
||||||
|
e2e-tests-web-maintenance:
|
||||||
|
<<: *e2e-web-job
|
||||||
|
name: End-to-End Tests (Web Maintenance)
|
||||||
|
steps:
|
||||||
|
- *e2e-token-step
|
||||||
|
- *e2e-checkout-step
|
||||||
|
- *e2e-setup-pnpm-step
|
||||||
|
- *e2e-setup-node-step
|
||||||
|
- *e2e-setup-sdk-step
|
||||||
|
- *e2e-install-deps-step
|
||||||
|
- *e2e-install-playwright-step
|
||||||
|
- *e2e-download-images-step
|
||||||
|
- *e2e-load-images-step
|
||||||
|
- *e2e-docker-compose-up-step
|
||||||
- name: Run maintenance tests
|
- name: Run maintenance tests
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
PLAYWRIGHT_DISABLE_WEBSERVER: true
|
||||||
@@ -549,19 +659,16 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: e2e-maintenance-isolated-test-results-${{ matrix.runner }}
|
name: e2e-maintenance-isolated-test-results-${{ matrix.runner }}
|
||||||
path: e2e/playwright-report/
|
path: e2e/playwright-report/
|
||||||
- name: Capture Docker logs
|
- *e2e-capture-docker-logs-step
|
||||||
if: always()
|
|
||||||
run: docker compose logs --no-color > docker-compose-logs.txt
|
|
||||||
working-directory: ./e2e
|
|
||||||
- name: Archive Docker logs
|
- name: Archive Docker logs
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: e2e-web-docker-logs-${{ matrix.runner }}
|
name: e2e-web-maintenance-docker-logs-${{ matrix.runner }}
|
||||||
path: e2e/docker-compose-logs.txt
|
path: e2e/docker-compose-logs.txt
|
||||||
success-check-e2e:
|
success-check-e2e:
|
||||||
name: End-to-End Tests Success
|
name: End-to-End Tests Success
|
||||||
needs: [e2e-tests-server-cli, e2e-tests-web]
|
needs: [e2e-tests-server-cli, e2e-tests-server-maintenance, e2e-tests-web, e2e-tests-web-ui, e2e-tests-web-maintenance]
|
||||||
permissions: {}
|
permissions: {}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -23,10 +23,18 @@ import okhttp3.HttpUrl
|
|||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.chromium.net.CronetEngine
|
import org.chromium.net.CronetEngine
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.FileVisitResult
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.SimpleFileVisitor
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
import java.net.CookieHandler
|
import java.net.CookieHandler
|
||||||
import java.net.PasswordAuthentication
|
import java.net.PasswordAuthentication
|
||||||
@@ -277,10 +285,13 @@ object HttpClientManager {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rebuildCronetEngine(): CronetEngine {
|
suspend fun rebuildCronetEngine(): Result<Long> {
|
||||||
val old = cronetEngine!!
|
return runCatching {
|
||||||
cronetEngine = buildCronetEngine()
|
cronetEngine?.shutdown()
|
||||||
return old
|
val deletionResult = deleteFolderAndGetSize(cronetStoragePath.toPath())
|
||||||
|
cronetEngine = buildCronetEngine()
|
||||||
|
deletionResult
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cronetStoragePath: File get() = cronetStorageDir
|
val cronetStoragePath: File get() = cronetStorageDir
|
||||||
@@ -301,7 +312,7 @@ object HttpClientManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildCronetEngine(): CronetEngine {
|
fun buildCronetEngine(): CronetEngine {
|
||||||
return CronetEngine.Builder(appContext)
|
return CronetEngine.Builder(appContext)
|
||||||
.enableHttp2(true)
|
.enableHttp2(true)
|
||||||
.enableQuic(true)
|
.enableQuic(true)
|
||||||
@@ -312,6 +323,27 @@ object HttpClientManager {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun deleteFolderAndGetSize(root: Path): Long = withContext(Dispatchers.IO) {
|
||||||
|
var totalSize = 0L
|
||||||
|
|
||||||
|
Files.walkFileTree(root, object : SimpleFileVisitor<Path>() {
|
||||||
|
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||||
|
totalSize += attrs.size()
|
||||||
|
Files.delete(file)
|
||||||
|
return FileVisitResult.CONTINUE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult {
|
||||||
|
if (dir != root) {
|
||||||
|
Files.delete(dir)
|
||||||
|
}
|
||||||
|
return FileVisitResult.CONTINUE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
totalSize
|
||||||
|
}
|
||||||
|
|
||||||
private fun build(cacheDir: File): OkHttpClient {
|
private fun build(cacheDir: File): OkHttpClient {
|
||||||
val connectionPool = ConnectionPool(
|
val connectionPool = ConnectionPool(
|
||||||
maxIdleConnections = KEEP_ALIVE_CONNECTIONS,
|
maxIdleConnections = KEEP_ALIVE_CONNECTIONS,
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ import java.io.EOFException
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.file.FileVisitResult
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.SimpleFileVisitor
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
private class RemoteRequest(val cancellationSignal: CancellationSignal)
|
private class RemoteRequest(val cancellationSignal: CancellationSignal)
|
||||||
@@ -205,18 +200,15 @@ private class CronetImageFetcher : ImageFetcher {
|
|||||||
|
|
||||||
private fun onDrained() {
|
private fun onDrained() {
|
||||||
val onCacheCleared = synchronized(stateLock) {
|
val onCacheCleared = synchronized(stateLock) {
|
||||||
val onCacheCleared = onCacheCleared
|
val onCacheCleared = this.onCacheCleared
|
||||||
this.onCacheCleared = null
|
this.onCacheCleared = null
|
||||||
onCacheCleared
|
onCacheCleared
|
||||||
}
|
} ?: return
|
||||||
if (onCacheCleared != null) {
|
|
||||||
val oldEngine = HttpClientManager.rebuildCronetEngine()
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
oldEngine.shutdown()
|
val result = HttpClientManager.rebuildCronetEngine()
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
synchronized(stateLock) { draining = false }
|
||||||
val result = runCatching { deleteFolderAndGetSize(HttpClientManager.cronetStoragePath.toPath()) }
|
onCacheCleared(result)
|
||||||
synchronized(stateLock) { draining = false }
|
|
||||||
onCacheCleared(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,26 +298,6 @@ private class CronetImageFetcher : ImageFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteFolderAndGetSize(root: Path): Long = withContext(Dispatchers.IO) {
|
|
||||||
var totalSize = 0L
|
|
||||||
|
|
||||||
Files.walkFileTree(root, object : SimpleFileVisitor<Path>() {
|
|
||||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
|
||||||
totalSize += attrs.size()
|
|
||||||
Files.delete(file)
|
|
||||||
return FileVisitResult.CONTINUE
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult {
|
|
||||||
if (dir != root) {
|
|
||||||
Files.delete(dir)
|
|
||||||
}
|
|
||||||
return FileVisitResult.CONTINUE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
totalSize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OkHttpImageFetcher private constructor(
|
private class OkHttpImageFetcher private constructor(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const Map<String, Locale> locales = {
|
|||||||
'Arabic (ar)': Locale('ar'),
|
'Arabic (ar)': Locale('ar'),
|
||||||
'Bulgarian (bg)': Locale('bg'),
|
'Bulgarian (bg)': Locale('bg'),
|
||||||
'Catalan (ca)': Locale('ca'),
|
'Catalan (ca)': Locale('ca'),
|
||||||
'Chinese Simplified (zh_CN)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'),
|
'Chinese Simplified (zh_CN)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'),
|
||||||
'Chinese Traditional (zh_TW)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
'Chinese Traditional (zh_TW)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
||||||
'Croatian (hr)': Locale('hr'),
|
'Croatian (hr)': Locale('hr'),
|
||||||
'Czech (cs)': Locale('cs'),
|
'Czech (cs)': Locale('cs'),
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ import 'package:flutter/painting.dart';
|
|||||||
/// Codec is disposed through the MultiFrameImageStreamCompleter's internals onDispose method
|
/// Codec is disposed through the MultiFrameImageStreamCompleter's internals onDispose method
|
||||||
class AnimatedImageStreamCompleter extends MultiFrameImageStreamCompleter {
|
class AnimatedImageStreamCompleter extends MultiFrameImageStreamCompleter {
|
||||||
void Function()? _onLastListenerRemoved;
|
void Function()? _onLastListenerRemoved;
|
||||||
ImageStreamListener? _cacheListener;
|
int _listenerCount = 0;
|
||||||
|
// True once any image or the codec has been provided.
|
||||||
|
// Until then the image cache holds one listener, so "last real listener gone"
|
||||||
|
// is _listenerCount == 1, not 0.
|
||||||
|
bool didProvideImage = false;
|
||||||
|
|
||||||
AnimatedImageStreamCompleter._({
|
AnimatedImageStreamCompleter._({
|
||||||
required super.codec,
|
required super.codec,
|
||||||
@@ -34,15 +38,18 @@ class AnimatedImageStreamCompleter extends MultiFrameImageStreamCompleter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (initialImage != null) {
|
if (initialImage != null) {
|
||||||
|
self.didProvideImage = true;
|
||||||
self.setImage(initialImage);
|
self.setImage(initialImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.listen(
|
stream.listen(
|
||||||
(item) {
|
(item) {
|
||||||
if (item is ImageInfo) {
|
if (item is ImageInfo) {
|
||||||
|
self.didProvideImage = true;
|
||||||
self.setImage(item);
|
self.setImage(item);
|
||||||
} else if (item is ui.Codec) {
|
} else if (item is ui.Codec) {
|
||||||
if (!codecCompleter.isCompleted) {
|
if (!codecCompleter.isCompleted) {
|
||||||
|
self.didProvideImage = true;
|
||||||
codecCompleter.complete(item);
|
codecCompleter.complete(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,6 +60,8 @@ class AnimatedImageStreamCompleter extends MultiFrameImageStreamCompleter {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDone: () {
|
onDone: () {
|
||||||
|
// also complete if we are done but no error occurred, and we didn't call complete yet
|
||||||
|
// could happen on cancellation
|
||||||
if (!codecCompleter.isCompleted) {
|
if (!codecCompleter.isCompleted) {
|
||||||
codecCompleter.completeError(StateError('Stream closed without providing a codec'));
|
codecCompleter.completeError(StateError('Stream closed without providing a codec'));
|
||||||
}
|
}
|
||||||
@@ -64,20 +73,24 @@ class AnimatedImageStreamCompleter extends MultiFrameImageStreamCompleter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void addListener(ImageStreamListener listener) {
|
void addListener(ImageStreamListener listener) {
|
||||||
_cacheListener ??= listener;
|
|
||||||
super.addListener(listener);
|
super.addListener(listener);
|
||||||
|
_listenerCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void removeListener(ImageStreamListener listener) {
|
void removeListener(ImageStreamListener listener) {
|
||||||
super.removeListener(listener);
|
super.removeListener(listener);
|
||||||
if (listener != _cacheListener) {
|
_listenerCount--;
|
||||||
_cancel();
|
|
||||||
|
final bool onlyCacheListenerLeft = _listenerCount == 1 && !didProvideImage;
|
||||||
|
final bool noListenersAfterCodec = _listenerCount == 0 && didProvideImage;
|
||||||
|
|
||||||
|
if (onlyCacheListenerLeft || noListenersAfterCodec) {
|
||||||
|
final onLastListenerRemoved = _onLastListenerRemoved;
|
||||||
|
if (onLastListenerRemoved != null) {
|
||||||
|
_onLastListenerRemoved = null;
|
||||||
|
onLastListenerRemoved();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cancel() {
|
|
||||||
_onLastListenerRemoved?.call();
|
|
||||||
_onLastListenerRemoved = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-11
@@ -10,7 +10,9 @@ import 'package:flutter/painting.dart';
|
|||||||
/// An ImageStreamCompleter with support for loading multiple images.
|
/// An ImageStreamCompleter with support for loading multiple images.
|
||||||
class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter {
|
class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter {
|
||||||
void Function()? _onLastListenerRemoved;
|
void Function()? _onLastListenerRemoved;
|
||||||
ImageStreamListener? _cacheListener;
|
int _listenerCount = 0;
|
||||||
|
// True once setImage() has been called at least once.
|
||||||
|
bool didProvideImage = false;
|
||||||
|
|
||||||
/// The constructor to create an OneFramePlaceholderImageStreamCompleter. The [images]
|
/// The constructor to create an OneFramePlaceholderImageStreamCompleter. The [images]
|
||||||
/// should be the primary images to display (typically asynchronously as they load).
|
/// should be the primary images to display (typically asynchronously as they load).
|
||||||
@@ -21,12 +23,17 @@ class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
ImageInfo? initialImage,
|
ImageInfo? initialImage,
|
||||||
InformationCollector? informationCollector,
|
InformationCollector? informationCollector,
|
||||||
void Function()? onLastListenerRemoved,
|
void Function()? onLastListenerRemoved,
|
||||||
}) : _onLastListenerRemoved = onLastListenerRemoved {
|
}) {
|
||||||
if (initialImage != null) {
|
if (initialImage != null) {
|
||||||
|
didProvideImage = true;
|
||||||
setImage(initialImage);
|
setImage(initialImage);
|
||||||
}
|
}
|
||||||
|
_onLastListenerRemoved = onLastListenerRemoved;
|
||||||
images.listen(
|
images.listen(
|
||||||
(image) => setImage(image),
|
(image) {
|
||||||
|
didProvideImage = true;
|
||||||
|
setImage(image);
|
||||||
|
},
|
||||||
onError: (Object error, StackTrace stack) {
|
onError: (Object error, StackTrace stack) {
|
||||||
reportError(
|
reportError(
|
||||||
context: ErrorDescription('resolving a single-frame image stream'),
|
context: ErrorDescription('resolving a single-frame image stream'),
|
||||||
@@ -41,20 +48,23 @@ class OneFramePlaceholderImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void addListener(ImageStreamListener listener) {
|
void addListener(ImageStreamListener listener) {
|
||||||
_cacheListener ??= listener;
|
|
||||||
super.addListener(listener);
|
super.addListener(listener);
|
||||||
|
_listenerCount = _listenerCount + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void removeListener(ImageStreamListener listener) {
|
void removeListener(ImageStreamListener listener) {
|
||||||
super.removeListener(listener);
|
super.removeListener(listener);
|
||||||
if (listener != _cacheListener) {
|
_listenerCount = _listenerCount - 1;
|
||||||
_cancel();
|
|
||||||
|
final bool onlyCacheListenerLeft = _listenerCount == 1 && !didProvideImage;
|
||||||
|
final bool noListenersAfterImage = _listenerCount == 0 && didProvideImage;
|
||||||
|
|
||||||
|
final onLastListenerRemoved = _onLastListenerRemoved;
|
||||||
|
|
||||||
|
if (onLastListenerRemoved != null && (noListenersAfterImage || onlyCacheListenerLeft)) {
|
||||||
|
_onLastListenerRemoved = null;
|
||||||
|
onLastListenerRemoved();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cancel() {
|
|
||||||
_onLastListenerRemoved?.call();
|
|
||||||
_onLastListenerRemoved = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ final class CustomImageCache implements ImageCache {
|
|||||||
set maximumSize(int value) => _small.maximumSize = value;
|
set maximumSize(int value) => _small.maximumSize = value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
set maximumSizeBytes(int value) => _small.maximumSize = value;
|
set maximumSizeBytes(int value) => _small.maximumSizeBytes = value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clear() {
|
void clear() {
|
||||||
|
|||||||
+4
-4
@@ -1194,10 +1194,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1897,10 +1897,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6"
|
version: "0.7.7"
|
||||||
thumbhash:
|
thumbhash:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
Generated
+86
-81
@@ -748,7 +748,7 @@ importers:
|
|||||||
version: link:../open-api/typescript-sdk
|
version: link:../open-api/typescript-sdk
|
||||||
'@immich/ui':
|
'@immich/ui':
|
||||||
specifier: ^0.65.3
|
specifier: ^0.65.3
|
||||||
version: 0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)
|
version: 0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)
|
||||||
'@mapbox/mapbox-gl-rtl-text':
|
'@mapbox/mapbox-gl-rtl-text':
|
||||||
specifier: 0.3.0
|
specifier: 0.3.0
|
||||||
version: 0.3.0
|
version: 0.3.0
|
||||||
@@ -781,7 +781,7 @@ importers:
|
|||||||
version: 0.42.0
|
version: 0.42.0
|
||||||
'@zoom-image/svelte':
|
'@zoom-image/svelte':
|
||||||
specifier: ^0.3.0
|
specifier: ^0.3.0
|
||||||
version: 0.3.9(svelte@5.53.7)
|
version: 0.3.9(svelte@5.53.13)
|
||||||
dom-to-image:
|
dom-to-image:
|
||||||
specifier: ^2.6.0
|
specifier: ^2.6.0
|
||||||
version: 2.6.0
|
version: 2.6.0
|
||||||
@@ -832,16 +832,16 @@ importers:
|
|||||||
version: 5.2.2
|
version: 5.2.2
|
||||||
svelte-i18n:
|
svelte-i18n:
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1(svelte@5.53.7)
|
version: 4.0.1(svelte@5.53.13)
|
||||||
svelte-jsoneditor:
|
svelte-jsoneditor:
|
||||||
specifier: ^3.10.0
|
specifier: ^3.10.0
|
||||||
version: 3.11.0(svelte@5.53.7)
|
version: 3.11.0(svelte@5.53.13)
|
||||||
svelte-maplibre:
|
svelte-maplibre:
|
||||||
specifier: ^1.2.5
|
specifier: ^1.2.5
|
||||||
version: 1.2.6(svelte@5.53.7)
|
version: 1.2.6(svelte@5.53.13)
|
||||||
svelte-persisted-store:
|
svelte-persisted-store:
|
||||||
specifier: ^0.12.0
|
specifier: ^0.12.0
|
||||||
version: 0.12.0(svelte@5.53.7)
|
version: 0.12.0(svelte@5.53.13)
|
||||||
tabbable:
|
tabbable:
|
||||||
specifier: ^6.2.0
|
specifier: ^6.2.0
|
||||||
version: 6.4.0
|
version: 6.4.0
|
||||||
@@ -875,16 +875,16 @@ importers:
|
|||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
'@sveltejs/adapter-static':
|
'@sveltejs/adapter-static':
|
||||||
specifier: ^3.0.8
|
specifier: ^3.0.8
|
||||||
version: 3.0.10(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))
|
version: 3.0.10(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))
|
||||||
'@sveltejs/enhanced-img':
|
'@sveltejs/enhanced-img':
|
||||||
specifier: ^0.10.0
|
specifier: ^0.10.0
|
||||||
version: 0.10.3(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(rollup@4.55.1)(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 0.10.3(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(rollup@4.55.1)(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
'@sveltejs/kit':
|
'@sveltejs/kit':
|
||||||
specifier: ^2.27.1
|
specifier: ^2.27.1
|
||||||
version: 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
specifier: 6.2.4
|
specifier: 6.2.4
|
||||||
version: 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.7
|
specifier: ^4.1.7
|
||||||
version: 4.2.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 4.2.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
@@ -893,7 +893,7 @@ importers:
|
|||||||
version: 6.9.1
|
version: 6.9.1
|
||||||
'@testing-library/svelte':
|
'@testing-library/svelte':
|
||||||
specifier: ^5.2.8
|
specifier: ^5.2.8
|
||||||
version: 5.3.1(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
version: 5.3.1(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
'@testing-library/user-event':
|
'@testing-library/user-event':
|
||||||
specifier: ^14.5.2
|
specifier: ^14.5.2
|
||||||
version: 14.6.1(@testing-library/dom@10.4.1)
|
version: 14.6.1(@testing-library/dom@10.4.1)
|
||||||
@@ -932,7 +932,7 @@ importers:
|
|||||||
version: 6.2.1(eslint@10.0.2(jiti@2.6.1))
|
version: 6.2.1(eslint@10.0.2(jiti@2.6.1))
|
||||||
eslint-plugin-svelte:
|
eslint-plugin-svelte:
|
||||||
specifier: ^3.12.4
|
specifier: ^3.12.4
|
||||||
version: 3.15.0(eslint@10.0.2(jiti@2.6.1))(svelte@5.53.7)
|
version: 3.15.0(eslint@10.0.2(jiti@2.6.1))(svelte@5.53.13)
|
||||||
eslint-plugin-unicorn:
|
eslint-plugin-unicorn:
|
||||||
specifier: ^63.0.0
|
specifier: ^63.0.0
|
||||||
version: 63.0.0(eslint@10.0.2(jiti@2.6.1))
|
version: 63.0.0(eslint@10.0.2(jiti@2.6.1))
|
||||||
@@ -953,19 +953,19 @@ importers:
|
|||||||
version: 4.2.0(prettier@3.8.1)
|
version: 4.2.0(prettier@3.8.1)
|
||||||
prettier-plugin-svelte:
|
prettier-plugin-svelte:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.5.1(prettier@3.8.1)(svelte@5.53.7)
|
version: 3.5.1(prettier@3.8.1)(svelte@5.53.13)
|
||||||
rollup-plugin-visualizer:
|
rollup-plugin-visualizer:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.11(rollup@4.55.1)
|
version: 6.0.11(rollup@4.55.1)
|
||||||
svelte:
|
svelte:
|
||||||
specifier: 5.53.7
|
specifier: 5.53.13
|
||||||
version: 5.53.7
|
version: 5.53.13
|
||||||
svelte-check:
|
svelte-check:
|
||||||
specifier: ^4.1.5
|
specifier: ^4.1.5
|
||||||
version: 4.4.4(picomatch@4.0.3)(svelte@5.53.7)(typescript@5.9.3)
|
version: 4.4.4(picomatch@4.0.3)(svelte@5.53.13)(typescript@5.9.3)
|
||||||
svelte-eslint-parser:
|
svelte-eslint-parser:
|
||||||
specifier: ^1.3.3
|
specifier: ^1.3.3
|
||||||
version: 1.6.0(svelte@5.53.7)
|
version: 1.6.0(svelte@5.53.13)
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.1.7
|
specifier: ^4.1.7
|
||||||
version: 4.2.1
|
version: 4.2.1
|
||||||
@@ -6752,6 +6752,9 @@ packages:
|
|||||||
devalue@5.6.3:
|
devalue@5.6.3:
|
||||||
resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==}
|
resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==}
|
||||||
|
|
||||||
|
devalue@5.6.4:
|
||||||
|
resolution: {integrity: sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==}
|
||||||
|
|
||||||
devlop@1.1.0:
|
devlop@1.1.0:
|
||||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||||
|
|
||||||
@@ -11218,8 +11221,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^5.30.2
|
svelte: ^5.30.2
|
||||||
|
|
||||||
svelte@5.53.7:
|
svelte@5.53.13:
|
||||||
resolution: {integrity: sha512-uxck1KI7JWtlfP3H6HOWi/94soAl23jsGJkBzN2BAWcQng0+lTrRNhxActFqORgnO9BHVd1hKJhG+ljRuIUWfQ==}
|
resolution: {integrity: sha512-9P6I/jGcQMzAMb76Uyd6L6RELAC7qt53GOSBLCke9lubh9iJjmjCo+EffRH4gOPnTB/x4RR2Tmt6s3o9ywQO3g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
svg-parser@2.0.4:
|
svg-parser@2.0.4:
|
||||||
@@ -14948,22 +14951,22 @@ snapshots:
|
|||||||
pg-connection-string: 2.12.0
|
pg-connection-string: 2.12.0
|
||||||
postgres: 3.4.8
|
postgres: 3.4.8
|
||||||
|
|
||||||
'@immich/svelte-markdown-preprocess@0.2.1(svelte@5.53.7)':
|
'@immich/svelte-markdown-preprocess@0.2.1(svelte@5.53.13)':
|
||||||
dependencies:
|
dependencies:
|
||||||
front-matter: 4.0.2
|
front-matter: 4.0.2
|
||||||
marked: 17.0.3
|
marked: 17.0.3
|
||||||
node-emoji: 2.2.0
|
node-emoji: 2.2.0
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
'@immich/ui@0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)':
|
'@immich/ui@0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@immich/svelte-markdown-preprocess': 0.2.1(svelte@5.53.7)
|
'@immich/svelte-markdown-preprocess': 0.2.1(svelte@5.53.13)
|
||||||
'@internationalized/date': 3.10.0
|
'@internationalized/date': 3.10.0
|
||||||
'@mdi/js': 7.4.47
|
'@mdi/js': 7.4.47
|
||||||
bits-ui: 2.16.0(@internationalized/date@3.10.0)(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)
|
bits-ui: 2.16.0(@internationalized/date@3.10.0)(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)
|
||||||
luxon: 3.7.2
|
luxon: 3.7.2
|
||||||
simple-icons: 16.9.0
|
simple-icons: 16.9.0
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
svelte-highlight: 7.9.0
|
svelte-highlight: 7.9.0
|
||||||
tailwind-merge: 3.5.0
|
tailwind-merge: 3.5.0
|
||||||
tailwind-variants: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
|
tailwind-variants: 3.2.2(tailwind-merge@3.5.0)(tailwindcss@4.2.1)
|
||||||
@@ -16300,17 +16303,17 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.16.0
|
acorn: 8.16.0
|
||||||
|
|
||||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))':
|
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/kit': 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/kit': 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
|
|
||||||
'@sveltejs/enhanced-img@0.10.3(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(rollup@4.55.1)(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
'@sveltejs/enhanced-img@0.10.3(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(rollup@4.55.1)(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
svelte-parse-markup: 0.1.5(svelte@5.53.7)
|
svelte-parse-markup: 0.1.5(svelte@5.53.13)
|
||||||
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
vite-imagetools: 9.0.3(rollup@4.55.1)
|
vite-imagetools: 9.0.3(rollup@4.55.1)
|
||||||
zimmerframe: 1.1.4
|
zimmerframe: 1.1.4
|
||||||
@@ -16318,11 +16321,11 @@ snapshots:
|
|||||||
- rollup
|
- rollup
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
'@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
'@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0)
|
'@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0)
|
||||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
'@types/cookie': 0.6.0
|
'@types/cookie': 0.6.0
|
||||||
acorn: 8.16.0
|
acorn: 8.16.0
|
||||||
cookie: 0.6.0
|
cookie: 0.6.0
|
||||||
@@ -16333,28 +16336,28 @@ snapshots:
|
|||||||
mrmime: 2.0.1
|
mrmime: 2.0.1
|
||||||
set-cookie-parser: 3.0.1
|
set-cookie-parser: 3.0.1
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
|
|
||||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
'@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
deepmerge: 4.3.1
|
deepmerge: 4.3.1
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
vitefu: 1.1.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
vitefu: 1.1.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -16602,15 +16605,15 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
redent: 3.0.0
|
redent: 3.0.0
|
||||||
|
|
||||||
'@testing-library/svelte-core@1.0.0(svelte@5.53.7)':
|
'@testing-library/svelte-core@1.0.0(svelte@5.53.13)':
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
'@testing-library/svelte@5.3.1(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
'@testing-library/svelte@5.3.1(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@testing-library/dom': 10.4.1
|
'@testing-library/dom': 10.4.1
|
||||||
'@testing-library/svelte-core': 1.0.0(svelte@5.53.7)
|
'@testing-library/svelte-core': 1.0.0(svelte@5.53.13)
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.3)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
|
||||||
@@ -17527,10 +17530,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@namnode/store': 0.1.0
|
'@namnode/store': 0.1.0
|
||||||
|
|
||||||
'@zoom-image/svelte@0.3.9(svelte@5.53.7)':
|
'@zoom-image/svelte@0.3.9(svelte@5.53.13)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@zoom-image/core': 0.42.0
|
'@zoom-image/core': 0.42.0
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
abbrev@1.1.1: {}
|
abbrev@1.1.1: {}
|
||||||
|
|
||||||
@@ -17894,15 +17897,15 @@ snapshots:
|
|||||||
|
|
||||||
binary-extensions@2.3.0: {}
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
bits-ui@2.16.0(@internationalized/date@3.10.0)(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7):
|
bits-ui@2.16.0(@internationalized/date@3.10.0)(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@floating-ui/core': 1.7.3
|
'@floating-ui/core': 1.7.3
|
||||||
'@floating-ui/dom': 1.7.4
|
'@floating-ui/dom': 1.7.4
|
||||||
'@internationalized/date': 3.10.0
|
'@internationalized/date': 3.10.0
|
||||||
esm-env: 1.2.2
|
esm-env: 1.2.2
|
||||||
runed: 0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)
|
runed: 0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
svelte-toolbelt: 0.10.6(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)
|
svelte-toolbelt: 0.10.6(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)
|
||||||
tabbable: 6.4.0
|
tabbable: 6.4.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@sveltejs/kit'
|
- '@sveltejs/kit'
|
||||||
@@ -18995,6 +18998,8 @@ snapshots:
|
|||||||
|
|
||||||
devalue@5.6.3: {}
|
devalue@5.6.3: {}
|
||||||
|
|
||||||
|
devalue@5.6.4: {}
|
||||||
|
|
||||||
devlop@1.1.0:
|
devlop@1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
@@ -19403,7 +19408,7 @@ snapshots:
|
|||||||
'@types/eslint': 9.6.1
|
'@types/eslint': 9.6.1
|
||||||
eslint-config-prettier: 10.1.8(eslint@10.0.2(jiti@2.6.1))
|
eslint-config-prettier: 10.1.8(eslint@10.0.2(jiti@2.6.1))
|
||||||
|
|
||||||
eslint-plugin-svelte@3.15.0(eslint@10.0.2(jiti@2.6.1))(svelte@5.53.7):
|
eslint-plugin-svelte@3.15.0(eslint@10.0.2(jiti@2.6.1))(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1))
|
'@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1))
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
@@ -19415,9 +19420,9 @@ snapshots:
|
|||||||
postcss-load-config: 3.1.4(postcss@8.5.8)
|
postcss-load-config: 3.1.4(postcss@8.5.8)
|
||||||
postcss-safe-parser: 7.0.1(postcss@8.5.8)
|
postcss-safe-parser: 7.0.1(postcss@8.5.8)
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
svelte-eslint-parser: 1.6.0(svelte@5.53.7)
|
svelte-eslint-parser: 1.6.0(svelte@5.53.13)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
@@ -23155,10 +23160,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prettier: 3.8.1
|
prettier: 3.8.1
|
||||||
|
|
||||||
prettier-plugin-svelte@3.5.1(prettier@3.8.1)(svelte@5.53.7):
|
prettier-plugin-svelte@3.5.1(prettier@3.8.1)(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
prettier: 3.8.1
|
prettier: 3.8.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
prettier@3.8.1: {}
|
prettier@3.8.1: {}
|
||||||
|
|
||||||
@@ -23763,14 +23768,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
queue-microtask: 1.2.3
|
queue-microtask: 1.2.3
|
||||||
|
|
||||||
runed@0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7):
|
runed@0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
esm-env: 1.2.2
|
esm-env: 1.2.2
|
||||||
lz-string: 1.5.0
|
lz-string: 1.5.0
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@sveltejs/kit': 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
'@sveltejs/kit': 2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
|
||||||
|
|
||||||
rw@1.3.3: {}
|
rw@1.3.3: {}
|
||||||
|
|
||||||
@@ -24394,23 +24399,23 @@ snapshots:
|
|||||||
|
|
||||||
supports-preserve-symlinks-flag@1.0.0: {}
|
supports-preserve-symlinks-flag@1.0.0: {}
|
||||||
|
|
||||||
svelte-awesome@3.3.5(svelte@5.53.7):
|
svelte-awesome@3.3.5(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
svelte-check@4.4.4(picomatch@4.0.3)(svelte@5.53.7)(typescript@5.9.3):
|
svelte-check@4.4.4(picomatch@4.0.3)(svelte@5.53.13)(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.31
|
'@jridgewell/trace-mapping': 0.3.31
|
||||||
chokidar: 4.0.3
|
chokidar: 4.0.3
|
||||||
fdir: 6.5.0(picomatch@4.0.3)
|
fdir: 6.5.0(picomatch@4.0.3)
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
sade: 1.8.1
|
sade: 1.8.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- picomatch
|
- picomatch
|
||||||
|
|
||||||
svelte-eslint-parser@1.6.0(svelte@5.53.7):
|
svelte-eslint-parser@1.6.0(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-scope: 8.4.0
|
eslint-scope: 8.4.0
|
||||||
eslint-visitor-keys: 4.2.1
|
eslint-visitor-keys: 4.2.1
|
||||||
@@ -24420,7 +24425,7 @@ snapshots:
|
|||||||
postcss-selector-parser: 7.1.1
|
postcss-selector-parser: 7.1.1
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
svelte-floating-ui@1.5.8:
|
svelte-floating-ui@1.5.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -24433,7 +24438,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
highlight.js: 11.11.1
|
highlight.js: 11.11.1
|
||||||
|
|
||||||
svelte-i18n@4.0.1(svelte@5.53.7):
|
svelte-i18n@4.0.1(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
cli-color: 2.0.4
|
cli-color: 2.0.4
|
||||||
deepmerge: 4.3.1
|
deepmerge: 4.3.1
|
||||||
@@ -24441,10 +24446,10 @@ snapshots:
|
|||||||
estree-walker: 2.0.2
|
estree-walker: 2.0.2
|
||||||
intl-messageformat: 10.7.18
|
intl-messageformat: 10.7.18
|
||||||
sade: 1.8.1
|
sade: 1.8.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
tiny-glob: 0.2.9
|
tiny-glob: 0.2.9
|
||||||
|
|
||||||
svelte-jsoneditor@3.11.0(svelte@5.53.7):
|
svelte-jsoneditor@3.11.0(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@codemirror/autocomplete': 6.20.0
|
'@codemirror/autocomplete': 6.20.0
|
||||||
'@codemirror/commands': 6.10.1
|
'@codemirror/commands': 6.10.1
|
||||||
@@ -24471,42 +24476,42 @@ snapshots:
|
|||||||
memoize-one: 6.0.0
|
memoize-one: 6.0.0
|
||||||
natural-compare-lite: 1.4.0
|
natural-compare-lite: 1.4.0
|
||||||
sass: 1.97.1
|
sass: 1.97.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
svelte-awesome: 3.3.5(svelte@5.53.7)
|
svelte-awesome: 3.3.5(svelte@5.53.13)
|
||||||
svelte-select: 5.8.3
|
svelte-select: 5.8.3
|
||||||
vanilla-picker: 2.12.3
|
vanilla-picker: 2.12.3
|
||||||
|
|
||||||
svelte-maplibre@1.2.6(svelte@5.53.7):
|
svelte-maplibre@1.2.6(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
d3-geo: 3.1.1
|
d3-geo: 3.1.1
|
||||||
dequal: 2.0.3
|
dequal: 2.0.3
|
||||||
just-compare: 2.3.0
|
just-compare: 2.3.0
|
||||||
maplibre-gl: 5.19.0
|
maplibre-gl: 5.19.0
|
||||||
pmtiles: 3.2.1
|
pmtiles: 3.2.1
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
svelte-parse-markup@0.1.5(svelte@5.53.7):
|
svelte-parse-markup@0.1.5(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
svelte-persisted-store@0.12.0(svelte@5.53.7):
|
svelte-persisted-store@0.12.0(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
|
|
||||||
svelte-select@5.8.3:
|
svelte-select@5.8.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte-floating-ui: 1.5.8
|
svelte-floating-ui: 1.5.8
|
||||||
|
|
||||||
svelte-toolbelt@0.10.6(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7):
|
svelte-toolbelt@0.10.6(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
runed: 0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.7)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.7)
|
runed: 0.35.1(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.13)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)
|
||||||
style-to-object: 1.0.14
|
style-to-object: 1.0.14
|
||||||
svelte: 5.53.7
|
svelte: 5.53.13
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@sveltejs/kit'
|
- '@sveltejs/kit'
|
||||||
|
|
||||||
svelte@5.53.7:
|
svelte@5.53.13:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/remapping': 2.3.5
|
'@jridgewell/remapping': 2.3.5
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
@@ -24517,7 +24522,7 @@ snapshots:
|
|||||||
aria-query: 5.3.1
|
aria-query: 5.3.1
|
||||||
axobject-query: 4.1.0
|
axobject-query: 4.1.0
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
devalue: 5.6.3
|
devalue: 5.6.4
|
||||||
esm-env: 1.2.2
|
esm-env: 1.2.2
|
||||||
esrap: 2.2.3
|
esrap: 2.2.3
|
||||||
is-reference: 3.0.3
|
is-reference: 3.0.3
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@
|
|||||||
"typeorm": "typeorm",
|
"typeorm": "typeorm",
|
||||||
"migrations:debug": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations generate --debug",
|
"migrations:debug": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations generate --debug",
|
||||||
"migrations:generate": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations generate",
|
"migrations:generate": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations generate",
|
||||||
"migrations:create": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations generate",
|
"migrations:create": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations create",
|
||||||
"migrations:run": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations run",
|
"migrations:run": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations run",
|
||||||
"migrations:revert": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations revert",
|
"migrations:revert": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} migrations revert",
|
||||||
"schema:drop": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} query 'DROP schema public cascade; CREATE schema public;'",
|
"schema:drop": "sql-tools -u ${DB_URL:-postgres://postgres:postgres@localhost:5432/immich} query 'DROP schema public cascade; CREATE schema public;'",
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export type AuthSharedLink = {
|
|||||||
id: string;
|
id: string;
|
||||||
expiresAt: Date | null;
|
expiresAt: Date | null;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
albumId: string | null;
|
||||||
showExif: boolean;
|
showExif: boolean;
|
||||||
allowUpload: boolean;
|
allowUpload: boolean;
|
||||||
allowDownload: boolean;
|
allowDownload: boolean;
|
||||||
@@ -357,15 +358,6 @@ export const columns = {
|
|||||||
authUser: ['user.id', 'user.name', 'user.email', 'user.isAdmin', 'user.quotaUsageInBytes', 'user.quotaSizeInBytes'],
|
authUser: ['user.id', 'user.name', 'user.email', 'user.isAdmin', 'user.quotaUsageInBytes', 'user.quotaSizeInBytes'],
|
||||||
authApiKey: ['api_key.id', 'api_key.permissions'],
|
authApiKey: ['api_key.id', 'api_key.permissions'],
|
||||||
authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt', 'session.appVersion'],
|
authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt', 'session.appVersion'],
|
||||||
authSharedLink: [
|
|
||||||
'shared_link.id',
|
|
||||||
'shared_link.userId',
|
|
||||||
'shared_link.expiresAt',
|
|
||||||
'shared_link.showExif',
|
|
||||||
'shared_link.allowUpload',
|
|
||||||
'shared_link.allowDownload',
|
|
||||||
'shared_link.password',
|
|
||||||
],
|
|
||||||
user: userColumns,
|
user: userColumns,
|
||||||
userWithPrefix: userWithPrefixColumns,
|
userWithPrefix: userWithPrefixColumns,
|
||||||
userAdmin: [
|
userAdmin: [
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ order by
|
|||||||
select
|
select
|
||||||
"shared_link"."id",
|
"shared_link"."id",
|
||||||
"shared_link"."userId",
|
"shared_link"."userId",
|
||||||
|
"shared_link"."albumId",
|
||||||
"shared_link"."expiresAt",
|
"shared_link"."expiresAt",
|
||||||
"shared_link"."showExif",
|
"shared_link"."showExif",
|
||||||
"shared_link"."allowUpload",
|
"shared_link"."allowUpload",
|
||||||
@@ -211,6 +212,7 @@ where
|
|||||||
select
|
select
|
||||||
"shared_link"."id",
|
"shared_link"."id",
|
||||||
"shared_link"."userId",
|
"shared_link"."userId",
|
||||||
|
"shared_link"."albumId",
|
||||||
"shared_link"."expiresAt",
|
"shared_link"."expiresAt",
|
||||||
"shared_link"."showExif",
|
"shared_link"."showExif",
|
||||||
"shared_link"."allowUpload",
|
"shared_link"."allowUpload",
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ export class AlbumRepository {
|
|||||||
await db
|
await db
|
||||||
.insertInto('album_asset')
|
.insertInto('album_asset')
|
||||||
.values(assetIds.map((assetId) => ({ albumId, assetId })))
|
.values(assetIds.map((assetId) => ({ albumId, assetId })))
|
||||||
|
.onConflict((oc) => oc.doNothing())
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,14 @@ export class SharedLinkRepository {
|
|||||||
.leftJoin('album', 'album.id', 'shared_link.albumId')
|
.leftJoin('album', 'album.id', 'shared_link.albumId')
|
||||||
.where('album.deletedAt', 'is', null)
|
.where('album.deletedAt', 'is', null)
|
||||||
.select((eb) => [
|
.select((eb) => [
|
||||||
...columns.authSharedLink,
|
'shared_link.id',
|
||||||
|
'shared_link.userId',
|
||||||
|
'shared_link.albumId',
|
||||||
|
'shared_link.expiresAt',
|
||||||
|
'shared_link.showExif',
|
||||||
|
'shared_link.allowUpload',
|
||||||
|
'shared_link.allowDownload',
|
||||||
|
'shared_link.password',
|
||||||
jsonObjectFrom(
|
jsonObjectFrom(
|
||||||
eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'),
|
eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'),
|
||||||
).as('user'),
|
).as('user'),
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`
|
||||||
|
DELETE FROM "shared_link_asset"
|
||||||
|
USING "shared_link"
|
||||||
|
WHERE "shared_link_asset"."sharedLinkId" = "shared_link"."id" AND "shared_link"."type" = 'ALBUM';
|
||||||
|
`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
@@ -165,6 +165,12 @@ export class AlbumService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||||
|
if (auth.sharedLink) {
|
||||||
|
this.logger.deprecate(
|
||||||
|
'Assets uploaded to a shared link are automatically added and calling this endpoint is no longer necessary. It will be removed in the next major release.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const album = await this.findOrFail(id, { withAssets: false });
|
const album = await this.findOrFail(id, { withAssets: false });
|
||||||
await this.requireAccess({ auth, permission: Permission.AlbumAssetCreate, ids: [id] });
|
await this.requireAccess({ auth, permission: Permission.AlbumAssetCreate, ids: [id] });
|
||||||
|
|
||||||
@@ -195,6 +201,12 @@ export class AlbumService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addAssetsToAlbums(auth: AuthDto, dto: AlbumsAddAssetsDto): Promise<AlbumsAddAssetsResponseDto> {
|
async addAssetsToAlbums(auth: AuthDto, dto: AlbumsAddAssetsDto): Promise<AlbumsAddAssetsResponseDto> {
|
||||||
|
if (auth.sharedLink) {
|
||||||
|
this.logger.deprecate(
|
||||||
|
'Assets uploaded to a shared link are automatically added and calling this endpoint is no longer necessary. It will be removed in the next major release.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const results: AlbumsAddAssetsResponseDto = {
|
const results: AlbumsAddAssetsResponseDto = {
|
||||||
success: false,
|
success: false,
|
||||||
error: BulkIdErrorReason.DUPLICATE,
|
error: BulkIdErrorReason.DUPLICATE,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { BadRequestException, Injectable, InternalServerErrorException, NotFound
|
|||||||
import { extname } from 'node:path';
|
import { extname } from 'node:path';
|
||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { Asset } from 'src/database';
|
import { Asset, AuthSharedLink } from 'src/database';
|
||||||
import {
|
import {
|
||||||
AssetBulkUploadCheckResponseDto,
|
AssetBulkUploadCheckResponseDto,
|
||||||
AssetMediaResponseDto,
|
AssetMediaResponseDto,
|
||||||
@@ -152,7 +152,7 @@ export class AssetMediaService extends BaseService {
|
|||||||
const asset = await this.create(auth.user.id, dto, file, sidecarFile);
|
const asset = await this.create(auth.user.id, dto, file, sidecarFile);
|
||||||
|
|
||||||
if (auth.sharedLink) {
|
if (auth.sharedLink) {
|
||||||
await this.sharedLinkRepository.addAssets(auth.sharedLink.id, [asset.id]);
|
await this.addToSharedLink(auth.sharedLink, asset.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userRepository.updateUsage(auth.user.id, file.size);
|
await this.userRepository.updateUsage(auth.user.id, file.size);
|
||||||
@@ -326,6 +326,12 @@ export class AssetMediaService extends BaseService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async addToSharedLink(sharedLink: AuthSharedLink, assetId: string) {
|
||||||
|
await (sharedLink.albumId
|
||||||
|
? this.albumRepository.addAssetIds(sharedLink.albumId, [assetId])
|
||||||
|
: this.sharedLinkRepository.addAssets(sharedLink.id, [assetId]));
|
||||||
|
}
|
||||||
|
|
||||||
private async handleUploadError(
|
private async handleUploadError(
|
||||||
error: any,
|
error: any,
|
||||||
auth: AuthDto,
|
auth: AuthDto,
|
||||||
@@ -347,7 +353,7 @@ export class AssetMediaService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auth.sharedLink) {
|
if (auth.sharedLink) {
|
||||||
await this.sharedLinkRepository.addAssets(auth.sharedLink.id, [duplicateId]);
|
await this.addToSharedLink(auth.sharedLink, duplicateId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { status: AssetMediaStatus.DUPLICATE, id: duplicateId };
|
return { status: AssetMediaStatus.DUPLICATE, id: duplicateId };
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { SemVer } from 'semver';
|
import { SemVer } from 'semver';
|
||||||
|
import { defaults } from 'src/config';
|
||||||
import { serverVersion } from 'src/constants';
|
import { serverVersion } from 'src/constants';
|
||||||
import { ImmichEnvironment, JobName, JobStatus, SystemMetadataKey } from 'src/enum';
|
import { ImmichEnvironment, JobName, JobStatus, SystemMetadataKey } from 'src/enum';
|
||||||
import { VersionService } from 'src/services/version.service';
|
import { VersionService } from 'src/services/version.service';
|
||||||
@@ -130,6 +131,32 @@ describe(VersionService.name, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('onConfigUpdate', () => {
|
||||||
|
it('should queue a version check job when newVersionCheck is enabled', async () => {
|
||||||
|
await sut.onConfigUpdate({
|
||||||
|
oldConfig: { ...defaults, newVersionCheck: { enabled: false } },
|
||||||
|
newConfig: { ...defaults, newVersionCheck: { enabled: true } },
|
||||||
|
});
|
||||||
|
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.VersionCheck, data: {} });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not queue a version check job when newVersionCheck is disabled', async () => {
|
||||||
|
await sut.onConfigUpdate({
|
||||||
|
oldConfig: { ...defaults, newVersionCheck: { enabled: true } },
|
||||||
|
newConfig: { ...defaults, newVersionCheck: { enabled: false } },
|
||||||
|
});
|
||||||
|
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not queue a version check job when newVersionCheck was already enabled', async () => {
|
||||||
|
await sut.onConfigUpdate({
|
||||||
|
oldConfig: { ...defaults, newVersionCheck: { enabled: true } },
|
||||||
|
newConfig: { ...defaults, newVersionCheck: { enabled: true } },
|
||||||
|
});
|
||||||
|
expect(mocks.job.queue).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('onWebsocketConnection', () => {
|
describe('onWebsocketConnection', () => {
|
||||||
it('should send on_server_version client event', async () => {
|
it('should send on_server_version client event', async () => {
|
||||||
await sut.onWebsocketConnection({ userId: '42' });
|
await sut.onWebsocketConnection({ userId: '42' });
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ export class VersionService extends BaseService {
|
|||||||
return this.versionRepository.getAll();
|
return this.versionRepository.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnEvent({ name: 'ConfigUpdate' })
|
||||||
|
async onConfigUpdate({ oldConfig, newConfig }: ArgOf<'ConfigUpdate'>) {
|
||||||
|
if (!oldConfig.newVersionCheck.enabled && newConfig.newVersionCheck.enabled) {
|
||||||
|
await this.handleQueueVersionCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async handleQueueVersionCheck() {
|
async handleQueueVersionCheck() {
|
||||||
await this.jobRepository.queue({ name: JobName.VersionCheck, data: {} });
|
await this.jobRepository.queue({ name: JobName.VersionCheck, data: {} });
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
@@ -48,6 +48,7 @@ export const authStub = {
|
|||||||
showExif: true,
|
showExif: true,
|
||||||
allowDownload: true,
|
allowDownload: true,
|
||||||
allowUpload: true,
|
allowUpload: true,
|
||||||
|
albumId: null,
|
||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
password: null,
|
password: null,
|
||||||
userId: '42',
|
userId: '42',
|
||||||
|
|||||||
@@ -220,9 +220,9 @@ export class MediumTestContext<S extends BaseService = BaseService> {
|
|||||||
return { result };
|
return { result };
|
||||||
}
|
}
|
||||||
|
|
||||||
async newAlbum(dto: Insertable<AlbumTable>) {
|
async newAlbum(dto: Insertable<AlbumTable>, assetIds?: string[]) {
|
||||||
const album = mediumFactory.albumInsert(dto);
|
const album = mediumFactory.albumInsert(dto);
|
||||||
const result = await this.get(AlbumRepository).create(album, [], []);
|
const result = await this.get(AlbumRepository).create(album, assetIds ?? [], []);
|
||||||
return { album, result };
|
return { album, result };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { Kysely } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
|
import { randomBytes } from 'node:crypto';
|
||||||
import { AssetMediaStatus } from 'src/dtos/asset-media-response.dto';
|
import { AssetMediaStatus } from 'src/dtos/asset-media-response.dto';
|
||||||
import { AssetMediaSize } from 'src/dtos/asset-media.dto';
|
import { AssetMediaSize } from 'src/dtos/asset-media.dto';
|
||||||
import { AssetFileType } from 'src/enum';
|
import { AssetFileType, SharedLinkType } from 'src/enum';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
|
import { AlbumRepository } from 'src/repositories/album.repository';
|
||||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||||
import { EventRepository } from 'src/repositories/event.repository';
|
import { EventRepository } from 'src/repositories/event.repository';
|
||||||
import { JobRepository } from 'src/repositories/job.repository';
|
import { JobRepository } from 'src/repositories/job.repository';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { SharedLinkRepository } from 'src/repositories/shared-link.repository';
|
||||||
import { StorageRepository } from 'src/repositories/storage.repository';
|
import { StorageRepository } from 'src/repositories/storage.repository';
|
||||||
import { UserRepository } from 'src/repositories/user.repository';
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
import { DB } from 'src/schema';
|
import { DB } from 'src/schema';
|
||||||
@@ -22,7 +25,7 @@ let defaultDatabase: Kysely<DB>;
|
|||||||
const setup = (db?: Kysely<DB>) => {
|
const setup = (db?: Kysely<DB>) => {
|
||||||
return newMediumService(AssetMediaService, {
|
return newMediumService(AssetMediaService, {
|
||||||
database: db || defaultDatabase,
|
database: db || defaultDatabase,
|
||||||
real: [AccessRepository, AssetRepository, UserRepository],
|
real: [AccessRepository, AlbumRepository, AssetRepository, SharedLinkRepository, UserRepository],
|
||||||
mock: [EventRepository, LoggingRepository, JobRepository, StorageRepository],
|
mock: [EventRepository, LoggingRepository, JobRepository, StorageRepository],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -44,7 +47,6 @@ describe(AssetService.name, () => {
|
|||||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
||||||
const auth = factory.auth({ user: { id: user.id } });
|
const auth = factory.auth({ user: { id: user.id } });
|
||||||
const file = mediumFactory.uploadFile();
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
sut.uploadAsset(
|
sut.uploadAsset(
|
||||||
@@ -56,7 +58,7 @@ describe(AssetService.name, () => {
|
|||||||
fileCreatedAt: new Date(),
|
fileCreatedAt: new Date(),
|
||||||
assetData: Buffer.from('some data'),
|
assetData: Buffer.from('some data'),
|
||||||
},
|
},
|
||||||
file,
|
mediumFactory.uploadFile(),
|
||||||
),
|
),
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
@@ -99,6 +101,168 @@ describe(AssetService.name, () => {
|
|||||||
status: AssetMediaStatus.CREATED,
|
status: AssetMediaStatus.CREATED,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add to a shared link', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||||
|
|
||||||
|
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||||
|
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||||
|
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||||
|
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
|
||||||
|
const sharedLink = await sharedLinkRepo.create({
|
||||||
|
key: randomBytes(50),
|
||||||
|
type: SharedLinkType.Individual,
|
||||||
|
description: 'Shared link description',
|
||||||
|
userId: user.id,
|
||||||
|
allowDownload: true,
|
||||||
|
allowUpload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id }, sharedLink });
|
||||||
|
const file = mediumFactory.uploadFile();
|
||||||
|
const uploadDto = {
|
||||||
|
deviceId: 'some-id',
|
||||||
|
deviceAssetId: 'some-id',
|
||||||
|
fileModifiedAt: new Date(),
|
||||||
|
fileCreatedAt: new Date(),
|
||||||
|
assetData: Buffer.from('some data'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await sut.uploadAsset(auth, uploadDto, file);
|
||||||
|
expect(response).toEqual({ id: expect.any(String), status: AssetMediaStatus.CREATED });
|
||||||
|
|
||||||
|
const update = await sharedLinkRepo.get(user.id, sharedLink.id);
|
||||||
|
const assets = update!.assets;
|
||||||
|
expect(assets).toHaveLength(1);
|
||||||
|
expect(assets[0]).toMatchObject({ id: response.id });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle adding a duplicate asset to a shared link', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||||
|
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||||
|
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||||
|
|
||||||
|
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||||
|
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
||||||
|
|
||||||
|
const sharedLink = await sharedLinkRepo.create({
|
||||||
|
key: randomBytes(50),
|
||||||
|
type: SharedLinkType.Individual,
|
||||||
|
description: 'Shared link description',
|
||||||
|
userId: user.id,
|
||||||
|
allowDownload: true,
|
||||||
|
allowUpload: true,
|
||||||
|
assetIds: [asset.id],
|
||||||
|
});
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id }, sharedLink });
|
||||||
|
const uploadDto = {
|
||||||
|
deviceId: 'some-id',
|
||||||
|
deviceAssetId: 'some-id',
|
||||||
|
fileModifiedAt: new Date(),
|
||||||
|
fileCreatedAt: new Date(),
|
||||||
|
assetData: Buffer.from('some data'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await sut.uploadAsset(auth, uploadDto, mediumFactory.uploadFile({ checksum: asset.checksum }));
|
||||||
|
expect(response).toEqual({ id: expect.any(String), status: AssetMediaStatus.DUPLICATE });
|
||||||
|
|
||||||
|
const update = await sharedLinkRepo.get(user.id, sharedLink.id);
|
||||||
|
const assets = update!.assets;
|
||||||
|
expect(assets).toHaveLength(1);
|
||||||
|
expect(assets[0]).toMatchObject({ id: response.id });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add to an album shared link', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||||
|
|
||||||
|
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||||
|
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||||
|
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||||
|
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||||
|
|
||||||
|
const sharedLink = await sharedLinkRepo.create({
|
||||||
|
key: randomBytes(50),
|
||||||
|
type: SharedLinkType.Album,
|
||||||
|
albumId: album.id,
|
||||||
|
description: 'Shared link description',
|
||||||
|
userId: user.id,
|
||||||
|
allowDownload: true,
|
||||||
|
allowUpload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id }, sharedLink });
|
||||||
|
const uploadDto = {
|
||||||
|
deviceId: 'some-id',
|
||||||
|
deviceAssetId: 'some-id',
|
||||||
|
fileModifiedAt: new Date(),
|
||||||
|
fileCreatedAt: new Date(),
|
||||||
|
assetData: Buffer.from('some data'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await sut.uploadAsset(auth, uploadDto, mediumFactory.uploadFile());
|
||||||
|
expect(response).toEqual({ id: expect.any(String), status: AssetMediaStatus.CREATED });
|
||||||
|
|
||||||
|
const result = await ctx.get(AlbumRepository).getAssetIds(album.id, [response.id]);
|
||||||
|
const assets = [...result];
|
||||||
|
expect(assets).toHaveLength(1);
|
||||||
|
expect(assets[0]).toEqual(response.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle adding a duplicate asset to an album shared link', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
|
||||||
|
const sharedLinkRepo = ctx.get(SharedLinkRepository);
|
||||||
|
|
||||||
|
ctx.getMock(StorageRepository).utimes.mockResolvedValue();
|
||||||
|
ctx.getMock(EventRepository).emit.mockResolvedValue();
|
||||||
|
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||||
|
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
const { album } = await ctx.newAlbum({ ownerId: user.id }, [asset.id]);
|
||||||
|
// await ctx.newExif({ assetId: asset.id, fileSizeInByte: 12_345 });
|
||||||
|
|
||||||
|
const sharedLink = await sharedLinkRepo.create({
|
||||||
|
key: randomBytes(50),
|
||||||
|
type: SharedLinkType.Album,
|
||||||
|
albumId: album.id,
|
||||||
|
description: 'Shared link description',
|
||||||
|
userId: user.id,
|
||||||
|
allowDownload: true,
|
||||||
|
allowUpload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const auth = factory.auth({ user: { id: user.id }, sharedLink });
|
||||||
|
const uploadDto = {
|
||||||
|
deviceId: 'some-id',
|
||||||
|
deviceAssetId: 'some-id',
|
||||||
|
fileModifiedAt: new Date(),
|
||||||
|
fileCreatedAt: new Date(),
|
||||||
|
assetData: Buffer.from('some data'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await sut.uploadAsset(auth, uploadDto, mediumFactory.uploadFile({ checksum: asset.checksum }));
|
||||||
|
expect(response).toEqual({ id: expect.any(String), status: AssetMediaStatus.DUPLICATE });
|
||||||
|
|
||||||
|
const result = await ctx.get(AlbumRepository).getAssetIds(album.id, [response.id]);
|
||||||
|
const assets = [...result];
|
||||||
|
expect(assets).toHaveLength(1);
|
||||||
|
expect(assets[0]).toEqual(response.id);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('viewThumbnail', () => {
|
describe('viewThumbnail', () => {
|
||||||
|
|||||||
@@ -63,12 +63,22 @@ const authSharedLinkFactory = (sharedLink: Partial<AuthSharedLink> = {}) => {
|
|||||||
expiresAt = null,
|
expiresAt = null,
|
||||||
userId = newUuid(),
|
userId = newUuid(),
|
||||||
showExif = true,
|
showExif = true,
|
||||||
|
albumId = null,
|
||||||
allowUpload = false,
|
allowUpload = false,
|
||||||
allowDownload = true,
|
allowDownload = true,
|
||||||
password = null,
|
password = null,
|
||||||
} = sharedLink;
|
} = sharedLink;
|
||||||
|
|
||||||
return { id, expiresAt, userId, showExif, allowUpload, allowDownload, password };
|
return {
|
||||||
|
id,
|
||||||
|
albumId,
|
||||||
|
expiresAt,
|
||||||
|
userId,
|
||||||
|
showExif,
|
||||||
|
allowUpload,
|
||||||
|
allowDownload,
|
||||||
|
password,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const authApiKeyFactory = (apiKey: Partial<AuthApiKey> = {}) => ({
|
const authApiKeyFactory = (apiKey: Partial<AuthApiKey> = {}) => ({
|
||||||
|
|||||||
+1
-1
@@ -100,7 +100,7 @@
|
|||||||
"prettier-plugin-sort-json": "^4.1.1",
|
"prettier-plugin-sort-json": "^4.1.1",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"rollup-plugin-visualizer": "^6.0.0",
|
"rollup-plugin-visualizer": "^6.0.0",
|
||||||
"svelte": "5.53.7",
|
"svelte": "5.53.13",
|
||||||
"svelte-check": "^4.1.5",
|
"svelte-check": "^4.1.5",
|
||||||
"svelte-eslint-parser": "^1.3.3",
|
"svelte-eslint-parser": "^1.3.3",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
|
|||||||
@@ -212,12 +212,12 @@
|
|||||||
bottom: `${rootHeight - top}px`,
|
bottom: `${rootHeight - top}px`,
|
||||||
left: `${left}px`,
|
left: `${left}px`,
|
||||||
width: `${boundary.width}px`,
|
width: `${boundary.width}px`,
|
||||||
maxHeight: maxHeight(top - dropdownOffset),
|
maxHeight: maxHeight(boundary.top - dropdownOffset),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportHeight = visualViewport?.height || rootHeight;
|
const viewportHeight = visualViewport?.height || window.innerHeight;
|
||||||
const availableHeight = modalBounds ? rootHeight - bottom : viewportHeight - boundary.bottom;
|
const availableHeight = viewportHeight - boundary.bottom;
|
||||||
return {
|
return {
|
||||||
top: `${bottom}px`,
|
top: `${bottom}px`,
|
||||||
left: `${left}px`,
|
left: `${left}px`,
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ async function fileUploader({
|
|||||||
uploadAssetsStore.track('success');
|
uploadAssetsStore.track('success');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (albumId) {
|
if (albumId && !authManager.isSharedLink) {
|
||||||
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_adding_to_album') });
|
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_adding_to_album') });
|
||||||
await addAssetsToAlbums([albumId], [responseData.id], { notify: false });
|
await addAssetsToAlbums([albumId], [responseData.id], { notify: false });
|
||||||
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_added_to_album') });
|
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_added_to_album') });
|
||||||
|
|||||||
+5
-1
@@ -287,7 +287,11 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAlbumAddAssets = async () => {
|
const onAlbumAddAssets = async ({ albumIds }: { albumIds: string[] }) => {
|
||||||
|
if (!albumIds.includes(album.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await refreshAlbum();
|
await refreshAlbum();
|
||||||
timelineInteraction.clearMultiselect();
|
timelineInteraction.clearMultiselect();
|
||||||
await setModeToView();
|
await setModeToView();
|
||||||
|
|||||||
Reference in New Issue
Block a user