diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index 8750556c71..ce411c19e4 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -58,7 +58,8 @@ jobs: permissions: contents: read # Skip when PR from a fork - if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && fromJSON(needs.pre-job.outputs.should_run).mobile == true }} + # if: ${{ !github.event.pull_request.head.repo.fork && github.actor != 'dependabot[bot]' && fromJSON(needs.pre-job.outputs.should_run).mobile == true }} + if: ${{ false }} runs-on: mich steps: @@ -154,3 +155,111 @@ jobs: mobile/android/.gradle mobile/.dart_tool key: ${{ steps.cache-gradle-restore.outputs.cache-primary-key }} + + build-sign-ios: + name: Build and sign iOS + needs: pre-job + permissions: + contents: read + # Run on main branch or workflow_dispatch + if: ${{ !github.event.pull_request.head.repo.fork && fromJSON(needs.pre-job.outputs.should_run).mobile == true && github.ref == 'refs/heads/main' }} + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + ref: ${{ inputs.ref || github.sha }} + persist-credentials: false + + - name: Setup Flutter SDK + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + flutter-version-file: ./mobile/pubspec.yaml + cache: true + + - name: Install Flutter dependencies + working-directory: ./mobile + run: flutter pub get + + - name: Generate translation files + run: dart run easy_localization:generate -S ../i18n && dart run bin/generate_keys.dart + working-directory: ./mobile + + - name: Generate platform APIs + run: make pigeon + working-directory: ./mobile + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + working-directory: ./mobile/ios + + - name: Install Fastlane + run: | + cd mobile/ios + gem install bundler + bundle config set --local path 'vendor/bundle' + bundle install + + - name: Create API Key JSON + env: + API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} + API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY }} + working-directory: ./mobile/ios + run: | + mkdir -p ~/.appstoreconnect/private_keys + echo "$API_KEY_CONTENT" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8 + cat > api_key.json << EOF + { + "key_id": "${API_KEY_ID}", + "issuer_id": "${API_KEY_ISSUER_ID}", + "key": "$(cat ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8)", + "duration": 1200, + "in_house": false + } + EOF + + - name: Import Certificate and Provisioning Profile + env: + IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }} + IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }} + working-directory: ./mobile/ios + run: | + echo "$IOS_CERTIFICATE_P12" | base64 --decode > certificate.p12 + echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision + + - name: Create keychain + env: + KEYCHAIN_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + run: | + security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security set-keychain-settings -t 3600 -u build.keychain + + - name: Build and deploy to TestFlight + env: + FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }} + IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + KEYCHAIN_NAME: build.keychain + KEYCHAIN_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + working-directory: ./mobile/ios + run: bundle exec fastlane release_ci + + - name: Clean up keychain + if: always() + run: | + security delete-keychain build.keychain || true + + - name: Upload IPA artifact + uses: actions/upload-artifact@v4 + with: + name: ios-release-ipa + path: mobile/ios/Runner.ipa diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index a17225513a..f440276397 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -44,7 +44,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './cli/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/close-duplicates.yml b/.github/workflows/close-duplicates.yml index d135d9b1a0..ba360b50dc 100644 --- a/.github/workflows/close-duplicates.yml +++ b/.github/workflows/close-duplicates.yml @@ -54,16 +54,10 @@ jobs: issues: write discussions: write steps: - - id: token - uses: immich-app/devtools/actions/create-workflow-token@da177fa133657503ddb7503f8ba53dccefec5da1 # create-workflow-token-action-v1.0.0 - with: - app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }} - private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }} - - name: Close issue if: ${{ github.event_name == 'issues' }} env: - GH_TOKEN: ${{ steps.token.outputs.token }} + GH_TOKEN: ${{ github.token }} NODE_ID: ${{ github.event.issue.node_id }} run: | gh api graphql \ @@ -89,7 +83,7 @@ jobs: - name: Close discussion if: ${{ github.event_name == 'discussion' && github.event.discussion.category.name == 'Feature Request' }} env: - GH_TOKEN: ${{ steps.token.outputs.token }} + GH_TOKEN: ${{ github.token }} NODE_ID: ${{ github.event.discussion.node_id }} run: | gh api graphql \ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 125ae28123..3f32478c0c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -57,7 +57,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 + uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -70,7 +70,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 + uses: github/codeql-action/autobuild@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -83,6 +83,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@755f44910c12a3d7ca0d8c6e42c048b3362f7cec # v3.30.8 + uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 09a498607c..0c4dc9765c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -116,24 +116,23 @@ jobs: matrix: include: - device: cpu - tag-suffix: '' - device: cuda - tag-suffix: '-cuda' + suffixes: '-cuda' platforms: linux/amd64 - device: openvino - tag-suffix: '-openvino' + suffixes: '-openvino' platforms: linux/amd64 - device: armnn - tag-suffix: '-armnn' + suffixes: '-armnn' platforms: linux/arm64 - device: rknn - tag-suffix: '-rknn' + suffixes: '-rknn' platforms: linux/arm64 - device: rocm - tag-suffix: '-rocm' + suffixes: '-rocm' platforms: linux/amd64 runner-mapping: '{"linux/amd64": "mich"}' - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@946acac326940f8badf09ccf591d9cb345d6a689 # multi-runner-build-workflow-v0.2.1 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@a667ef0a5cf3ff1ff1e41be52d3fe326b24e3a00 # multi-runner-build-workflow-v1.1.3 permissions: contents: read actions: read @@ -147,7 +146,7 @@ jobs: dockerfile: machine-learning/Dockerfile platforms: ${{ matrix.platforms }} runner-mapping: ${{ matrix.runner-mapping }} - tag-suffix: ${{ matrix.tag-suffix }} + suffixes: ${{ matrix.suffixes }} dockerhub-push: ${{ github.event_name == 'release' }} build-args: | DEVICE=${{ matrix.device }} @@ -156,7 +155,7 @@ jobs: name: Build and Push Server needs: pre-job if: ${{ fromJSON(needs.pre-job.outputs.should_run).server == true }} - uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@946acac326940f8badf09ccf591d9cb345d6a689 # multi-runner-build-workflow-v0.2.1 + uses: immich-app/devtools/.github/workflows/multi-runner-build.yml@a667ef0a5cf3ff1ff1e41be52d3fe326b24e3a00 # multi-runner-build-workflow-v1.1.3 permissions: contents: read actions: read diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 24542c8585..2a28b57569 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -69,7 +69,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './docs/.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 4990e063bc..a74a2ec613 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -5,6 +5,9 @@ on: types: - completed +env: + TG_NON_INTERACTIVE: 'true' + jobs: checks: name: Docs Deploy Checks @@ -182,15 +185,11 @@ jobs: CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} working-directory: 'deployment/modules/cloudflare/docs' - run: 'mise run tf output -json' - - - name: Output Cleaning - id: clean - env: - TG_OUTPUT: ${{ steps.docs-output.outputs.tg_action_output }} run: | - CLEANED=$(echo "$TG_OUTPUT" | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .) - echo "output=$CLEANED" >> $GITHUB_OUTPUT + mise run tf output -- -json | jq -r ' + "projectName=\(.pages_project_name.value)", + "subdomain=\(.immich_app_branch_subdomain.value)" + ' >> $GITHUB_OUTPUT - name: Publish to Cloudflare Pages # TODO: Action is deprecated @@ -198,7 +197,7 @@ jobs: with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - projectName: ${{ fromJson(steps.clean.outputs.output).pages_project_name.value }} + projectName: ${{ steps.docs-output.outputs.projectName }} workingDirectory: 'docs' directory: 'build' branch: ${{ steps.parameters.outputs.name }} @@ -221,6 +220,6 @@ jobs: token: ${{ steps.token.outputs.token }} number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }} body: | - 📖 Documentation deployed to [${{ fromJson(steps.clean.outputs.output).immich_app_branch_subdomain.value }}](https://${{ fromJson(steps.clean.outputs.output).immich_app_branch_subdomain.value }}) + 📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }}) emojis: 'rocket' body-include: '' diff --git a/.github/workflows/docs-destroy.yml b/.github/workflows/docs-destroy.yml index b9bc359393..7de2d81858 100644 --- a/.github/workflows/docs-destroy.yml +++ b/.github/workflows/docs-destroy.yml @@ -5,6 +5,9 @@ on: permissions: {} +env: + TG_NON_INTERACTIVE: 'true' + jobs: deploy: name: Docs Destroy @@ -36,7 +39,7 @@ jobs: CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }} working-directory: 'deployment/modules/cloudflare/docs' - run: 'mise run tf destroy -refresh=false' + run: 'mise run tf destroy -- -refresh=false' - name: Comment uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0 diff --git a/.github/workflows/fix-format.yml b/.github/workflows/fix-format.yml index 68267011ce..90810c2cfc 100644 --- a/.github/workflows/fix-format.yml +++ b/.github/workflows/fix-format.yml @@ -32,7 +32,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 97656eaf0c..4aa78ee13a 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -62,13 +62,13 @@ jobs: ref: main - name: Install uv - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 + uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1 - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index 96e5495cdd..12bdbc55bf 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -31,7 +31,7 @@ jobs: uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 # Setup .npmrc file to publish to npm - - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './open-api/typescript-sdk/.nvmrc' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88db0257ed..8c7eae6532 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,7 +77,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -121,7 +121,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './cli/.nvmrc' cache: 'pnpm' @@ -168,7 +168,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './cli/.nvmrc' cache: 'pnpm' @@ -210,7 +210,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -254,7 +254,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -292,7 +292,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './web/.nvmrc' cache: 'pnpm' @@ -340,7 +340,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -386,7 +386,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -425,7 +425,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -480,7 +480,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './e2e/.nvmrc' cache: 'pnpm' @@ -562,7 +562,7 @@ jobs: persist-credentials: false token: ${{ steps.token.outputs.token }} - name: Install uv - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 + uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v7.1.1 - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 # TODO: add caching when supported (https://github.com/actions/setup-python/pull/818) # with: @@ -608,7 +608,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './.github/.nvmrc' cache: 'pnpm' @@ -659,7 +659,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' @@ -721,7 +721,7 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version-file: './server/.nvmrc' cache: 'pnpm' diff --git a/cli/package.json b/cli/package.json index f0b5bd5cd1..c3db88a7ac 100644 --- a/cli/package.json +++ b/cli/package.json @@ -20,7 +20,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.18.8", + "@types/node": "^22.18.10", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", diff --git a/cli/src/index.ts b/cli/src/index.ts index a0392186c0..cd49d0e830 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -8,6 +8,7 @@ import { serverInfo } from 'src/commands/server-info'; import { version } from '../package.json'; const defaultConfigDirectory = path.join(os.homedir(), '.config/immich/'); +const defaultConcurrency = Math.max(1, os.cpus().length - 1); const program = new Command() .name('immich') @@ -66,7 +67,7 @@ program .addOption( new Option('-c, --concurrency ', 'Number of assets to upload at the same time') .env('IMMICH_UPLOAD_CONCURRENCY') - .default(4), + .default(defaultConcurrency), ) .addOption( new Option('-j, --json-output', 'Output detailed information in json format') diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 78ba0653ac..22bb0f3444 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -83,7 +83,7 @@ services: container_name: immich_prometheus ports: - 9090:9090 - image: prom/prometheus@sha256:63805ebb8d2b3920190daf1cb14a60871b16fd38bed42b857a3182bc621f4996 + image: prom/prometheus@sha256:23031bfe0e74a13004252caaa74eccd0d62b6c6e7a04711d5b8bf5b7e113adc7 volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus-data:/prometheus diff --git a/docker/example.env b/docker/example.env index 6d6fd1e3fe..6641cceaaa 100644 --- a/docker/example.env +++ b/docker/example.env @@ -9,8 +9,8 @@ DB_DATA_LOCATION=./postgres # To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List # TZ=Etc/UTC -# The Immich version to use. You can pin this to a specific version like "v1.71.0" -IMMICH_VERSION=release +# The Immich version to use. You can pin this to a specific version like "v2.1.0" +IMMICH_VERSION=v2 # Connection secret for postgres. You should change it to a random password # Please use only the characters `A-Za-z0-9`, without special characters or spaces diff --git a/e2e/package.json b/e2e/package.json index 67aeb00c1b..56d982682d 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -25,7 +25,7 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.18.8", + "@types/node": "^22.18.10", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", @@ -34,7 +34,7 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^60.0.0", - "exiftool-vendored": "^28.3.1", + "exiftool-vendored": "^31.1.0", "globals": "^16.0.0", "jose": "^5.6.3", "luxon": "^3.4.4", diff --git a/e2e/src/api/specs/tag.e2e-spec.ts b/e2e/src/api/specs/tag.e2e-spec.ts index 7b645f8bd4..d69536f3a3 100644 --- a/e2e/src/api/specs/tag.e2e-spec.ts +++ b/e2e/src/api/specs/tag.e2e-spec.ts @@ -582,7 +582,7 @@ describe('/tags', () => { expect(body).toEqual([expect.objectContaining({ id: userAsset.id, success: true })]); }); - it('should remove duplicate assets only once', async () => { + it.skip('should remove duplicate assets only once', async () => { const tagA = await create(user.accessToken, { name: 'TagA' }); await tagAssets( { id: tagA.id, bulkIdsDto: { ids: [userAsset.id] } }, diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts index b0696dcada..2d6e08b5fb 100644 --- a/e2e/src/api/specs/user-admin.e2e-spec.ts +++ b/e2e/src/api/specs/user-admin.e2e-spec.ts @@ -1,4 +1,5 @@ import { + JobName, LoginResponseDto, createStack, deleteUserAdmin, @@ -327,6 +328,8 @@ describe('/admin/users', () => { { headers: asBearerAuth(user.accessToken) }, ); + await utils.waitForQueueFinish(admin.accessToken, JobName.BackgroundTask); + const { status, body } = await request(app) .delete(`/admin/users/${user.userId}`) .send({ force: true }) diff --git a/e2e/src/responses.ts b/e2e/src/responses.ts index b14aedf895..27e6091206 100644 --- a/e2e/src/responses.ts +++ b/e2e/src/responses.ts @@ -119,5 +119,6 @@ export const deviceDto = { isPendingSyncReset: false, deviceOS: '', deviceType: '', + appVersion: null, }, }; diff --git a/i18n/en.json b/i18n/en.json index 8669a048b8..02e573de69 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -474,6 +474,7 @@ "app_bar_signout_dialog_title": "Sign out", "app_download_links": "App Download Links", "app_settings": "App Settings", + "app_stores": "App Stores", "app_update_available": "App update is available", "appears_in": "Appears in", "apply_count": "Apply ({count, number})", @@ -745,6 +746,7 @@ "create": "Create", "create_album": "Create album", "create_album_page_untitled": "Untitled", + "create_api_key": "Create API key", "create_library": "Create Library", "create_link": "Create link", "create_link_to_share": "Create link to share", @@ -1351,7 +1353,7 @@ "minutes": "Minutes", "missing": "Missing", "mobile_app": "Mobile App", - "mobile_app_download_onboarding_note": "You can access these options again from the Utilities page.", + "mobile_app_download_onboarding_note": "Download the companion mobile app using the following options", "model": "Model", "month": "Month", "monthly_title_text_date_format": "MMMM y", @@ -1433,7 +1435,7 @@ "notifications_setting_description": "Manage notifications", "oauth": "OAuth", "obtainium_configurator": "Obtainium Configurator", - "obtainium_configurator_instructions": "Please create an API key and select a variant to create your Obtainium configuration link.", + "obtainium_configurator_instructions": "Use Obtainium to install and update the Android app directly from Immich GitHub's release. Create an API key and select a variant to create your Obtainium configuration link", "official_immich_resources": "Official Immich Resources", "offline": "Offline", "offset": "Offset", diff --git a/mise.toml b/mise.toml index cbef17a53a..d3af8bfeb9 100644 --- a/mise.toml +++ b/mise.toml @@ -1,9 +1,9 @@ [tools] node = "22.20.0" -flutter = "3.35.5" +flutter = "3.35.6" pnpm = "10.18.1" -terragrunt = "0.58.12" -opentofu = "1.7.1" +terragrunt = "0.91.2" +opentofu = "1.10.6" [tools."github:CQLabs/homebrew-dcm"] version = "1.30.0" diff --git a/mobile/.fvmrc b/mobile/.fvmrc index a4d5f6d9b7..d70a803f6e 100644 --- a/mobile/.fvmrc +++ b/mobile/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.35.4" + "flutter": "3.35.6" } \ No newline at end of file diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json index 9c6057e582..f1d5ac8fd5 100644 --- a/mobile/.vscode/settings.json +++ b/mobile/.vscode/settings.json @@ -1,5 +1,5 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.35.4", + "dart.flutterSdkPath": ".fvm/versions/3.35.6", "dart.lineLength": 120, "[dart]": { "editor.rulers": [120] diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt index 034f5ee72e..4383b3098d 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/MainActivity.kt @@ -9,6 +9,7 @@ import app.alextran.immich.background.BackgroundWorkerFgHostApi import app.alextran.immich.background.BackgroundWorkerLockApi import app.alextran.immich.connectivity.ConnectivityApi import app.alextran.immich.connectivity.ConnectivityApiImpl +import app.alextran.immich.core.ImmichPlugin import app.alextran.immich.images.ThumbnailApi import app.alextran.immich.images.ThumbnailsImpl import app.alextran.immich.sync.NativeSyncApi @@ -42,6 +43,14 @@ class MainActivity : FlutterFragmentActivity() { flutterEngine.plugins.add(BackgroundServicePlugin()) flutterEngine.plugins.add(HttpSSLOptionsPlugin()) flutterEngine.plugins.add(backgroundEngineLockImpl) + flutterEngine.plugins.add(nativeSyncApiImpl) + } + + fun cancelPlugins(flutterEngine: FlutterEngine) { + val nativeApi = + flutterEngine.plugins.get(NativeSyncApiImpl26::class.java) as ImmichPlugin? + ?: flutterEngine.plugins.get(NativeSyncApiImpl30::class.java) as ImmichPlugin? + nativeApi?.detachFromEngine() } } } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt index 504267a4e5..b11b53bcde 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundEngineLock.kt @@ -2,12 +2,13 @@ package app.alextran.immich.background import android.content.Context import android.util.Log +import app.alextran.immich.core.ImmichPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin import java.util.concurrent.atomic.AtomicInteger private const val TAG = "BackgroundEngineLock" -class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterPlugin { +class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, ImmichPlugin() { private val ctx: Context = context.applicationContext companion object { @@ -41,12 +42,14 @@ class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, FlutterP } override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + super.onAttachedToEngine(binding) checkAndEnforceBackgroundLock(binding.applicationContext) engineCount.incrementAndGet() Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + super.onDetachedFromEngine(binding) engineCount.decrementAndGet() Log.i(TAG, "Flutter engine detached. Attached Engines count: $engineCount") } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt index 5857453ad3..b6b387db03 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt index e59cee2c16..7dce1f6edf 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorker.kt @@ -190,6 +190,9 @@ class BackgroundWorker(context: Context, params: WorkerParameters) : private fun complete(success: Result) { Log.d(TAG, "About to complete BackupWorker with result: $success") isComplete = true + if (engine != null) { + MainActivity.cancelPlugins(engine!!) + } engine?.destroy() engine = null flutterApi = null diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt index 3d00bafba2..d7353f0462 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/background/BackgroundWorkerLock.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt index 434ba47ca1..629071382a 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/connectivity/Connectivity.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt new file mode 100644 index 0000000000..4cc131b058 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/core/ImmichPlugin.kt @@ -0,0 +1,29 @@ +package app.alextran.immich.core + +import androidx.annotation.CallSuper +import io.flutter.embedding.engine.plugins.FlutterPlugin + +abstract class ImmichPlugin : FlutterPlugin { + private var detached: Boolean = false; + + @CallSuper + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + detached = false; + } + + fun detachFromEngine() { + detached = true + } + + @CallSuper + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + detachFromEngine() + } + + fun completeWhenActive(callback: (T) -> Unit, value: T) { + if (detached) { + return; + } + callback(value); + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt index e9993a3c45..ae2cca4d7b 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/images/Thumbnails.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index 28400c803f..08ff0e821a 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 868f3c6cdd..0ea86bb10d 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -7,6 +7,7 @@ import android.database.Cursor import android.provider.MediaStore import android.util.Base64 import androidx.core.database.getStringOrNull +import app.alextran.immich.core.ImmichPlugin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -27,7 +28,7 @@ sealed class AssetResult { } @SuppressLint("InlinedApi") -open class NativeSyncApiImplBase(context: Context) { +open class NativeSyncApiImplBase(context: Context) : ImmichPlugin() { private val ctx: Context = context.applicationContext private var hashTask: Job? = null @@ -237,7 +238,7 @@ open class NativeSyncApiImplBase(context: Context) { callback: (Result>) -> Unit ) { if (assetIds.isEmpty()) { - callback(Result.success(emptyList())) + completeWhenActive(callback, Result.success(emptyList())) return } @@ -253,10 +254,10 @@ open class NativeSyncApiImplBase(context: Context) { } }.awaitAll() - callback(Result.success(results)) + completeWhenActive(callback, Result.success(results)) } catch (e: CancellationException) { - callback( - Result.failure( + completeWhenActive( + callback, Result.failure( FlutterError( HASHING_CANCELLED_CODE, "Hashing operation was cancelled", @@ -265,7 +266,7 @@ open class NativeSyncApiImplBase(context: Context) { ) ) } catch (e: Exception) { - callback(Result.failure(e)) + completeWhenActive(callback, Result.failure(e)) } } } diff --git a/mobile/ios/.gitignore b/mobile/ios/.gitignore index e32cadbf68..f1a46a2fef 100644 --- a/mobile/ios/.gitignore +++ b/mobile/ios/.gitignore @@ -33,3 +33,4 @@ Runner/GeneratedPluginRegistrant.* !default.perspectivev3 fastlane/report.xml +Gemfile.lock \ No newline at end of file diff --git a/mobile/ios/Gemfile b/mobile/ios/Gemfile index 7a118b49be..bb94aef518 100644 --- a/mobile/ios/Gemfile +++ b/mobile/ios/Gemfile @@ -1,3 +1,4 @@ source "https://rubygems.org" gem "fastlane" +gem "cocoapods" \ No newline at end of file diff --git a/mobile/ios/Gemfile.lock b/mobile/ios/Gemfile.lock deleted file mode 100644 index 218b8c1355..0000000000 --- a/mobile/ios/Gemfile.lock +++ /dev/null @@ -1,218 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) - artifactory (3.0.17) - atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.932.0) - aws-sdk-core (3.196.1) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) - jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.81.0) - aws-sdk-core (~> 3, >= 3.193.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.151.0) - aws-sdk-core (~> 3, >= 3.194.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - base64 (0.2.0) - claide (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - declarative (0.0.20) - digest-crc (0.6.5) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20240107) - dotenv (2.8.1) - emoji_regex (3.2.3) - excon (0.110.0) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - fastimage (2.3.1) - fastlane (2.214.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.8, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander (~> 4.6) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (>= 2.0.0, < 3.0.0) - naturally (~> 2.2) - optparse (~> 0.1.1) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.54.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.3) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-playcustomapp_v1 (0.13.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.31.0) - google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) - google-cloud-env (>= 1.0, < 3.a) - google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.4.0) - google-cloud-storage (1.47.0) - addressable (~> 2.8) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.31.0) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - highline (2.0.3) - http-cookie (1.0.5) - domain_name (~> 0.5) - httpclient (2.8.3) - jmespath (1.6.2) - json (2.7.2) - jwt (2.8.1) - base64 - mini_magick (4.12.0) - mini_mime (1.1.5) - multi_json (1.15.0) - multipart-post (2.4.1) - nanaimo (0.3.0) - naturally (2.2.1) - nkf (0.2.0) - optparse (0.1.1) - os (1.1.4) - plist (3.7.1) - public_suffix (4.0.7) - rake (13.2.1) - representable (3.2.0) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.3.6) - strscan - rouge (2.0.7) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - security (0.1.3) - signet (0.19.0) - addressable (~> 2.8) - faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.10) - CFPropertyList - naturally - strscan (3.1.0) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.2) - tty-cursor (0.7.1) - tty-screen (0.8.2) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unicode-display_width (1.8.0) - word_wrap (1.0.0) - xcodeproj (1.25.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - x86_64-darwin-21 - x86_64-linux - -DEPENDENCIES - fastlane - -BUNDLED WITH - 2.3.7 diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 9bff8cd8e2..d869aa9c08 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -84,7 +84,7 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - - photo_manager (2.0.0): + - photo_manager (3.7.1): - Flutter - FlutterMacOS - SAMKeychain (1.5.3) @@ -262,7 +262,7 @@ SPEC CHECKSUMS: fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 geolocator_apple: 1560c3c875af2a412242c7a923e15d0d401966ff home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f - image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e isar_community_flutter_libs: bede843185a61a05ff364a05c9b23209523f7e0d local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 @@ -271,9 +271,9 @@ SPEC CHECKSUMS: native_video_player: b65c58951ede2f93d103a25366bdebca95081265 network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d - photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413 + photo_manager: 1d80ae07a89a67dfbcae95953a1e5a24af7c3e62 SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb @@ -285,7 +285,7 @@ SPEC CHECKSUMS: sqlite3_flutter_libs: f8fc13346870e73fe35ebf6dbb997fbcd156b241 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d - wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49 + wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 PODFILE CHECKSUM: 7ce312f2beab01395db96f6969d90a447279cf45 diff --git a/mobile/ios/Runner/Background/BackgroundWorker.g.swift b/mobile/ios/Runner/Background/BackgroundWorker.g.swift index e339f150e7..8c9391e8d2 100644 --- a/mobile/ios/Runner/Background/BackgroundWorker.g.swift +++ b/mobile/ios/Runner/Background/BackgroundWorker.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation diff --git a/mobile/ios/Runner/Connectivity/Connectivity.g.swift b/mobile/ios/Runner/Connectivity/Connectivity.g.swift index 45333f03d8..f8d85a2edf 100644 --- a/mobile/ios/Runner/Connectivity/Connectivity.g.swift +++ b/mobile/ios/Runner/Connectivity/Connectivity.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation diff --git a/mobile/ios/Runner/Images/Thumbnails.g.swift b/mobile/ios/Runner/Images/Thumbnails.g.swift index be40a18b41..fbaef294d3 100644 --- a/mobile/ios/Runner/Images/Thumbnails.g.swift +++ b/mobile/ios/Runner/Images/Thumbnails.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index 305aca5266..6bcafb9215 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 57c853e751..3bf0d8345f 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -16,6 +16,65 @@ default_platform(:ios) platform :ios do + desc "iOS Release to TestFlight" + lane :release_ci do + # Setup CI environment + setup_ci + + # Load App Store Connect API Key + api_key = app_store_connect_api_key( + key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"], + issuer_id: ENV["APP_STORE_CONNECT_API_KEY_ISSUER_ID"], + key_filepath: "api_key.json" + ) + + # Import certificate and provisioning profile + import_certificate( + certificate_path: "certificate.p12", + certificate_password: ENV["IOS_CERTIFICATE_PASSWORD"], + keychain_name: ENV["KEYCHAIN_NAME"], + keychain_password: ENV["KEYCHAIN_PASSWORD"] + ) + + # Install provisioning profile + install_provisioning_profile(path: "profile.mobileprovision") + + # Configure code signing + update_code_signing_settings( + use_automatic_signing: false, + path: "./Runner.xcodeproj", + team_id: ENV["FASTLANE_TEAM_ID"], + profile_name: "app.alextran.immich AppStore" + ) + + # Increment build number + increment_build_number( + build_number: latest_testflight_build_number( + api_key: api_key, + app_identifier: "app.alextran.immich" + ) + 1, + xcodeproj: "./Runner.xcodeproj" + ) + + # Build the app + build_app( + scheme: "Runner", + workspace: "Runner.xcworkspace", + export_method: "app-store", + export_options: { + provisioningProfiles: { + "app.alextran.immich" => "app.alextran.immich AppStore" + } + } + ) + + # Upload to TestFlight + upload_to_testflight( + api_key: api_key, + skip_waiting_for_build_processing: true + ) + end + desc "iOS Release" lane :release do enable_automatic_code_signing( diff --git a/mobile/lib/domain/services/background_worker.service.dart b/mobile/lib/domain/services/background_worker.service.dart index 78ba5b7088..e6ac3eaebd 100644 --- a/mobile/lib/domain/services/background_worker.service.dart +++ b/mobile/lib/domain/services/background_worker.service.dart @@ -30,9 +30,9 @@ import 'package:immich_mobile/services/upload.service.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:immich_mobile/wm_executor.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; -import 'package:worker_manager/worker_manager.dart'; class BackgroundWorkerFgService { final BackgroundWorkerFgHostApi _foregroundHostApi; @@ -94,7 +94,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { await Future.wait( [ loadTranslations(), - workerManager.init(dynamicSpawning: true), + workerManagerPatch.init(dynamicSpawning: true), _ref?.read(authServiceProvider).setOpenApiServiceEndpoint(), // Initialize the file downloader FileDownloader().configure( @@ -193,7 +193,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi { _logger.info("Cleaning up background worker"); final cleanupFutures = [ nativeSyncApi?.cancelHashing(), - workerManager.dispose().catchError((_) async { + workerManagerPatch.dispose().catchError((_) async { // Discard any errors on the dispose call return; }), diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 263a5ef769..b1d87b36ab 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:auto_route/auto_route.dart'; import 'package:background_downloader/background_downloader.dart'; @@ -40,10 +41,10 @@ import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/licenses.dart'; import 'package:immich_mobile/utils/migration.dart'; +import 'package:immich_mobile/wm_executor.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:logging/logging.dart'; import 'package:timezone/data/latest.dart'; -import 'package:worker_manager/worker_manager.dart'; void main() async { ImmichWidgetsBinding(); @@ -52,7 +53,7 @@ void main() async { await Bootstrap.initDomain(isar, drift, logDb); await initApp(); // Warm-up isolate pool for worker manager - await workerManager.init(dynamicSpawning: true); + await workerManagerPatch.init(dynamicSpawning: true, isolatesCount: max(Platform.numberOfProcessors - 1, 5)); await migrateDatabaseIfNeeded(isar, drift); HttpSSLOptions.apply(); diff --git a/mobile/lib/platform/background_worker_api.g.dart b/mobile/lib/platform/background_worker_api.g.dart index 22325603c0..e8c87aa1a4 100644 --- a/mobile/lib/platform/background_worker_api.g.dart +++ b/mobile/lib/platform/background_worker_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/lib/platform/background_worker_lock_api.g.dart b/mobile/lib/platform/background_worker_lock_api.g.dart index 9f00017dc8..93852d2564 100644 --- a/mobile/lib/platform/background_worker_lock_api.g.dart +++ b/mobile/lib/platform/background_worker_lock_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/lib/platform/connectivity_api.g.dart b/mobile/lib/platform/connectivity_api.g.dart index c348356f81..0422d87438 100644 --- a/mobile/lib/platform/connectivity_api.g.dart +++ b/mobile/lib/platform/connectivity_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 01237f8c19..8e4b900292 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/lib/platform/thumbnail_api.g.dart b/mobile/lib/platform/thumbnail_api.g.dart index 2b4add7482..53d7b10fc3 100644 --- a/mobile/lib/platform/thumbnail_api.g.dart +++ b/mobile/lib/platform/thumbnail_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v26.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 44660e440c..3111512823 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -43,7 +43,7 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), - if (asset.type == AssetType.image && isOwner) const EditImageActionButton(), + if (asset.type == AssetType.image) const EditImageActionButton(), if (isOwner) ...[ if (asset.hasRemote && isOwner && isArchived) const UnArchiveActionButton(source: ActionSource.viewer) diff --git a/mobile/lib/presentation/widgets/bottom_sheet/activities_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/activities_bottom_sheet.widget.dart deleted file mode 100644 index e8f29a976e..0000000000 --- a/mobile/lib/presentation/widgets/bottom_sheet/activities_bottom_sheet.widget.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/presentation/widgets/album/drift_activity_text_field.dart'; -import 'package:immich_mobile/providers/activity.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; -import 'package:immich_mobile/widgets/activities/activity_tile.dart'; -import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; -import 'base_bottom_sheet.widget.dart'; - -class ActivitiesBottomSheet extends HookConsumerWidget { - final DraggableScrollableController controller; - final double initialChildSize; - final bool scrollToBottomInitially; - - const ActivitiesBottomSheet({ - required this.controller, - this.initialChildSize = 0.35, - this.scrollToBottomInitially = true, - super.key, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final album = ref.watch(currentRemoteAlbumProvider)!; - final asset = ref.watch(currentAssetNotifier) as RemoteAsset?; - final user = ref.watch(currentUserProvider); - - final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier); - final activities = ref.watch(albumActivityProvider(album.id, asset?.id)); - - Future onAddComment(String comment) async { - await activityNotifier.addComment(comment); - } - - Widget buildActivitiesSliver() { - return activities.widgetWhen( - onLoading: () => const SliverToBoxAdapter(child: SizedBox.shrink()), - onData: (data) { - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - if (index == data.length) { - // return const SizedBox(height: 5); - return const SizedBox.shrink(); - } - final activity = data[index]; - final canDelete = activity.user.id == user?.id || album.ownerId == user?.id; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 1), - child: DismissibleActivity( - activity.id, - ActivityTile(activity), - onDismiss: canDelete - ? (activityId) async => await activityNotifier.removeActivity(activity.id) - : null, - ), - ); - }, childCount: data.length + 1), - ); - }, - ); - } - - return BaseBottomSheet( - actions: [], - slivers: [buildActivitiesSliver()], - footer: Column( - children: [ - const Divider(indent: 16, endIndent: 16), - DriftActivityTextField( - isEnabled: album.isActivityEnabled, - isBottomSheet: true, - // likeId: likedId, - onSubmit: onAddComment, - ), - ], - ), - controller: controller, - initialChildSize: initialChildSize, - minChildSize: 0.1, - maxChildSize: 0.88, - expand: false, - shouldCloseOnMinExtent: false, - resizeOnScroll: false, - backgroundColor: context.isDarkTheme ? Colors.black : Colors.white, - ); - } -} diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index 5359391261..c7628cb472 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -42,31 +42,23 @@ class ThumbnailTile extends ConsumerWidget { multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); - final borderStyle = lockSelection - ? BoxDecoration( - color: context.colorScheme.surfaceContainerHighest, - border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6), - ) - : isSelected - ? BoxDecoration( - color: assetContainerColor, - border: Border.all(color: assetContainerColor, width: 6), - ) - : const BoxDecoration(); - final bool storageIndicator = ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && showStorageIndicator; return Stack( children: [ + Container(color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor), AnimatedContainer( duration: Durations.short4, curve: Curves.decelerate, - decoration: borderStyle, - child: ClipRRect( - borderRadius: isSelected || lockSelection - ? const BorderRadius.all(Radius.circular(15.0)) - : BorderRadius.zero, + padding: EdgeInsets.all(isSelected || lockSelection ? 6 : 0), + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: (isSelected || lockSelection) ? 15.0 : 0.0), + duration: Durations.short4, + curve: Curves.decelerate, + builder: (context, value, child) { + return ClipRRect(borderRadius: BorderRadius.all(Radius.circular(value)), child: child); + }, child: Stack( children: [ Positioned.fill( @@ -116,29 +108,36 @@ class ThumbnailTile extends ConsumerWidget { ), ), ), - if (isSelected || lockSelection) - Padding( - padding: const EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: _SelectionIndicator( - isSelected: isSelected, - isLocked: lockSelection, - color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor, + TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: (isSelected || lockSelection) ? 1.0 : 0.0), + duration: Durations.short4, + curve: Curves.decelerate, + builder: (context, value, child) { + return Padding( + padding: EdgeInsets.all((isSelected || lockSelection) ? value * 3.0 : 3.0), + child: Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: (isSelected || lockSelection) ? 1 : value, + child: _SelectionIndicator( + isLocked: lockSelection, + color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor, + ), + ), ), - ), - ), + ); + }, + ), ], ); } } class _SelectionIndicator extends StatelessWidget { - final bool isSelected; final bool isLocked; final Color? color; - const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); + const _SelectionIndicator({required this.isLocked, this.color}); @override Widget build(BuildContext context) { @@ -147,13 +146,11 @@ class _SelectionIndicator extends StatelessWidget { decoration: BoxDecoration(shape: BoxShape.circle, color: color), child: const Icon(Icons.check_circle_rounded, color: Colors.grey), ); - } else if (isSelected) { + } else { return DecoratedBox( decoration: BoxDecoration(shape: BoxShape.circle, color: color), child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); - } else { - return const Icon(Icons.circle_outlined, color: Colors.white); } } } diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 1ccf00d58b..491e1bf107 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:immich_mobile/wm_executor.dart'; import 'package:logging/logging.dart'; import 'package:worker_manager/worker_manager.dart'; @@ -31,7 +32,7 @@ Cancelable runInIsolateGentle({ throw const InvalidIsolateUsageException(); } - return workerManager.executeGentle((cancelledChecker) async { + return workerManagerPatch.executeGentle((cancelledChecker) async { T? result; await runZonedGuarded( () async { diff --git a/mobile/lib/utils/semver.dart b/mobile/lib/utils/semver.dart index 5df63618e4..0eb6726b65 100644 --- a/mobile/lib/utils/semver.dart +++ b/mobile/lib/utils/semver.dart @@ -15,7 +15,7 @@ class SemVer { } factory SemVer.fromString(String version) { - final parts = version.split('.'); + final parts = version.split("-")[0].split('.'); return SemVer(major: int.parse(parts[0]), minor: int.parse(parts[1]), patch: int.parse(parts[2])); } diff --git a/mobile/lib/wm_executor.dart b/mobile/lib/wm_executor.dart new file mode 100644 index 0000000000..73e882e8e6 --- /dev/null +++ b/mobile/lib/wm_executor.dart @@ -0,0 +1,251 @@ +// part of 'package:worker_manager/worker_manager.dart'; +// ignore_for_file: implementation_imports, avoid_print + +import 'dart:async'; +import 'dart:math'; + +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:worker_manager/src/number_of_processors/processors_io.dart'; +import 'package:worker_manager/src/worker/worker.dart'; +import 'package:worker_manager/worker_manager.dart'; + +final workerManagerPatch = _Executor(); + +// [-2^54; 2^53] is compatible with dart2js, see core.int doc +const _minId = -9007199254740992; +const _maxId = 9007199254740992; + +class Mixinable { + late final itSelf = this as T; +} + +mixin _ExecutorLogger on Mixinable<_Executor> { + var log = false; + + @mustCallSuper + void init() { + logMessage("${itSelf._isolatesCount} workers have been spawned and initialized"); + } + + void logTaskAdded(String uid) { + logMessage("added task with number $uid"); + } + + @mustCallSuper + void dispose() { + logMessage("worker_manager have been disposed"); + } + + @mustCallSuper + void _cancel(Task task) { + logMessage("Task ${task.id} have been canceled"); + } + + void logMessage(String message) { + if (log) print(message); + } +} + +class _Executor extends Mixinable<_Executor> with _ExecutorLogger { + final _queue = PriorityQueue(); + final _pool = []; + var _nextTaskId = _minId; + var _dynamicSpawning = false; + var _isolatesCount = numberOfProcessors; + + @override + Future init({int? isolatesCount, bool? dynamicSpawning}) async { + if (_pool.isNotEmpty) { + print("worker_manager already warmed up, init is ignored. Dispose before init"); + return; + } + if (isolatesCount != null) { + if (isolatesCount < 0) { + throw Exception("isolatesCount must be greater than 0"); + } + + _isolatesCount = isolatesCount; + } + _dynamicSpawning = dynamicSpawning ?? false; + await _ensureWorkersInitialized(); + super.init(); + } + + @override + Future dispose() async { + _queue.clear(); + for (final worker in _pool) { + worker.kill(); + } + _pool.clear(); + super.dispose(); + } + + Cancelable execute(Execute execution, {WorkPriority priority = WorkPriority.immediately}) { + return _createCancelable(execution: execution, priority: priority); + } + + Cancelable executeNow(ExecuteGentle execution) { + final task = TaskGentle( + id: "", + workPriority: WorkPriority.immediately, + execution: execution, + completer: Completer(), + ); + + Future run() async { + try { + final result = await execution(() => task.canceled); + task.complete(result, null, null); + } catch (error, st) { + task.complete(null, error, st); + } + } + + run(); + return Cancelable(completer: task.completer, onCancel: () => _cancel(task)); + } + + Cancelable executeWithPort( + ExecuteWithPort execution, { + WorkPriority priority = WorkPriority.immediately, + required void Function(T value) onMessage, + }) { + return _createCancelable( + execution: execution, + priority: priority, + onMessage: (message) => onMessage(message as T), + ); + } + + Cancelable executeGentle(ExecuteGentle execution, {WorkPriority priority = WorkPriority.immediately}) { + return _createCancelable(execution: execution, priority: priority); + } + + Cancelable executeGentleWithPort( + ExecuteGentleWithPort execution, { + WorkPriority priority = WorkPriority.immediately, + required void Function(T value) onMessage, + }) { + return _createCancelable( + execution: execution, + priority: priority, + onMessage: (message) => onMessage(message as T), + ); + } + + void _createWorkers() { + for (var i = 0; i < _isolatesCount; i++) { + _pool.add(Worker()); + } + } + + Future _initializeWorkers() async { + await Future.wait(_pool.map((e) => e.initialize())); + } + + Cancelable _createCancelable({ + required Function execution, + WorkPriority priority = WorkPriority.immediately, + void Function(Object value)? onMessage, + }) { + if (_nextTaskId + 1 == _maxId) { + _nextTaskId = _minId; + } + final id = _nextTaskId.toString(); + _nextTaskId++; + late final Task task; + final completer = Completer(); + if (execution is Execute) { + task = TaskRegular(id: id, workPriority: priority, execution: execution, completer: completer); + } else if (execution is ExecuteWithPort) { + task = TaskWithPort( + id: id, + workPriority: priority, + execution: execution, + completer: completer, + onMessage: onMessage!, + ); + } else if (execution is ExecuteGentle) { + task = TaskGentle(id: id, workPriority: priority, execution: execution, completer: completer); + } else if (execution is ExecuteGentleWithPort) { + task = TaskGentleWithPort( + id: id, + workPriority: priority, + execution: execution, + completer: completer, + onMessage: onMessage!, + ); + } + _queue.add(task); + _schedule(); + logTaskAdded(task.id); + return Cancelable(completer: task.completer, onCancel: () => _cancel(task)); + } + + Future _ensureWorkersInitialized() async { + if (_pool.isEmpty) { + _createWorkers(); + if (!_dynamicSpawning) { + await _initializeWorkers(); + final poolSize = _pool.length; + final queueSize = _queue.length; + for (int i = 0; i <= min(poolSize, queueSize); i++) { + _schedule(); + } + } + } + if (_pool.every((worker) => worker.taskId != null)) { + return; + } + if (_dynamicSpawning) { + final freeWorker = _pool.firstWhereOrNull( + (worker) => worker.taskId == null && !worker.initialized && !worker.initializing, + ); + await freeWorker?.initialize(); + _schedule(); + } + } + + void _schedule() { + final availableWorker = _pool.firstWhereOrNull((worker) => worker.taskId == null && worker.initialized); + if (availableWorker == null) { + _ensureWorkersInitialized(); + return; + } + if (_queue.isEmpty) return; + final task = _queue.removeFirst(); + + availableWorker + .work(task) + .then( + (value) { + //could be completed already by cancel and it is normal. + //Assuming that worker finished with error and cleaned gracefully + task.complete(value, null, null); + }, + onError: (error, st) { + task.complete(null, error, st); + }, + ) + .whenComplete(() { + if (_dynamicSpawning && _queue.isEmpty) availableWorker.kill(); + _schedule(); + }); + } + + @override + void _cancel(Task task) { + task.cancel(); + _queue.remove(task); + final targetWorker = _pool.firstWhereOrNull((worker) => worker.taskId == task.id); + if (task is Gentle) { + targetWorker?.cancelGentle(); + } else { + targetWorker?.kill(); + if (!_dynamicSpawning) targetWorker?.initialize(); + } + super._cancel(task); + } +} diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 698b9774da..7ee04c07b1 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -282,6 +282,7 @@ Class | Method | HTTP request | Description *UsersAdminApi* | [**deleteUserAdmin**](doc//UsersAdminApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} | *UsersAdminApi* | [**getUserAdmin**](doc//UsersAdminApi.md#getuseradmin) | **GET** /admin/users/{id} | *UsersAdminApi* | [**getUserPreferencesAdmin**](doc//UsersAdminApi.md#getuserpreferencesadmin) | **GET** /admin/users/{id}/preferences | +*UsersAdminApi* | [**getUserSessionsAdmin**](doc//UsersAdminApi.md#getusersessionsadmin) | **GET** /admin/users/{id}/sessions | *UsersAdminApi* | [**getUserStatisticsAdmin**](doc//UsersAdminApi.md#getuserstatisticsadmin) | **GET** /admin/users/{id}/statistics | *UsersAdminApi* | [**restoreUserAdmin**](doc//UsersAdminApi.md#restoreuseradmin) | **POST** /admin/users/{id}/restore | *UsersAdminApi* | [**searchUsersAdmin**](doc//UsersAdminApi.md#searchusersadmin) | **GET** /admin/users | diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index e4fc1673ef..4a4301ff43 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -231,6 +231,62 @@ class UsersAdminApi { return null; } + /// This endpoint is an admin-only route, and requires the `adminSession.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] id (required): + Future getUserSessionsAdminWithHttpInfo(String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/admin/users/{id}/sessions' + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint is an admin-only route, and requires the `adminSession.read` permission. + /// + /// Parameters: + /// + /// * [String] id (required): + Future?> getUserSessionsAdmin(String id,) async { + final response = await getUserSessionsAdminWithHttpInfo(id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. /// /// Note: This method returns the HTTP [Response]. diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index 95b9a55fba..86011eb835 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -150,6 +150,7 @@ class Permission { static const adminUserPeriodRead = Permission._(r'adminUser.read'); static const adminUserPeriodUpdate = Permission._(r'adminUser.update'); static const adminUserPeriodDelete = Permission._(r'adminUser.delete'); + static const adminSessionPeriodRead = Permission._(r'adminSession.read'); static const adminAuthPeriodUnlinkAll = Permission._(r'adminAuth.unlinkAll'); /// List of all possible values in this [enum][Permission]. @@ -281,6 +282,7 @@ class Permission { adminUserPeriodRead, adminUserPeriodUpdate, adminUserPeriodDelete, + adminSessionPeriodRead, adminAuthPeriodUnlinkAll, ]; @@ -447,6 +449,7 @@ class PermissionTypeTransformer { case r'adminUser.read': return Permission.adminUserPeriodRead; case r'adminUser.update': return Permission.adminUserPeriodUpdate; case r'adminUser.delete': return Permission.adminUserPeriodDelete; + case r'adminSession.read': return Permission.adminSessionPeriodRead; case r'adminAuth.unlinkAll': return Permission.adminAuthPeriodUnlinkAll; default: if (!allowNull) { diff --git a/mobile/openapi/lib/model/session_create_response_dto.dart b/mobile/openapi/lib/model/session_create_response_dto.dart index a4f93e8d9c..e16597f3b5 100644 --- a/mobile/openapi/lib/model/session_create_response_dto.dart +++ b/mobile/openapi/lib/model/session_create_response_dto.dart @@ -13,6 +13,7 @@ part of openapi.api; class SessionCreateResponseDto { /// Returns a new [SessionCreateResponseDto] instance. SessionCreateResponseDto({ + required this.appVersion, required this.createdAt, required this.current, required this.deviceOS, @@ -24,6 +25,8 @@ class SessionCreateResponseDto { required this.updatedAt, }); + String? appVersion; + String createdAt; bool current; @@ -50,6 +53,7 @@ class SessionCreateResponseDto { @override bool operator ==(Object other) => identical(this, other) || other is SessionCreateResponseDto && + other.appVersion == appVersion && other.createdAt == createdAt && other.current == current && other.deviceOS == deviceOS && @@ -63,6 +67,7 @@ class SessionCreateResponseDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (appVersion == null ? 0 : appVersion!.hashCode) + (createdAt.hashCode) + (current.hashCode) + (deviceOS.hashCode) + @@ -74,10 +79,15 @@ class SessionCreateResponseDto { (updatedAt.hashCode); @override - String toString() => 'SessionCreateResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, isPendingSyncReset=$isPendingSyncReset, token=$token, updatedAt=$updatedAt]'; + String toString() => 'SessionCreateResponseDto[appVersion=$appVersion, createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, isPendingSyncReset=$isPendingSyncReset, token=$token, updatedAt=$updatedAt]'; Map toJson() { final json = {}; + if (this.appVersion != null) { + json[r'appVersion'] = this.appVersion; + } else { + // json[r'appVersion'] = null; + } json[r'createdAt'] = this.createdAt; json[r'current'] = this.current; json[r'deviceOS'] = this.deviceOS; @@ -103,6 +113,7 @@ class SessionCreateResponseDto { final json = value.cast(); return SessionCreateResponseDto( + appVersion: mapValueOfType(json, r'appVersion'), createdAt: mapValueOfType(json, r'createdAt')!, current: mapValueOfType(json, r'current')!, deviceOS: mapValueOfType(json, r'deviceOS')!, @@ -159,6 +170,7 @@ class SessionCreateResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'appVersion', 'createdAt', 'current', 'deviceOS', diff --git a/mobile/openapi/lib/model/session_response_dto.dart b/mobile/openapi/lib/model/session_response_dto.dart index e76e4d48b4..85acb8a358 100644 --- a/mobile/openapi/lib/model/session_response_dto.dart +++ b/mobile/openapi/lib/model/session_response_dto.dart @@ -13,6 +13,7 @@ part of openapi.api; class SessionResponseDto { /// Returns a new [SessionResponseDto] instance. SessionResponseDto({ + required this.appVersion, required this.createdAt, required this.current, required this.deviceOS, @@ -23,6 +24,8 @@ class SessionResponseDto { required this.updatedAt, }); + String? appVersion; + String createdAt; bool current; @@ -47,6 +50,7 @@ class SessionResponseDto { @override bool operator ==(Object other) => identical(this, other) || other is SessionResponseDto && + other.appVersion == appVersion && other.createdAt == createdAt && other.current == current && other.deviceOS == deviceOS && @@ -59,6 +63,7 @@ class SessionResponseDto { @override int get hashCode => // ignore: unnecessary_parenthesis + (appVersion == null ? 0 : appVersion!.hashCode) + (createdAt.hashCode) + (current.hashCode) + (deviceOS.hashCode) + @@ -69,10 +74,15 @@ class SessionResponseDto { (updatedAt.hashCode); @override - String toString() => 'SessionResponseDto[createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, isPendingSyncReset=$isPendingSyncReset, updatedAt=$updatedAt]'; + String toString() => 'SessionResponseDto[appVersion=$appVersion, createdAt=$createdAt, current=$current, deviceOS=$deviceOS, deviceType=$deviceType, expiresAt=$expiresAt, id=$id, isPendingSyncReset=$isPendingSyncReset, updatedAt=$updatedAt]'; Map toJson() { final json = {}; + if (this.appVersion != null) { + json[r'appVersion'] = this.appVersion; + } else { + // json[r'appVersion'] = null; + } json[r'createdAt'] = this.createdAt; json[r'current'] = this.current; json[r'deviceOS'] = this.deviceOS; @@ -97,6 +107,7 @@ class SessionResponseDto { final json = value.cast(); return SessionResponseDto( + appVersion: mapValueOfType(json, r'appVersion'), createdAt: mapValueOfType(json, r'createdAt')!, current: mapValueOfType(json, r'current')!, deviceOS: mapValueOfType(json, r'deviceOS')!, @@ -152,6 +163,7 @@ class SessionResponseDto { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'appVersion', 'createdAt', 'current', 'deviceOS', diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 125e4d46e2..44bb2ae65e 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: args - sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.7.0" async: dependency: "direct main" description: @@ -77,10 +77,10 @@ packages: dependency: "direct main" description: name: background_downloader - sha256: "9ed74c55750932178f6989ba8a659687c2a102e05b70f561a1b3f047a5dda790" + sha256: a22acfa37aa06ba5cfe6eb7b1aa700c78af64770ff450c73dd3d279d7c37d4ac url: "https://pub.dev" source: hosted - version: "9.2.5" + version: "9.2.6" bonsoir: dependency: transitive description: @@ -437,10 +437,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "49413c8ca514dea7633e8def233b25efdf83ec8522955cc2c0e3ad802927e7c6" + sha256: dd0e8e02186b2196c7848c9d394a5fd6e5b57a43a546082c5820b1ec72317e33 url: "https://pub.dev" source: hosted - version: "12.1.0" + version: "12.2.0" device_info_plus_platform_interface: dependency: transitive description: @@ -469,34 +469,26 @@ packages: dependency: "direct main" description: name: drift_flutter - sha256: "0cadbf3b8733409a6cf61d18ba2e94e149df81df7de26f48ae0695b48fd71922" + sha256: b52bd710f809db11e25259d429d799d034ba1c5224ce6a73fe8419feb980d44c url: "https://pub.dev" source: hosted - version: "0.2.4" + version: "0.2.6" dynamic_color: dependency: "direct main" description: name: dynamic_color - sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d + sha256: "43a5a6679649a7731ab860334a5812f2067c2d9ce6452cf069c5e0c25336c17c" url: "https://pub.dev" source: hosted - version: "1.7.0" - easy_image_viewer: - dependency: "direct main" - description: - name: easy_image_viewer - sha256: fb6cb123c3605552cc91150dcdb50ca977001dcddfb71d20caa0c5edc9a80947 - url: "https://pub.dev" - source: hosted - version: "1.5.1" + version: "1.8.1" easy_localization: dependency: "direct main" description: name: easy_localization - sha256: "0f5239c7b8ab06c66440cfb0e9aa4b4640429c6668d5a42fe389c5de42220b12" + sha256: "2ccdf9db8fe4d9c5a75c122e6275674508fd0f0d49c827354967b8afcc56bbed" url: "https://pub.dev" source: hosted - version: "3.0.7+1" + version: "3.0.8" easy_logger: dependency: transitive description: @@ -594,10 +586,10 @@ packages: dependency: "direct main" description: name: flutter_displaymode - sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef" + sha256: ecd44b1e902b0073b42ff5b55bf283f38e088270724cdbb7f7065ccf54aa60a8 url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.7.0" flutter_driver: dependency: transitive description: flutter @@ -607,18 +599,18 @@ packages: dependency: "direct main" description: name: flutter_hooks - sha256: b772e710d16d7a20c0740c4f855095026b31c7eb5ba3ab67d2bd52021cd9461d + sha256: "8ae1f090e5f4ef5cfa6670ce1ab5dddadd33f3533a7f9ba19d9f958aa2a89f42" url: "https://pub.dev" source: hosted - version: "0.21.2" + version: "0.21.3+1" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons - sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" url: "https://pub.dev" source: hosted - version: "0.14.3" + version: "0.14.4" flutter_lints: dependency: "direct dev" description: @@ -660,10 +652,10 @@ packages: dependency: "direct dev" description: name: flutter_native_splash - sha256: edb09c35ee9230c4b03f13dd45bb3a276d0801865f0a4650b7e2a3bba61a803a + sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.7" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -732,10 +724,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b + sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -745,10 +737,10 @@ packages: dependency: "direct main" description: name: flutter_udid - sha256: be464dc5b1fb7ee894f6a32d65c086ca5e177fdcf9375ac08d77495b98150f84 + sha256: "166bee5989a58c66b8b62000ea65edccc7c8167bbafdbb08022638db330dd030" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.0" flutter_web_auth_2: dependency: "direct main" description: @@ -799,14 +791,22 @@ packages: description: flutter source: sdk version: "0.0.0" + geoclue: + dependency: transitive + description: + name: geoclue + sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f + url: "https://pub.dev" + source: hosted + version: "0.1.1" geolocator: dependency: "direct main" description: name: geolocator - sha256: e7ebfa04ce451daf39b5499108c973189a71a919aa53c1204effda1c5b93b822 + sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516" url: "https://pub.dev" source: hosted - version: "14.0.0" + version: "14.0.2" geolocator_android: dependency: transitive description: @@ -823,6 +823,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.9" + geolocator_linux: + dependency: transitive + description: + name: geolocator_linux + sha256: c4e966f0a7a87e70049eac7a2617f9e16fd4c585a26e4330bdfc3a71e6a721f3 + url: "https://pub.dev" + source: hosted + version: "0.2.3" geolocator_platform_interface: dependency: transitive description: @@ -863,14 +871,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + gsettings: + dependency: transitive + description: + name: gsettings + sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c" + url: "https://pub.dev" + source: hosted + version: "0.2.8" home_widget: dependency: "direct main" description: name: home_widget - sha256: ad9634ef5894f3bac73f04d59e2e5151a39798f49985399fd928dadc828d974a + sha256: "908d033514a981f829fd98213909e11a428104327be3b422718aa643ac9d084a" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.8.1" hooks_riverpod: dependency: "direct main" description: @@ -891,18 +907,18 @@ packages: dependency: transitive description: name: html - sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" url: "https://pub.dev" source: hosted - version: "0.15.5" + version: "0.15.6" http: dependency: "direct main" description: name: http - sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.5.0" http_multi_server: dependency: transitive description: @@ -923,74 +939,74 @@ packages: dependency: transitive description: name: image - sha256: "13d3349ace88f12f4a0d175eb5c12dcdd39d35c4c109a8a13dfeb6d0bd9e31c3" + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.dev" source: hosted - version: "4.5.3" + version: "4.5.4" image_picker: dependency: "direct main" description: name: image_picker - sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: "8bd392ba8b0c8957a157ae0dc9fcf48c58e6c20908d5880aea1d79734df090e9" + sha256: "58a85e6f09fe9c4484d53d18a0bd6271b72c53fce1d05e6f745ae36d8c18efca" url: "https://pub.dev" source: hosted - version: "0.8.12+22" + version: "0.8.13+5" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83" + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100" + sha256: e675c22790bcc24e9abd455deead2b7a88de4b79f7327a281812f14de1a56f58 url: "https://pub.dev" source: hosted - version: "0.8.12+2" + version: "0.8.13+1" image_picker_linux: dependency: transitive description: name: image_picker_linux - sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" url: "https://pub.dev" source: hosted - version: "0.2.1+1" + version: "0.2.2" image_picker_macos: dependency: transitive description: name: image_picker_macos - sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1" + sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" url: "https://pub.dev" source: hosted - version: "0.2.1+2" + version: "0.2.2+1" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0" + sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665" url: "https://pub.dev" source: hosted - version: "2.10.1" + version: "2.11.0" image_picker_windows: dependency: transitive description: name: image_picker_windows - sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae url: "https://pub.dev" source: hosted - version: "0.2.1+1" + version: "0.2.2" immich_mobile_immich_lint: dependency: "direct dev" description: @@ -1321,10 +1337,10 @@ packages: dependency: "direct main" description: name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.3" path_provider_linux: dependency: transitive description: @@ -1401,42 +1417,34 @@ packages: dependency: transitive description: name: petitparser - sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "7.0.1" photo_manager: dependency: "direct main" description: name: photo_manager - sha256: "0bc7548fd3111eb93a3b0abf1c57364e40aeda32512c100085a48dade60e574f" + sha256: a0d9a7a9bc35eda02d33766412bde6d883a8b0acb86bbe37dac5f691a0894e8a url: "https://pub.dev" source: hosted - version: "3.6.4" - photo_manager_image_provider: - dependency: "direct main" - description: - name: photo_manager_image_provider - sha256: b6015b67b32f345f57cf32c126f871bced2501236c405aafaefa885f7c821e4f - url: "https://pub.dev" - source: hosted - version: "2.2.0" + version: "3.7.1" pigeon: dependency: "direct dev" description: name: pigeon - sha256: b65acb352dc5a5f8615d074a83419388cbcc249f07c6d8c78b5bc16680a55dda + sha256: "0045b172d1da43c40cb3f58e80e04b50a65cba20b8b70dc880af04181f7758da" url: "https://pub.dev" source: hosted - version: "26.0.0" + version: "26.0.2" pinput: dependency: "direct main" description: name: pinput - sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + sha256: c41f42ee301505ae2375ec32871c985d3717bf8aee845620465b286e0140aad2 url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" platform: dependency: transitive description: @@ -1585,18 +1593,18 @@ packages: dependency: "direct main" description: name: share_handler - sha256: "76575533be04df3fecbebd3c5b5325a8271b5973131f8b8b0ab8490c395a5d37" + sha256: "0a6d007f0e44fbee27164adcd159ecbc88238864313f4e5c58161cae2180328d" url: "https://pub.dev" source: hosted - version: "0.0.22" + version: "0.0.25" share_handler_android: dependency: transitive description: name: share_handler_android - sha256: "124dcc914fb7ecd89076d3dc28435b98fe2129a988bf7742f7a01dcb66a95667" + sha256: caf555b933dc72783aa37fef75688c7b86bd6f7bc17d80fbf585bc42f123cc8d url: "https://pub.dev" source: hosted - version: "0.0.9" + version: "0.0.11" share_handler_ios: dependency: transitive description: @@ -1950,10 +1958,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.2" url_launcher_android: dependency: transitive description: @@ -2038,10 +2046,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc url: "https://pub.dev" source: hosted - version: "1.1.16" + version: "1.1.19" vector_math: dependency: transitive description: @@ -2062,18 +2070,18 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: "36c88af0b930121941345306d259ec4cc4ecca3b151c02e3a9e71aede83c615e" + sha256: "61713aa82b7f85c21c9f4cd0a148abd75f38a74ec645fcb1e446f882c82fd09b" url: "https://pub.dev" source: hosted - version: "1.2.10" + version: "1.3.3" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: "70e780bc99796e1db82fe764b1e7dcb89a86f1e5b3afb1db354de50f2e41eb7a" + sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.3.0" watcher: dependency: transitive description: @@ -2142,10 +2150,10 @@ packages: dependency: "direct main" description: name: worker_manager - sha256: "086ed63e9b36266e851404ca90fd44e37c0f4c9bbf819e5f8d7c87f9741c0591" + sha256: "1bce9f894a0c187856f5fc0e150e7fe1facce326f048ca6172947754dac3d4f3" url: "https://pub.dev" source: hosted - version: "7.2.3" + version: "7.2.7" xdg_directories: dependency: transitive description: @@ -2158,10 +2166,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.6.1" xxh3: dependency: transitive description: @@ -2179,5 +2187,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.35.4" + dart: ">=3.9.0 <4.0.0" + flutter: ">=3.35.6" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 82ce321954..a6d20a2cb3 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -6,115 +6,112 @@ version: 2.1.0+3022 environment: sdk: '>=3.8.0 <4.0.0' - flutter: 3.35.4 + flutter: 3.35.6 dependencies: - flutter: - sdk: flutter - - async: ^2.11.0 + async: ^2.13.0 auto_route: ^9.2.0 - background_downloader: ^9.2.5 + background_downloader: ^9.2.6 cached_network_image: ^3.4.1 cancellation_token_http: ^2.1.0 cast: ^2.1.0 - collection: ^1.18.0 + collection: ^1.19.1 connectivity_plus: ^6.1.3 crop_image: ^1.0.16 crypto: ^3.0.6 - device_info_plus: ^12.0.0 - dynamic_color: ^1.7.0 - easy_image_viewer: ^1.5.1 - easy_localization: ^3.0.7+1 + device_info_plus: ^12.2.0 + # DB + drift: ^2.26.0 + drift_flutter: ^0.2.6 + dynamic_color: ^1.8.1 + easy_localization: ^3.0.8 + ffi: ^2.1.4 file_picker: ^8.0.0+1 + flutter: + sdk: flutter flutter_cache_manager: ^3.4.1 - flutter_displaymode: ^0.6.0 - flutter_hooks: ^0.21.2 + flutter_displaymode: ^0.7.0 + flutter_hooks: ^0.21.3+1 flutter_local_notifications: ^17.2.1+2 flutter_secure_storage: ^9.2.4 - flutter_svg: ^2.0.17 - flutter_udid: ^3.0.0 + flutter_svg: ^2.2.1 + flutter_udid: ^4.0.0 flutter_web_auth_2: ^5.0.0-alpha.0 fluttertoast: ^8.2.12 - geolocator: ^14.0.0 + geolocator: ^14.0.2 + home_widget: ^0.8.1 hooks_riverpod: ^2.6.1 - home_widget: ^0.8.0 - http: ^1.3.0 - image_picker: ^1.1.2 - intl: ^0.20.0 - local_auth: ^2.3.0 - logging: ^1.3.0 - maplibre_gl: ^0.22.0 - network_info_plus: ^6.1.3 - octo_image: ^2.1.0 - package_info_plus: ^8.3.0 - path: ^1.9.1 - path_provider: ^2.1.5 - path_provider_foundation: ^2.4.1 - permission_handler: ^11.4.0 - photo_manager: ^3.6.4 - photo_manager_image_provider: ^2.2.0 - pinput: ^5.0.1 - punycode: ^1.0.0 - riverpod_annotation: ^2.6.1 - scrollable_positioned_list: ^0.3.8 - share_handler: ^0.0.22 - share_plus: ^10.1.4 - sliver_tools: ^0.2.12 - socket_io_client: ^2.0.3+1 - stream_transform: ^2.1.1 - thumbhash: 0.1.0+1 - timezone: ^0.9.4 - url_launcher: ^6.3.1 - uuid: ^4.5.1 - wakelock_plus: ^1.2.10 - worker_manager: ^7.2.3 - scroll_date_picker: ^3.8.0 - ffi: ^2.1.4 - - native_video_player: - git: - url: https://github.com/immich-app/native_video_player - ref: '893894b' - openapi: - path: openapi + http: ^1.5.0 + image_picker: ^1.2.0 + intl: ^0.20.2 isar: git: url: https://github.com/immich-app/isar ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' path: packages/isar/ isar_community_flutter_libs: 3.3.0-dev.3 - # DB - drift: ^2.23.1 - drift_flutter: ^0.2.4 + local_auth: ^2.3.0 + logging: ^1.3.0 + maplibre_gl: ^0.22.0 + + native_video_player: + git: + url: https://github.com/immich-app/native_video_player + ref: '893894b' + network_info_plus: ^6.1.3 + octo_image: ^2.1.0 + openapi: + path: openapi + package_info_plus: ^8.3.0 + path: ^1.9.1 + path_provider: ^2.1.5 + path_provider_foundation: ^2.4.3 + permission_handler: ^11.4.0 + photo_manager: ^3.7.1 + pinput: ^5.0.2 + punycode: ^1.0.0 + riverpod_annotation: ^2.6.1 + scroll_date_picker: ^3.8.0 + scrollable_positioned_list: ^0.3.8 + share_handler: ^0.0.25 + share_plus: ^10.1.4 + sliver_tools: ^0.2.12 + socket_io_client: ^2.0.3+1 + stream_transform: ^2.1.1 + thumbhash: 0.1.0+1 + timezone: ^0.9.4 + url_launcher: ^6.3.2 + uuid: ^4.5.1 + wakelock_plus: ^1.3.0 + worker_manager: ^7.2.7 dev_dependencies: + auto_route_generator: ^9.0.0 + build_runner: ^2.4.8 + custom_lint: ^0.7.5 + # Drift generator + drift_dev: ^2.26.0 + fake_async: ^1.3.3 + file: ^7.0.1 # for MemoryFileSystem + flutter_launcher_icons: ^0.14.4 + flutter_lints: ^5.0.0 + flutter_native_splash: ^2.4.7 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 - build_runner: ^2.4.8 - auto_route_generator: ^9.0.0 - flutter_launcher_icons: ^0.14.3 - flutter_native_splash: ^2.4.5 + immich_mobile_immich_lint: + path: './immich_lint' + integration_test: + sdk: flutter isar_generator: git: url: https://github.com/immich-app/isar ref: 'bb1dca40fe87a001122e5d43bc6254718cb49f3a' path: packages/isar_generator/ - integration_test: - sdk: flutter - custom_lint: ^0.7.5 - riverpod_lint: ^2.6.1 - riverpod_generator: ^2.6.1 mocktail: ^1.0.4 - immich_mobile_immich_lint: - path: './immich_lint' - fake_async: ^1.3.1 - file: ^7.0.1 # for MemoryFileSystem - # Drift generator - drift_dev: ^2.23.1 # Type safe platform code - pigeon: ^26.0.0 + pigeon: ^26.0.2 + riverpod_generator: ^2.6.1 + riverpod_lint: ^2.6.1 flutter: uses-material-design: true diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index fdfc40eb6c..3b258d505f 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -773,6 +773,54 @@ "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." } }, + "/admin/users/{id}/sessions": { + "get": { + "operationId": "getUserSessionsAdmin", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/SessionResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Users (admin)" + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminSession.read", + "description": "This endpoint is an admin-only route, and requires the `adminSession.read` permission." + } + }, "/admin/users/{id}/statistics": { "get": { "operationId": "getUserStatisticsAdmin", @@ -13267,6 +13315,7 @@ "adminUser.read", "adminUser.update", "adminUser.delete", + "adminSession.read", "adminAuth.unlinkAll" ], "type": "string" @@ -14303,6 +14352,10 @@ }, "SessionCreateResponseDto": { "properties": { + "appVersion": { + "nullable": true, + "type": "string" + }, "createdAt": { "type": "string" }, @@ -14332,6 +14385,7 @@ } }, "required": [ + "appVersion", "createdAt", "current", "deviceOS", @@ -14345,6 +14399,10 @@ }, "SessionResponseDto": { "properties": { + "appVersion": { + "nullable": true, + "type": "string" + }, "createdAt": { "type": "string" }, @@ -14371,6 +14429,7 @@ } }, "required": [ + "appVersion", "createdAt", "current", "deviceOS", diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index 5be13572e2..c179d1504e 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.18.8", + "@types/node": "^22.18.10", "typescript": "^5.3.3" }, "repository": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index 5c952c30af..cdd0047701 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -244,6 +244,17 @@ export type UserPreferencesUpdateDto = { sharedLinks?: SharedLinksUpdate; tags?: TagsUpdate; }; +export type SessionResponseDto = { + appVersion: string | null; + createdAt: string; + current: boolean; + deviceOS: string; + deviceType: string; + expiresAt?: string; + id: string; + isPendingSyncReset: boolean; + updatedAt: string; +}; export type AssetStatsResponseDto = { images: number; total: number; @@ -1192,16 +1203,6 @@ export type ServerVersionHistoryResponseDto = { id: string; version: string; }; -export type SessionResponseDto = { - createdAt: string; - current: boolean; - deviceOS: string; - deviceType: string; - expiresAt?: string; - id: string; - isPendingSyncReset: boolean; - updatedAt: string; -}; export type SessionCreateDto = { deviceOS?: string; deviceType?: string; @@ -1209,6 +1210,7 @@ export type SessionCreateDto = { duration?: number; }; export type SessionCreateResponseDto = { + appVersion: string | null; createdAt: string; current: boolean; deviceOS: string; @@ -1853,6 +1855,19 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `adminSession.read` permission. + */ +export function getUserSessionsAdmin({ id }: { + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: SessionResponseDto[]; + }>(`/admin/users/${encodeURIComponent(id)}/sessions`, { + ...opts + })); +} /** * This endpoint is an admin-only route, and requires the `adminUser.read` permission. */ @@ -4830,6 +4845,7 @@ export enum Permission { AdminUserRead = "adminUser.read", AdminUserUpdate = "adminUser.update", AdminUserDelete = "adminUser.delete", + AdminSessionRead = "adminSession.read", AdminAuthUnlinkAll = "adminAuth.unlinkAll" } export enum AssetMetadataKey { diff --git a/package.json b/package.json index 415c598179..24ab4761c6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Monorepo for Immich", "private": true, - "packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34", + "packageManager": "pnpm@10.18.3+sha512.bbd16e6d7286fd7e01f6b3c0b3c932cda2965c06a908328f74663f10a9aea51f1129eea615134bf992831b009eabe167ecb7008b597f40ff9bc75946aadfb08d", "engines": { "pnpm": ">=10.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 269b246de9..98f4cd5540 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,11 +63,11 @@ importers: specifier: ^4.13.1 version: 4.13.4 '@types/node': - specifier: ^22.18.8 - version: 22.18.10 + specifier: ^22.18.10 + version: 22.18.12 '@vitest/coverage-v8': specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) byte-size: specifier: ^9.0.0 version: 9.0.1 @@ -106,19 +106,19 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) vite: specifier: ^7.0.0 - version: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest-fetch-mock: specifier: ^0.4.0 - version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) yaml: specifier: ^2.3.1 version: 2.8.1 @@ -211,8 +211,8 @@ importers: specifier: ^3.4.2 version: 3.7.1 '@types/node': - specifier: ^22.18.8 - version: 22.18.10 + specifier: ^22.18.10 + version: 22.18.12 '@types/oidc-provider': specifier: ^9.0.0 version: 9.5.0 @@ -238,8 +238,8 @@ importers: specifier: ^60.0.0 version: 60.0.0(eslint@9.37.0(jiti@2.6.1)) exiftool-vendored: - specifier: ^28.3.1 - version: 28.8.0 + specifier: ^31.1.0 + version: 31.1.0 globals: specifier: ^16.0.0 version: 16.4.0 @@ -278,13 +278,13 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) utimes: specifier: ^5.2.1 version: 5.2.1(encoding@0.1.13) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) open-api/typescript-sdk: dependencies: @@ -293,8 +293,8 @@ importers: version: 1.0.4 devDependencies: '@types/node': - specifier: ^22.18.8 - version: 22.18.10 + specifier: ^22.18.10 + version: 22.18.12 typescript: specifier: ^5.3.3 version: 5.9.3 @@ -303,7 +303,7 @@ importers: dependencies: '@nestjs/bullmq': specifier: ^11.0.1 - version: 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0) + version: 11.0.4(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0) '@nestjs/common': specifier: ^11.0.4 version: 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -321,7 +321,7 @@ importers: version: 6.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@nestjs/swagger': specifier: ^11.0.2 - version: 11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) + version: 11.2.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) '@nestjs/websockets': specifier: ^11.0.4 version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-socket.io@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -404,8 +404,8 @@ importers: specifier: 4.3.3 version: 4.3.3 exiftool-vendored: - specifier: ^28.8.0 - version: 28.8.0 + specifier: ^31.1.0 + version: 31.1.0 express: specifier: ^5.1.0 version: 5.1.0 @@ -425,8 +425,8 @@ importers: specifier: ^7.6.0 version: 7.14.0 ioredis: - specifier: ^5.3.2 - version: 5.8.1 + specifier: ^5.8.2 + version: 5.8.2 js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -450,13 +450,13 @@ importers: version: 2.0.2 nest-commander: specifier: ^3.16.0 - version: 3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.10)(typescript@5.9.3) + version: 3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.12)(typescript@5.9.3) nestjs-cls: specifier: ^5.0.0 version: 5.4.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) nestjs-kysely: - specifier: 3.0.0 - version: 3.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2) + specifier: 3.1.2 + version: 3.1.2(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2) nestjs-otel: specifier: ^7.0.0 version: 7.0.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) @@ -513,13 +513,13 @@ importers: version: 4.8.1 tailwindcss-preset-email: specifier: ^1.4.0 - version: 1.4.0(tailwindcss@3.4.18(yaml@2.8.1)) + version: 1.4.1(tailwindcss@3.4.18(yaml@2.8.1)) thumbhash: specifier: ^0.1.1 version: 0.1.1 ua-parser-js: specifier: ^2.0.0 - version: 2.0.5 + version: 2.0.6 uuid: specifier: ^11.1.0 version: 11.1.0 @@ -532,10 +532,10 @@ importers: version: 9.37.0 '@nestjs/cli': specifier: ^11.0.2 - version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.10) + version: 11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.12) '@nestjs/schematics': specifier: ^11.0.0 - version: 11.0.8(chokidar@4.0.3)(typescript@5.9.3) + version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) '@nestjs/testing': specifier: ^11.0.4 version: 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@nestjs/platform-express@11.1.6) @@ -582,8 +582,8 @@ importers: specifier: ^2.0.0 version: 2.0.0 '@types/node': - specifier: ^22.18.8 - version: 22.18.10 + specifier: ^22.18.10 + version: 22.18.12 '@types/nodemailer': specifier: ^7.0.0 version: 7.0.2 @@ -613,7 +613,7 @@ importers: version: 13.15.3 '@vitest/coverage-v8': specifier: ^3.0.0 - version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) eslint: specifier: ^9.14.0 version: 9.37.0(jiti@2.6.1) @@ -634,7 +634,7 @@ importers: version: 5.5.0 node-gyp: specifier: ^11.2.0 - version: 11.4.2 + version: 11.5.0 pngjs: specifier: ^7.0.0 version: 7.0.0 @@ -661,28 +661,28 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.28.0 - version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) unplugin-swc: specifier: ^1.4.5 - version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.4) + version: 1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.5) vite-tsconfig-paths: specifier: ^5.0.0 - version: 5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.9.3)(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) vitest: specifier: ^3.0.0 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) web: dependencies: '@formatjs/icu-messageformat-parser': specifier: ^2.9.8 - version: 2.11.3 + version: 2.11.4 '@immich/sdk': specifier: file:../open-api/typescript-sdk version: link:../open-api/typescript-sdk '@immich/ui': - specifier: ^0.34.0 - version: 0.34.2(@internationalized/date@3.8.2)(svelte@5.39.11) + specifier: ^0.37.1 + version: 0.37.1(@internationalized/date@3.8.2)(svelte@5.40.1) '@mapbox/mapbox-gl-rtl-text': specifier: 0.2.3 version: 0.2.3(mapbox-gl@1.13.3) @@ -712,7 +712,7 @@ importers: version: 0.41.3 '@zoom-image/svelte': specifier: ^0.3.0 - version: 0.3.7(svelte@5.39.11) + version: 0.3.7(svelte@5.40.1) async-mutex: specifier: ^0.5.0 version: 0.5.0 @@ -736,7 +736,7 @@ importers: version: 20.0.2 intl-messageformat: specifier: ^10.7.11 - version: 10.7.17 + version: 10.7.18 justified-layout: specifier: ^4.1.0 version: 4.1.0 @@ -748,7 +748,7 @@ importers: version: 3.7.2 maplibre-gl: specifier: ^5.6.2 - version: 5.8.0 + version: 5.9.0 pmtiles: specifier: ^4.3.0 version: 4.3.0 @@ -766,13 +766,13 @@ importers: version: 5.2.2 svelte-i18n: specifier: ^4.0.1 - version: 4.0.1(svelte@5.39.11) + version: 4.0.1(svelte@5.40.1) svelte-maplibre: specifier: ^1.2.0 - version: 1.2.1(svelte@5.39.11) + version: 1.2.3(svelte@5.40.1) svelte-persisted-store: specifier: ^0.12.0 - version: 0.12.0(svelte@5.39.11) + version: 0.12.0(svelte@5.40.1) tabbable: specifier: ^6.2.0 version: 6.2.0 @@ -785,7 +785,7 @@ importers: version: 9.37.0 '@faker-js/faker': specifier: ^10.0.0 - version: 10.0.0 + version: 10.1.0 '@koddsson/eslint-plugin-tscompat': specifier: ^0.2.0 version: 0.2.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) @@ -794,25 +794,25 @@ importers: version: 3.1.2 '@sveltejs/adapter-static': specifier: ^3.0.8 - version: 3.0.10(@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) + version: 3.0.10(@sveltejs/kit@2.47.0(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))) '@sveltejs/enhanced-img': specifier: ^0.8.0 - version: 0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.4)(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.5)(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/kit': specifier: ^2.27.1 - version: 2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 2.47.0(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@sveltejs/vite-plugin-svelte': specifier: 6.2.1 - version: 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@tailwindcss/vite': specifier: ^4.1.7 - version: 4.1.14(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 4.1.14(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/jest-dom': specifier: ^6.4.2 version: 6.9.1 '@testing-library/svelte': specifier: ^5.2.8 - version: 5.2.8(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + version: 5.2.8(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@testing-library/user-event': specifier: ^14.5.2 version: 14.6.1(@testing-library/dom@10.4.0) @@ -851,7 +851,7 @@ importers: version: 6.0.2(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-svelte: specifier: ^3.12.4 - version: 3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11) + version: 3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.40.1) eslint-plugin-unicorn: specifier: ^61.0.2 version: 61.0.2(eslint@9.37.0(jiti@2.6.1)) @@ -872,19 +872,19 @@ importers: version: 4.1.1(prettier@3.6.2) prettier-plugin-svelte: specifier: ^3.3.3 - version: 3.4.0(prettier@3.6.2)(svelte@5.39.11) + version: 3.4.0(prettier@3.6.2)(svelte@5.40.1) rollup-plugin-visualizer: specifier: ^6.0.0 - version: 6.0.4(rollup@4.52.4) + version: 6.0.4(rollup@4.52.5) svelte: - specifier: 5.39.11 - version: 5.39.11 + specifier: 5.40.1 + version: 5.40.1 svelte-check: specifier: ^4.1.5 - version: 4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3) + version: 4.3.3(picomatch@4.0.3)(svelte@5.40.1)(typescript@5.9.3) svelte-eslint-parser: specifier: ^1.3.3 - version: 1.3.3(svelte@5.39.11) + version: 1.3.3(svelte@5.40.1) tailwindcss: specifier: ^4.1.7 version: 4.1.14 @@ -893,10 +893,10 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.45.0 - version: 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + version: 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) vite: specifier: ^7.1.2 - version: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + version: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) @@ -2191,8 +2191,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.10': - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2203,8 +2203,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.10': - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2215,8 +2215,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.10': - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2227,8 +2227,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.10': - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2239,8 +2239,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.10': - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2251,8 +2251,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.10': - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2263,8 +2263,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.10': - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2275,8 +2275,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.10': - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2287,8 +2287,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.10': - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2299,8 +2299,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.10': - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2311,8 +2311,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.10': - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2323,8 +2323,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.10': - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2335,8 +2335,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.10': - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2347,8 +2347,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.10': - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2359,8 +2359,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.10': - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2371,8 +2371,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.10': - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2383,14 +2383,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.10': - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.10': - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2401,14 +2401,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.10': - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.10': - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2419,14 +2419,14 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.10': - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.10': - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2437,8 +2437,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.10': - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2449,8 +2449,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.10': - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2461,8 +2461,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.10': - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2473,8 +2473,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.10': - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2525,8 +2525,8 @@ packages: resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@faker-js/faker@10.0.0': - resolution: {integrity: sha512-UollFEUkVXutsaP+Vndjxar40Gs5JL2HeLcl8xO1QAjJgOdhc3OmBFWyEylS+RddWaaBiAzH+5/17PLQJwDiLw==} + '@faker-js/faker@10.1.0': + resolution: {integrity: sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg==} engines: {node: ^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0, npm: '>=10'} '@fig/complete-commander@3.2.0': @@ -2543,17 +2543,17 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@formatjs/ecma402-abstract@2.3.5': - resolution: {integrity: sha512-1HTESOq1IUa23g1lFZEGIXsfZKZOwWmB9RROwGn+xariiQnd++wwTMvlRAbZ8wtXRHFUamJPxsKcxpSzeCvFWQ==} + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} '@formatjs/fast-memoize@2.2.7': resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} - '@formatjs/icu-messageformat-parser@2.11.3': - resolution: {integrity: sha512-H/KfWSosaiDiOaW4nHe1Fn4Cgzm+oFQ8giTmB5RJzTBNSMmd+j2NVrvvZHAmlxJHcuOelzKBLjQ2EDcyH4NSWw==} + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} - '@formatjs/icu-skeleton-parser@1.8.15': - resolution: {integrity: sha512-qNrKxWJmnWxin5U4A4Evy7C0rgRiNw3IqXu9OGuT31B8lDxBGl+OgT8kcq0ZVKK0gqA4l4SQB9x+SFAvLT5hcQ==} + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} '@formatjs/intl-localematcher@0.6.2': resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} @@ -2726,8 +2726,8 @@ packages: cpu: [x64] os: [win32] - '@immich/ui@0.34.2': - resolution: {integrity: sha512-tWjEV1prSZ9VLes69Ha9jnnZj6tbv/9+PdQjsK+5zK7sQok/l7kyzAobj5z4XRD11XtGk/cAqi/ZOlqRWvZilA==} + '@immich/ui@0.37.1': + resolution: {integrity: sha512-8S9KsyqyRcNgRHeBU8G3qMQ7D7fN4u9I31jjRc9c3s2tkiYucASofPJdcFdmGZnKLX5fIj+yofxiNZV9tVitOg==} peerDependencies: svelte: ^5.0.0 @@ -3033,8 +3033,8 @@ packages: resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} engines: {node: '>=6.0.0'} - '@maplibre/maplibre-gl-style-spec@24.2.0': - resolution: {integrity: sha512-cE80g83fRcBbZbQC70siOUxUK6YJ/5ZkClDZbmm+hzrUbv+J6yntkMmcpdz9DbOrWOM7FHKR5rruc6Q/hWx5cA==} + '@maplibre/maplibre-gl-style-spec@24.3.0': + resolution: {integrity: sha512-CTJc/Nvldv+GNQuis29VnyV0TYsFTgQBY3SNagTzZ28oHDsDYJ7LwEmfick4Z30wPwI/4gXe3se8PH2IIfLx2g==} hasBin: true '@maplibre/vt-pbf@4.0.3': @@ -3097,14 +3097,14 @@ packages: '@namnode/store@0.1.0': resolution: {integrity: sha512-4NGTldxKcmY0UuZ7OEkvCjs8ZEoeYB6M2UwMu74pdLiFMKxXbj9HdNk1Qn213bxX1O7bY5h+PLh5DZsTURZkYA==} - '@nestjs/bull-shared@11.0.3': - resolution: {integrity: sha512-CaHniPkLAxis6fAB1DB8WZELQv8VPCLedbj7iP0VQ1pz74i6NSzG9mBg6tOomXq/WW4la4P4OMGEQ48UAJh20A==} + '@nestjs/bull-shared@11.0.4': + resolution: {integrity: sha512-VBJcDHSAzxQnpcDfA0kt9MTGUD1XZzfByV70su0W0eDCQ9aqIEBlzWRW21tv9FG9dIut22ysgDidshdjlnczLw==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/bullmq@11.0.3': - resolution: {integrity: sha512-0Qr7Fk3Ir3V2OBIKJk+ArEM0AesGjKaNZA8QQ4fH3qGogudYADSjaNe910/OAfmX8q+cjCRorvwTLdcShwWEMw==} + '@nestjs/bullmq@11.0.4': + resolution: {integrity: sha512-wBzK9raAVG0/6NTMdvLGM4/FQ1lsB35/pYS8L6a0SDgkTiLpd7mAjQ8R692oMx5s7IjvgntaZOuTUrKYLNfIkA==} peerDependencies: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 @@ -3186,13 +3186,13 @@ packages: '@nestjs/common': ^10.0.0 || ^11.0.0 '@nestjs/core': ^10.0.0 || ^11.0.0 - '@nestjs/schematics@11.0.8': - resolution: {integrity: sha512-HKunkzfBYLpNyL/qP5wu0OBKVPrISJLnrB4r6S53fT99pEvopDcJAeIuznSAD1Dx1njUqpbTR/uGyD0xL1y0nw==} + '@nestjs/schematics@11.0.9': + resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==} peerDependencies: typescript: '>=4.8.2' - '@nestjs/swagger@11.2.0': - resolution: {integrity: sha512-5wolt8GmpNcrQv34tIPUtPoV1EeFbCetm40Ij3+M0FNNnf2RJ3FyWfuQvI8SBlcJyfaounYVTKzKHreFXsUyOg==} + '@nestjs/swagger@11.2.1': + resolution: {integrity: sha512-1MS7xf0pzc1mofG53xrrtrurnziafPUHkqzRm4YUVPA/egeiMaSerQBD/feiAeQ2BnX0WiLsTX4HQFO0icvOjQ==} peerDependencies: '@fastify/static': ^8.0.0 '@nestjs/common': ^11.0.1 @@ -3495,8 +3495,8 @@ packages: peerDependencies: '@photo-sphere-viewer/core': 5.14.0 - '@photostructure/tz-lookup@11.2.0': - resolution: {integrity: sha512-DwrvodcXHNSdGdeSF7SBL5o8aBlsaeuCuG7633F04nYsL3hn5Hxe3z/5kCqxv61J1q7ggKZ27GPylR3x0cPNXQ==} + '@photostructure/tz-lookup@11.2.1': + resolution: {integrity: sha512-ugPtvpdLwGQ8IWezSGFgUCYOpO/XXetfKLNv+UN2jjTYyfIDq9dA21GydGyzXuoQ06nN3VGBd3JxmEu+ZtXScg==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -3690,113 +3690,113 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.4': - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.4': - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.4': - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.4': - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.4': - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.4': - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.4': - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.4': - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.4': - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.4': - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.4': - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.4': - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.4': - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.4': - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.4': - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.4': - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.4': - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.4': - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.4': - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.4': - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.4': - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] @@ -4036,8 +4036,8 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || >=7.0.0 - '@sveltejs/kit@2.46.4': - resolution: {integrity: sha512-J1fd80WokLzIm6EAV7z7C2+/C02qVAX645LZomARARTRJkbbJSY1Jln3wtBZYibUB8c9/5Z6xqLAV39VdbtWCQ==} + '@sveltejs/kit@2.47.0': + resolution: {integrity: sha512-mznN01MBXtr4T7X/E3ENkhF6GzqxTxL6/whG3OzCzUu8G8KYRNiCdoxLMVWAHJx/mDMPP3XAeKCMZHF/Xd/CDw==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -4547,8 +4547,8 @@ packages: '@types/koa@3.0.0': resolution: {integrity: sha512-MOcVYdVYmkSutVHZZPh8j3+dAjLyR5Tl59CN0eKgpkE1h/LBSmPAsQQuWs+bKu7WtGNn+hKfJH9Gzml+PulmDg==} - '@types/leaflet@1.9.20': - resolution: {integrity: sha512-rooalPMlk61LCaLOvBF2VIf9M47HgMQqi5xQ9QRi7c8PkdIe0WrIi5IxXUXQjAdL0c+vcQ01mYWbthzmp9GHWw==} + '@types/leaflet@1.9.21': + resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -4595,8 +4595,8 @@ packages: '@types/node@20.19.21': resolution: {integrity: sha512-CsGG2P3I5y48RPMfprQGfy4JPRZ6csfC3ltBZSRItG3ngggmNY/qs2uZKp4p9VbrpqNNSMzUZNFZKzgOGnd/VA==} - '@types/node@22.18.10': - resolution: {integrity: sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg==} + '@types/node@22.18.12': + resolution: {integrity: sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==} '@types/node@24.7.2': resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==} @@ -4718,63 +4718,63 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.46.0': - resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} + '@typescript-eslint/eslint-plugin@8.46.1': + resolution: {integrity: sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.0 + '@typescript-eslint/parser': ^8.46.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.0': - resolution: {integrity: sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==} + '@typescript-eslint/parser@8.46.1': + resolution: {integrity: sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.0': - resolution: {integrity: sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==} + '@typescript-eslint/project-service@8.46.1': + resolution: {integrity: sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.46.0': - resolution: {integrity: sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==} + '@typescript-eslint/scope-manager@8.46.1': + resolution: {integrity: sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.0': - resolution: {integrity: sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==} + '@typescript-eslint/tsconfig-utils@8.46.1': + resolution: {integrity: sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.0': - resolution: {integrity: sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==} + '@typescript-eslint/type-utils@8.46.1': + resolution: {integrity: sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.46.0': - resolution: {integrity: sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==} + '@typescript-eslint/types@8.46.1': + resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.46.0': - resolution: {integrity: sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==} + '@typescript-eslint/typescript-estree@8.46.1': + resolution: {integrity: sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.0': - resolution: {integrity: sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==} + '@typescript-eslint/utils@8.46.1': + resolution: {integrity: sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.46.0': - resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} + '@typescript-eslint/visitor-keys@8.46.1': + resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -5210,9 +5210,9 @@ packages: resolution: {integrity: sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==} hasBin: true - batch-cluster@13.0.0: - resolution: {integrity: sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==} - engines: {node: '>=14'} + batch-cluster@15.0.1: + resolution: {integrity: sha512-eUmh0ld1AUPKTEmdzwGF9QTSexXAyt9rA1F5zDfW1wUi3okA3Tal4NLdCeFI6aiKpBenQhR6NmK9bW9tBHTGPQ==} + engines: {node: '>=20'} batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} @@ -6095,8 +6095,8 @@ packages: engines: {node: '>= 4.0.0'} hasBin: true - devalue@5.3.2: - resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + devalue@5.4.1: + resolution: {integrity: sha512-YtoaOfsqjbZQKGIMRYDWKjUmSB4VJ/RElB+bXZawQAQYAo4xu08GKTMVlsZDTF6R2MbAgjcAQRPI5eIyRAT2OQ==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -6337,8 +6337,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.10: - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} hasBin: true @@ -6547,23 +6547,25 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exiftool-vendored.exe@13.0.0: - resolution: {integrity: sha512-4zAMuFGgxZkOoyQIzZMHv1HlvgyJK3AkNqjAgm8A8V0UmOZO7yv3pH49cDV1OduzFJqgs6yQ6eG4OGydhKtxlg==} + exiftool-vendored.exe@13.38.0: + resolution: {integrity: sha512-oZx5enTAvSiIAXL+OEk7nNWrfUhEdKUpaGwDjCmz4VKwOa4HbisqyM808xPGPYj8X7XikcME/fq5hvevPeE3cw==} os: [win32] - exiftool-vendored.pl@13.0.1: - resolution: {integrity: sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==} + exiftool-vendored.pl@13.38.0: + resolution: {integrity: sha512-Q3xl1nnwswrsR5344z4NyqvI74fKwla+VJHY1N+32gcDgt8cs9KBsDUwcNzKHSOSa/MjEfniuCJVrQiqR05iag==} os: ['!win32'] + hasBin: true - exiftool-vendored@28.8.0: - resolution: {integrity: sha512-R7tirJLr9fWuH9JS/KFFLB+O7jNGKuPXGxREc6YybYangEudGb+X8ERsYXk9AifMiAWh/2agNfbgkbcQcF+MxA==} + exiftool-vendored@31.1.0: + resolution: {integrity: sha512-q8StxLawHLDvhqv/uoBYCfVbDskn49Cr5ouNCZhh4lgryGu1aymHwK9AvO6RcW2SbPm5MSnQDJOfGp2MW5Nnrw==} + engines: {node: '>=20.0.0'} expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} - exponential-backoff@3.1.2: - resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} express@4.21.2: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} @@ -7294,14 +7296,14 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - intl-messageformat@10.7.17: - resolution: {integrity: sha512-0Ugaf65B2J76rb31drgNF1l6bGEDkbIiYc2Glx6jaZINHnwa5kDRGy8KXYuA+/8P4G0c9prAFhfVhQJJfzUuvQ==} + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ioredis@5.8.1: - resolution: {integrity: sha512-Qho8TgIamqEPdgiMadJwzRMW3TudIg6vpg4YONokGDudy4eqRIJtDbVX72pfLBcWxvbn3qm/40TyGUObdW4tLQ==} + ioredis@5.8.2: + resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==} engines: {node: '>=12.22.0'} ip-address@10.0.1: @@ -7933,8 +7935,8 @@ packages: resolution: {integrity: sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==} engines: {node: '>=6.4.0'} - maplibre-gl@5.8.0: - resolution: {integrity: sha512-zLblPFK+z5sxeitDF8RL2cnqfRaivNwxbGoQMfwAm9st6d1lRGTxgI7NNNr/U1AEPkp5+X+wjROagiHvJD8aqg==} + maplibre-gl@5.9.0: + resolution: {integrity: sha512-YxW9glb/YrDXGDhqy1u+aG113+L86ttAUpTd6sCkGHyUKMXOX8qbGHJQVqxOczy+4CtRKnqcCfSura2MzB0nQA==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} mark.js@8.11.1: @@ -8435,8 +8437,8 @@ packages: reflect-metadata: '*' rxjs: '>= 7' - nestjs-kysely@3.0.0: - resolution: {integrity: sha512-YA6tHBgXQYPNpMBPII2OvUOiaWjCCoh5pP5dUHirQcMUHxNFzInBL6MDk8y74rk2z/5IvAK9AUlsdPyJtToO6g==} + nestjs-kysely@3.1.2: + resolution: {integrity: sha512-m7gK37oza6yyRmOs6VWQ6Ri5wgqUnmWpMUByd1V8WLZcYCeVPgXGxzw3ABRWUIX/1kr+nZUhaAZRgAIhk2JXoQ==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -8494,8 +8496,8 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-gyp@11.4.2: - resolution: {integrity: sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==} + node-gyp@11.5.0: + resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} engines: {node: ^18.17.0 || >=20.5.0} hasBin: true @@ -9867,8 +9869,8 @@ packages: rollup: optional: true - rollup@4.52.4: - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -10395,8 +10397,8 @@ packages: peerDependencies: svelte: ^3 || ^4 || ^5 - svelte-maplibre@1.2.1: - resolution: {integrity: sha512-IVkbc54hQXznyaiFN69RIdjqbLHriNYPVEo1DQMtWSm1kLovrt/aZuhV4eOoZKn6wIvY2Vz34jXPS33f/d/GNw==} + svelte-maplibre@1.2.3: + resolution: {integrity: sha512-2EToGWdSlTq9Tr7MLmUlve3J86uDM9D6s5ErY/oc5LEsktd0TCTPXM1HJ1IGSaa+ElxCv/ka/igvGPb6L4BhLw==} peerDependencies: '@deck.gl/core': ^9 '@deck.gl/layers': ^9 @@ -10427,8 +10429,8 @@ packages: peerDependencies: svelte: ^5.30.2 - svelte@5.39.11: - resolution: {integrity: sha512-8MxWVm2+3YwrFbPaxOlT1bbMi6OTenrAgks6soZfiaS8Fptk4EVyRIFhJc3RpO264EeSNwgjWAdki0ufg4zkGw==} + svelte@5.40.1: + resolution: {integrity: sha512-0R3t2oiLxJNJb2buz61MNfPdkjeyj2qTCM7TtIv/4ZfF12zD7Ig8iIo+C8febroy+9S4QJ7qfijtearSdO/1ww==} engines: {node: '>=18'} svg-parser@2.0.4: @@ -10439,8 +10441,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - swagger-ui-dist@5.21.0: - resolution: {integrity: sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==} + swagger-ui-dist@5.29.4: + resolution: {integrity: sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==} symbol-observable@4.0.0: resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} @@ -10475,20 +10477,20 @@ packages: tailwind-merge: optional: true - tailwindcss-email-variants@3.0.4: - resolution: {integrity: sha512-ohtLSifyWQDAtddJnfbcxkIDCIyXp6Yb83hXRprrS+/2dSyme4OlUZAP+TDwQc0K8D0LAw80eKI6psgejxys8A==} + tailwindcss-email-variants@3.0.5: + resolution: {integrity: sha512-BbULkY0/0VLXwSzAZP0wxyO0siXJARatreZTSW+U6dM+LuTzPsfG/SfxxuouINM1xtIoPGGU++jKkMyfYKJe7Q==} engines: {node: '>=18'} peerDependencies: tailwindcss: '>=3.4.0 < 4' - tailwindcss-mso@2.0.2: - resolution: {integrity: sha512-GaR8RW/Kan+YWEQ9Y9Ah6AYy7R2wEQ3X++YK4ffJVWycCTd6ryMLezqmyhi7KWHqsgQOb4nhjJYayI+JF44BXw==} + tailwindcss-mso@2.0.3: + resolution: {integrity: sha512-on7ooPmtWoR+wgvvDmu3Q1SYx13PQghjo0vNuSwCLgW0cgGiQcmMYRl+zDX7IhM/lt8BOoKY7VFAE2VIDkXD3w==} engines: {node: '>=18.20'} peerDependencies: tailwindcss: '>=3.4.0 < 4' - tailwindcss-preset-email@1.4.0: - resolution: {integrity: sha512-UgvLHT5UsPEEXjto1WlR1wYXmYKeMaS2OPTJQqyufsU12os/EjBpeygEjTdrId7U2/mwDF4grlgo81qlzYSByg==} + tailwindcss-preset-email@1.4.1: + resolution: {integrity: sha512-mNzmFti3hRKTDpbQEdymegxB+WMiACYdHcvmLN5gfIFqJRLhkO4ffT9mqFM5zVQehpxS3dMVczwBKvDT0xpTQA==} peerDependencies: tailwindcss: '>=3.4.17' @@ -10770,8 +10772,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.46.0: - resolution: {integrity: sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==} + typescript-eslint@8.46.1: + resolution: {integrity: sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -10790,8 +10792,8 @@ packages: ua-is-frozen@0.1.2: resolution: {integrity: sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==} - ua-parser-js@2.0.5: - resolution: {integrity: sha512-sZErtx3rhpvZQanWW5umau4o/snfoLqRcQwQIZ54377WtRzIecnIKvjpkd5JwPcSUMglGnbIgcsQBGAbdi3S9Q==} + ua-parser-js@2.0.6: + resolution: {integrity: sha512-EmaxXfltJaDW75SokrY4/lXMrVyXomE/0FpIIqP2Ctic93gK7rlme55Cwkz8l3YZ6gqf94fCU7AnIkidd/KXPg==} hasBin: true uglify-js@3.19.3: @@ -11025,8 +11027,8 @@ packages: vite: optional: true - vite@7.1.9: - resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} + vite@7.1.11: + resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -11590,11 +11592,11 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.10)(chokidar@4.0.3)': + '@angular-devkit/schematics-cli@19.2.15(@types/node@22.18.12)(chokidar@4.0.3)': dependencies: '@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.18.10) + '@inquirer/prompts': 7.3.2(@types/node@22.18.12) ansi-colors: 4.1.3 symbol-observable: 4.0.0 yargs-parser: 21.1.1 @@ -13807,148 +13809,148 @@ snapshots: '@esbuild/aix-ppc64@0.19.12': optional: true - '@esbuild/aix-ppc64@0.25.10': + '@esbuild/aix-ppc64@0.25.11': optional: true '@esbuild/android-arm64@0.19.12': optional: true - '@esbuild/android-arm64@0.25.10': + '@esbuild/android-arm64@0.25.11': optional: true '@esbuild/android-arm@0.19.12': optional: true - '@esbuild/android-arm@0.25.10': + '@esbuild/android-arm@0.25.11': optional: true '@esbuild/android-x64@0.19.12': optional: true - '@esbuild/android-x64@0.25.10': + '@esbuild/android-x64@0.25.11': optional: true '@esbuild/darwin-arm64@0.19.12': optional: true - '@esbuild/darwin-arm64@0.25.10': + '@esbuild/darwin-arm64@0.25.11': optional: true '@esbuild/darwin-x64@0.19.12': optional: true - '@esbuild/darwin-x64@0.25.10': + '@esbuild/darwin-x64@0.25.11': optional: true '@esbuild/freebsd-arm64@0.19.12': optional: true - '@esbuild/freebsd-arm64@0.25.10': + '@esbuild/freebsd-arm64@0.25.11': optional: true '@esbuild/freebsd-x64@0.19.12': optional: true - '@esbuild/freebsd-x64@0.25.10': + '@esbuild/freebsd-x64@0.25.11': optional: true '@esbuild/linux-arm64@0.19.12': optional: true - '@esbuild/linux-arm64@0.25.10': + '@esbuild/linux-arm64@0.25.11': optional: true '@esbuild/linux-arm@0.19.12': optional: true - '@esbuild/linux-arm@0.25.10': + '@esbuild/linux-arm@0.25.11': optional: true '@esbuild/linux-ia32@0.19.12': optional: true - '@esbuild/linux-ia32@0.25.10': + '@esbuild/linux-ia32@0.25.11': optional: true '@esbuild/linux-loong64@0.19.12': optional: true - '@esbuild/linux-loong64@0.25.10': + '@esbuild/linux-loong64@0.25.11': optional: true '@esbuild/linux-mips64el@0.19.12': optional: true - '@esbuild/linux-mips64el@0.25.10': + '@esbuild/linux-mips64el@0.25.11': optional: true '@esbuild/linux-ppc64@0.19.12': optional: true - '@esbuild/linux-ppc64@0.25.10': + '@esbuild/linux-ppc64@0.25.11': optional: true '@esbuild/linux-riscv64@0.19.12': optional: true - '@esbuild/linux-riscv64@0.25.10': + '@esbuild/linux-riscv64@0.25.11': optional: true '@esbuild/linux-s390x@0.19.12': optional: true - '@esbuild/linux-s390x@0.25.10': + '@esbuild/linux-s390x@0.25.11': optional: true '@esbuild/linux-x64@0.19.12': optional: true - '@esbuild/linux-x64@0.25.10': + '@esbuild/linux-x64@0.25.11': optional: true - '@esbuild/netbsd-arm64@0.25.10': + '@esbuild/netbsd-arm64@0.25.11': optional: true '@esbuild/netbsd-x64@0.19.12': optional: true - '@esbuild/netbsd-x64@0.25.10': + '@esbuild/netbsd-x64@0.25.11': optional: true - '@esbuild/openbsd-arm64@0.25.10': + '@esbuild/openbsd-arm64@0.25.11': optional: true '@esbuild/openbsd-x64@0.19.12': optional: true - '@esbuild/openbsd-x64@0.25.10': + '@esbuild/openbsd-x64@0.25.11': optional: true - '@esbuild/openharmony-arm64@0.25.10': + '@esbuild/openharmony-arm64@0.25.11': optional: true '@esbuild/sunos-x64@0.19.12': optional: true - '@esbuild/sunos-x64@0.25.10': + '@esbuild/sunos-x64@0.25.11': optional: true '@esbuild/win32-arm64@0.19.12': optional: true - '@esbuild/win32-arm64@0.25.10': + '@esbuild/win32-arm64@0.25.11': optional: true '@esbuild/win32-ia32@0.19.12': optional: true - '@esbuild/win32-ia32@0.25.10': + '@esbuild/win32-ia32@0.25.11': optional: true '@esbuild/win32-x64@0.19.12': optional: true - '@esbuild/win32-x64@0.25.10': + '@esbuild/win32-x64@0.25.11': optional: true '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0(jiti@2.6.1))': @@ -14006,7 +14008,7 @@ snapshots: '@eslint/core': 0.16.0 levn: 0.4.1 - '@faker-js/faker@10.0.0': {} + '@faker-js/faker@10.1.0': {} '@fig/complete-commander@3.2.0(commander@11.1.0)': dependencies: @@ -14024,7 +14026,7 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@formatjs/ecma402-abstract@2.3.5': + '@formatjs/ecma402-abstract@2.3.6': dependencies: '@formatjs/fast-memoize': 2.2.7 '@formatjs/intl-localematcher': 0.6.2 @@ -14035,15 +14037,15 @@ snapshots: dependencies: tslib: 2.8.1 - '@formatjs/icu-messageformat-parser@2.11.3': + '@formatjs/icu-messageformat-parser@2.11.4': dependencies: - '@formatjs/ecma402-abstract': 2.3.5 - '@formatjs/icu-skeleton-parser': 1.8.15 + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 tslib: 2.8.1 - '@formatjs/icu-skeleton-parser@1.8.15': + '@formatjs/icu-skeleton-parser@1.8.16': dependencies: - '@formatjs/ecma402-abstract': 2.3.5 + '@formatjs/ecma402-abstract': 2.3.6 tslib: 2.8.1 '@formatjs/intl-localematcher@0.6.2': @@ -14180,13 +14182,13 @@ snapshots: '@img/sharp-win32-x64@0.34.4': optional: true - '@immich/ui@0.34.2(@internationalized/date@3.8.2)(svelte@5.39.11)': + '@immich/ui@0.37.1(@internationalized/date@3.8.2)(svelte@5.40.1)': dependencies: '@mdi/js': 7.4.47 - bits-ui: 2.9.8(@internationalized/date@3.8.2)(svelte@5.39.11) + bits-ui: 2.9.8(@internationalized/date@3.8.2)(svelte@5.40.1) luxon: 3.7.2 simple-icons: 15.16.1 - svelte: 5.39.11 + svelte: 5.40.1 svelte-highlight: 7.8.4 tailwind-merge: 3.3.1 tailwind-variants: 3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.14) @@ -14194,27 +14196,27 @@ snapshots: transitivePeerDependencies: - '@internationalized/date' - '@inquirer/checkbox@4.2.1(@types/node@22.18.10)': + '@inquirer/checkbox@4.2.1(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.12) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/confirm@5.1.15(@types/node@22.18.10)': + '@inquirer/confirm@5.1.15(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/core@10.1.15(@types/node@22.18.10)': + '@inquirer/core@10.1.15(@types/node@22.18.12)': dependencies: '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.12) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -14222,115 +14224,115 @@ snapshots: wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/editor@4.2.17(@types/node@22.18.10)': + '@inquirer/editor@4.2.17(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/external-editor': 1.0.2(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/expand@4.0.17(@types/node@22.18.10)': + '@inquirer/expand@4.0.17(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/external-editor@1.0.2(@types/node@22.18.10)': + '@inquirer/external-editor@1.0.2(@types/node@22.18.12)': dependencies: chardet: 2.1.0 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@inquirer/figures@1.0.13': {} - '@inquirer/input@4.2.1(@types/node@22.18.10)': + '@inquirer/input@4.2.1(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/number@3.0.17(@types/node@22.18.10)': + '@inquirer/number@3.0.17(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/password@4.0.17(@types/node@22.18.10)': + '@inquirer/password@4.0.17(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) ansi-escapes: 4.3.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/prompts@7.3.2(@types/node@22.18.10)': + '@inquirer/prompts@7.3.2(@types/node@22.18.12)': dependencies: - '@inquirer/checkbox': 4.2.1(@types/node@22.18.10) - '@inquirer/confirm': 5.1.15(@types/node@22.18.10) - '@inquirer/editor': 4.2.17(@types/node@22.18.10) - '@inquirer/expand': 4.0.17(@types/node@22.18.10) - '@inquirer/input': 4.2.1(@types/node@22.18.10) - '@inquirer/number': 3.0.17(@types/node@22.18.10) - '@inquirer/password': 4.0.17(@types/node@22.18.10) - '@inquirer/rawlist': 4.1.5(@types/node@22.18.10) - '@inquirer/search': 3.1.0(@types/node@22.18.10) - '@inquirer/select': 4.3.1(@types/node@22.18.10) + '@inquirer/checkbox': 4.2.1(@types/node@22.18.12) + '@inquirer/confirm': 5.1.15(@types/node@22.18.12) + '@inquirer/editor': 4.2.17(@types/node@22.18.12) + '@inquirer/expand': 4.0.17(@types/node@22.18.12) + '@inquirer/input': 4.2.1(@types/node@22.18.12) + '@inquirer/number': 3.0.17(@types/node@22.18.12) + '@inquirer/password': 4.0.17(@types/node@22.18.12) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.12) + '@inquirer/search': 3.1.0(@types/node@22.18.12) + '@inquirer/select': 4.3.1(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/prompts@7.8.0(@types/node@22.18.10)': + '@inquirer/prompts@7.8.0(@types/node@22.18.12)': dependencies: - '@inquirer/checkbox': 4.2.1(@types/node@22.18.10) - '@inquirer/confirm': 5.1.15(@types/node@22.18.10) - '@inquirer/editor': 4.2.17(@types/node@22.18.10) - '@inquirer/expand': 4.0.17(@types/node@22.18.10) - '@inquirer/input': 4.2.1(@types/node@22.18.10) - '@inquirer/number': 3.0.17(@types/node@22.18.10) - '@inquirer/password': 4.0.17(@types/node@22.18.10) - '@inquirer/rawlist': 4.1.5(@types/node@22.18.10) - '@inquirer/search': 3.1.0(@types/node@22.18.10) - '@inquirer/select': 4.3.1(@types/node@22.18.10) + '@inquirer/checkbox': 4.2.1(@types/node@22.18.12) + '@inquirer/confirm': 5.1.15(@types/node@22.18.12) + '@inquirer/editor': 4.2.17(@types/node@22.18.12) + '@inquirer/expand': 4.0.17(@types/node@22.18.12) + '@inquirer/input': 4.2.1(@types/node@22.18.12) + '@inquirer/number': 3.0.17(@types/node@22.18.12) + '@inquirer/password': 4.0.17(@types/node@22.18.12) + '@inquirer/rawlist': 4.1.5(@types/node@22.18.12) + '@inquirer/search': 3.1.0(@types/node@22.18.12) + '@inquirer/select': 4.3.1(@types/node@22.18.12) optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/rawlist@4.1.5(@types/node@22.18.10)': + '@inquirer/rawlist@4.1.5(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) + '@inquirer/type': 3.0.8(@types/node@22.18.12) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/search@3.1.0(@types/node@22.18.10)': + '@inquirer/search@3.1.0(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.12) yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/select@4.3.1(@types/node@22.18.10)': + '@inquirer/select@4.3.1(@types/node@22.18.12)': dependencies: - '@inquirer/core': 10.1.15(@types/node@22.18.10) + '@inquirer/core': 10.1.15(@types/node@22.18.12) '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8(@types/node@22.18.10) + '@inquirer/type': 3.0.8(@types/node@22.18.12) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@inquirer/type@3.0.8(@types/node@22.18.10)': + '@inquirer/type@3.0.8(@types/node@22.18.12)': optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@internationalized/date@3.8.2': dependencies: @@ -14368,7 +14370,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -14449,8 +14451,8 @@ snapshots: '@koddsson/eslint-plugin-tscompat@0.2.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@mdn/browser-compat-data': 6.0.27 - '@typescript-eslint/type-utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) browserslist: 4.26.3 transitivePeerDependencies: - eslint @@ -14533,7 +14535,7 @@ snapshots: '@mapbox/whoots-js@3.1.0': {} - '@maplibre/maplibre-gl-style-spec@24.2.0': + '@maplibre/maplibre-gl-style-spec@24.3.0': dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/unitbezier': 0.0.1 @@ -14621,27 +14623,27 @@ snapshots: '@namnode/store@0.1.0': {} - '@nestjs/bull-shared@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': + '@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)': dependencies: '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) tslib: 2.8.1 - '@nestjs/bullmq@11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0)': + '@nestjs/bullmq@11.0.4(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(bullmq@5.61.0)': dependencies: - '@nestjs/bull-shared': 11.0.3(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) + '@nestjs/bull-shared': 11.0.4(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) bullmq: 5.61.0 tslib: 2.8.1 - '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.10)': + '@nestjs/cli@11.0.10(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.18.12)': dependencies: '@angular-devkit/core': 19.2.15(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.15(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.10)(chokidar@4.0.3) - '@inquirer/prompts': 7.8.0(@types/node@22.18.10) - '@nestjs/schematics': 11.0.8(chokidar@4.0.3)(typescript@5.8.3) + '@angular-devkit/schematics-cli': 19.2.15(@types/node@22.18.12)(chokidar@4.0.3) + '@inquirer/prompts': 7.8.0(@types/node@22.18.12) + '@nestjs/schematics': 11.0.9(chokidar@4.0.3)(typescript@5.8.3) ansis: 4.1.0 chokidar: 4.0.3 cli-table3: 0.6.5 @@ -14732,7 +14734,7 @@ snapshots: '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) cron: 4.3.3 - '@nestjs/schematics@11.0.8(chokidar@4.0.3)(typescript@5.8.3)': + '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.8.3)': dependencies: '@angular-devkit/core': 19.2.17(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) @@ -14743,7 +14745,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/schematics@11.0.8(chokidar@4.0.3)(typescript@5.9.3)': + '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': dependencies: '@angular-devkit/core': 19.2.17(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) @@ -14754,7 +14756,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@11.2.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)': + '@nestjs/swagger@11.2.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)': dependencies: '@microsoft/tsdoc': 0.15.1 '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -14762,9 +14764,9 @@ snapshots: '@nestjs/mapped-types': 2.1.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2) js-yaml: 4.1.0 lodash: 4.17.21 - path-to-regexp: 8.2.0 + path-to-regexp: 8.3.0 reflect-metadata: 0.2.2 - swagger-ui-dist: 5.21.0 + swagger-ui-dist: 5.29.4 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.14.2 @@ -15128,7 +15130,7 @@ snapshots: '@photo-sphere-viewer/core': 5.14.0 three: 0.180.0 - '@photostructure/tz-lookup@11.2.0': {} + '@photostructure/tz-lookup@11.2.1': {} '@pkgjs/parseargs@0.11.0': optional: true @@ -15288,78 +15290,78 @@ snapshots: dependencies: react: 19.2.0 - '@rollup/pluginutils@5.3.0(rollup@4.52.4)': + '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.4 + rollup: 4.52.5 - '@rollup/rollup-android-arm-eabi@4.52.4': + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.52.4': + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.52.4': + '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.52.4': + '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.52.4': + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.52.4': + '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.4': + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.4': + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.4': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.4': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.4': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.4': + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.4': + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.4': + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.4': + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.52.4': + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-openharmony-arm64@4.52.4': + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.4': + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.4': + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.4': + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.4': + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true '@scarf/scarf@1.4.0': {} @@ -15689,33 +15691,33 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.47.0(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))': dependencies: - '@sveltejs/kit': 2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/kit': 2.47.0(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) - '@sveltejs/enhanced-img@0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.4)(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/enhanced-img@0.8.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(rollup@4.52.5)(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) magic-string: 0.30.19 sharp: 0.34.4 - svelte: 5.39.11 - svelte-parse-markup: 0.1.5(svelte@5.39.11) - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-imagetools: 8.0.0(rollup@4.52.4) + svelte: 5.40.1 + svelte-parse-markup: 0.1.5(svelte@5.40.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-imagetools: 8.0.0(rollup@4.52.5) zimmerframe: 1.1.4 transitivePeerDependencies: - rollup - supports-color - '@sveltejs/kit@2.46.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/kit@2.47.0(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.3.2 + devalue: 5.4.1 esm-env: 1.2.2 kleur: 4.1.5 magic-string: 0.30.19 @@ -15723,29 +15725,29 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.1 sirv: 3.0.2 - svelte: 5.39.11 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + svelte: 5.40.1 + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) optionalDependencies: '@opentelemetry/api': 1.9.0 - '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.3 - svelte: 5.39.11 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + svelte: 5.40.1 + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)))(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.19 - svelte: 5.39.11 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitefu: 1.1.1(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + svelte: 5.40.1 + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitefu: 1.1.1(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) transitivePeerDependencies: - supports-color @@ -15967,12 +15969,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14 '@tailwindcss/oxide-win32-x64-msvc': 4.1.14 - '@tailwindcss/vite@4.1.14(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.14(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@tailwindcss/node': 4.1.14 '@tailwindcss/oxide': 4.1.14 tailwindcss: 4.1.14 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/dom@10.4.0': dependencies: @@ -15994,12 +15996,12 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/svelte@5.2.8(svelte@5.39.11)(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@testing-library/svelte@5.2.8(svelte@5.40.1)(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@testing-library/dom': 10.4.0 - svelte: 5.39.11 + svelte: 5.40.1 optionalDependencies: - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': @@ -16042,7 +16044,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/archiver@6.0.3': dependencies: @@ -16054,16 +16056,16 @@ snapshots: '@types/bcrypt@6.0.0': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/braces@3.0.5': {} @@ -16084,21 +16086,21 @@ snapshots: '@types/cli-progress@3.11.6': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/compression@1.8.1': dependencies: '@types/express': 5.0.3 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 5.0.6 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/connect@3.4.38': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/content-disposition@0.5.9': {} @@ -16115,11 +16117,11 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 5.0.3 '@types/keygrip': 1.0.6 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/cors@2.8.19': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/debug@4.1.12': dependencies: @@ -16129,13 +16131,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ssh2': 1.15.5 '@types/dockerode@3.3.44': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ssh2': 1.15.5 '@types/dom-to-image@2.6.7': {} @@ -16158,14 +16160,14 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -16191,7 +16193,7 @@ snapshots: '@types/fluent-ffmpeg@2.1.27': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/geojson-vt@3.2.5': dependencies: @@ -16223,7 +16225,7 @@ snapshots: '@types/http-proxy@1.17.16': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/inquirer@8.2.11': dependencies: @@ -16261,9 +16263,9 @@ snapshots: '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 22.18.10 + '@types/node': 22.18.12 - '@types/leaflet@1.9.20': + '@types/leaflet@1.9.21': dependencies: '@types/geojson': 7946.0.16 @@ -16291,7 +16293,7 @@ snapshots: '@types/mock-fs@4.13.4': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ms@2.1.0': {} @@ -16301,7 +16303,7 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/node@17.0.45': {} @@ -16313,7 +16315,7 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@22.18.10': + '@types/node@22.18.12': dependencies: undici-types: 6.21.0 @@ -16325,7 +16327,7 @@ snapshots: '@types/nodemailer@7.0.2': dependencies: '@aws-sdk/client-sesv2': 3.907.0 - '@types/node': 22.18.10 + '@types/node': 22.18.12 transitivePeerDependencies: - aws-crt @@ -16333,7 +16335,7 @@ snapshots: dependencies: '@types/keygrip': 1.0.6 '@types/koa': 3.0.0 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/parse5@5.0.3': {} @@ -16343,7 +16345,7 @@ snapshots: '@types/pg@8.15.5': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 pg-protocol: 1.10.3 pg-types: 2.2.0 @@ -16351,13 +16353,13 @@ snapshots: '@types/pngjs@6.0.5': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/prismjs@1.26.5': {} '@types/qrcode@1.5.5': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/qs@6.14.0': {} @@ -16386,7 +16388,7 @@ snapshots: '@types/readdir-glob@1.1.5': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/retry@0.12.2': {} @@ -16396,14 +16398,14 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/semver@7.7.1': {} '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/serve-index@1.9.4': dependencies: @@ -16412,20 +16414,20 @@ snapshots: '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/send': 0.17.5 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ssh2-streams@0.1.12': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ssh2@0.5.52': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ssh2-streams': 0.1.12 '@types/ssh2@1.15.5': @@ -16436,7 +16438,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.18.10 + '@types/node': 22.18.12 form-data: 4.0.4 '@types/supercluster@7.1.3': @@ -16450,7 +16452,7 @@ snapshots: '@types/through@0.0.33': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/ua-parser-js@0.7.39': {} @@ -16464,7 +16466,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 '@types/yargs-parser@21.0.3': {} @@ -16472,14 +16474,14 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.0 - '@typescript-eslint/type-utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.0 + '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.1 + '@typescript-eslint/type-utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.1 eslint: 9.37.0(jiti@2.6.1) graphemer: 1.4.0 ignore: 7.0.5 @@ -16489,41 +16491,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.0 - '@typescript-eslint/types': 8.46.0 - '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.0 + '@typescript-eslint/scope-manager': 8.46.1 + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.1 debug: 4.4.3 eslint: 9.37.0(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.46.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) - '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3) + '@typescript-eslint/types': 8.46.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.0': + '@typescript-eslint/scope-manager@8.46.1': dependencies: - '@typescript-eslint/types': 8.46.0 - '@typescript-eslint/visitor-keys': 8.46.0 + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/visitor-keys': 8.46.1 - '@typescript-eslint/tsconfig-utils@8.46.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.46.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.46.0 - '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.37.0(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) @@ -16531,14 +16533,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.0': {} + '@typescript-eslint/types@8.46.1': {} - '@typescript-eslint/typescript-estree@8.46.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.46.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.46.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.0(typescript@5.9.3) - '@typescript-eslint/types': 8.46.0 - '@typescript-eslint/visitor-keys': 8.46.0 + '@typescript-eslint/project-service': 8.46.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3) + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/visitor-keys': 8.46.1 debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -16549,25 +16551,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.46.0 - '@typescript-eslint/types': 8.46.0 - '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.1 + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3) eslint: 9.37.0(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.46.0': + '@typescript-eslint/visitor-keys@8.46.1': dependencies: - '@typescript-eslint/types': 8.46.0 + '@typescript-eslint/types': 8.46.1 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -16582,7 +16584,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -16613,21 +16615,21 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -16739,10 +16741,10 @@ snapshots: dependencies: '@namnode/store': 0.1.0 - '@zoom-image/svelte@0.3.7(svelte@5.39.11)': + '@zoom-image/svelte@0.3.7(svelte@5.40.1)': dependencies: '@zoom-image/core': 0.41.3 - svelte: 5.39.11 + svelte: 5.40.1 abab@2.0.6: optional: true @@ -17069,7 +17071,7 @@ snapshots: baseline-browser-mapping@2.8.15: {} - batch-cluster@13.0.0: {} + batch-cluster@15.0.1: {} batch@0.6.1: {} @@ -17088,15 +17090,15 @@ snapshots: binary-extensions@2.3.0: {} - bits-ui@2.9.8(@internationalized/date@3.8.2)(svelte@5.39.11): + bits-ui@2.9.8(@internationalized/date@3.8.2)(svelte@5.40.1): dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/dom': 1.7.4 '@internationalized/date': 3.8.2 esm-env: 1.2.2 - runed: 0.29.2(svelte@5.39.11) - svelte: 5.39.11 - svelte-toolbelt: 0.9.3(svelte@5.39.11) + runed: 0.29.2(svelte@5.40.1) + svelte: 5.40.1 + svelte-toolbelt: 0.9.3(svelte@5.40.1) tabbable: 6.2.0 bl@4.1.0: @@ -17210,7 +17212,7 @@ snapshots: bullmq@5.61.0: dependencies: cron-parser: 4.9.0 - ioredis: 5.8.1 + ioredis: 5.8.2 msgpackr: 1.11.5 node-abort-controller: 3.1.1 semver: 7.7.3 @@ -17992,7 +17994,7 @@ snapshots: transitivePeerDependencies: - supports-color - devalue@5.3.2: {} + devalue@5.4.1: {} devlop@1.1.0: dependencies: @@ -18188,7 +18190,7 @@ snapshots: engine.io@6.6.4: dependencies: '@types/cors': 2.8.19 - '@types/node': 22.18.10 + '@types/node': 22.18.12 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -18302,34 +18304,34 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - esbuild@0.25.10: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.10 - '@esbuild/android-arm': 0.25.10 - '@esbuild/android-arm64': 0.25.10 - '@esbuild/android-x64': 0.25.10 - '@esbuild/darwin-arm64': 0.25.10 - '@esbuild/darwin-x64': 0.25.10 - '@esbuild/freebsd-arm64': 0.25.10 - '@esbuild/freebsd-x64': 0.25.10 - '@esbuild/linux-arm': 0.25.10 - '@esbuild/linux-arm64': 0.25.10 - '@esbuild/linux-ia32': 0.25.10 - '@esbuild/linux-loong64': 0.25.10 - '@esbuild/linux-mips64el': 0.25.10 - '@esbuild/linux-ppc64': 0.25.10 - '@esbuild/linux-riscv64': 0.25.10 - '@esbuild/linux-s390x': 0.25.10 - '@esbuild/linux-x64': 0.25.10 - '@esbuild/netbsd-arm64': 0.25.10 - '@esbuild/netbsd-x64': 0.25.10 - '@esbuild/openbsd-arm64': 0.25.10 - '@esbuild/openbsd-x64': 0.25.10 - '@esbuild/openharmony-arm64': 0.25.10 - '@esbuild/sunos-x64': 0.25.10 - '@esbuild/win32-arm64': 0.25.10 - '@esbuild/win32-ia32': 0.25.10 - '@esbuild/win32-x64': 0.25.10 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 escalade@3.2.0: {} @@ -18378,7 +18380,7 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 10.1.8(eslint@9.37.0(jiti@2.6.1)) - eslint-plugin-svelte@3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.39.11): + eslint-plugin-svelte@3.12.4(eslint@9.37.0(jiti@2.6.1))(svelte@5.40.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1)) '@jridgewell/sourcemap-codec': 1.5.5 @@ -18390,9 +18392,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.6) postcss-safe-parser: 7.0.1(postcss@8.5.6) semver: 7.7.3 - svelte-eslint-parser: 1.3.3(svelte@5.39.11) + svelte-eslint-parser: 1.3.3(svelte@5.40.1) optionalDependencies: - svelte: 5.39.11 + svelte: 5.40.1 transitivePeerDependencies: - ts-node @@ -18578,7 +18580,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 require-like: 0.1.2 event-emitter@0.3.5: @@ -18608,25 +18610,25 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exiftool-vendored.exe@13.0.0: + exiftool-vendored.exe@13.38.0: optional: true - exiftool-vendored.pl@13.0.1: {} + exiftool-vendored.pl@13.38.0: {} - exiftool-vendored@28.8.0: + exiftool-vendored@31.1.0: dependencies: - '@photostructure/tz-lookup': 11.2.0 + '@photostructure/tz-lookup': 11.2.1 '@types/luxon': 3.7.1 - batch-cluster: 13.0.0 - exiftool-vendored.pl: 13.0.1 + batch-cluster: 15.0.1 + exiftool-vendored.pl: 13.38.0 he: 1.2.0 luxon: 3.7.2 optionalDependencies: - exiftool-vendored.exe: 13.0.0 + exiftool-vendored.exe: 13.38.0 expect-type@1.2.1: {} - exponential-backoff@3.1.2: {} + exponential-backoff@3.1.3: {} express@4.21.2: dependencies: @@ -19565,9 +19567,9 @@ snapshots: inline-style-parser@0.2.4: {} - inquirer@8.2.7(@types/node@22.18.10): + inquirer@8.2.7(@types/node@22.18.12): dependencies: - '@inquirer/external-editor': 1.0.2(@types/node@22.18.10) + '@inquirer/external-editor': 1.0.2(@types/node@22.18.12) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 @@ -19587,18 +19589,18 @@ snapshots: internmap@2.0.3: {} - intl-messageformat@10.7.17: + intl-messageformat@10.7.18: dependencies: - '@formatjs/ecma402-abstract': 2.3.5 + '@formatjs/ecma402-abstract': 2.3.6 '@formatjs/fast-memoize': 2.2.7 - '@formatjs/icu-messageformat-parser': 2.11.3 + '@formatjs/icu-messageformat-parser': 2.11.4 tslib: 2.8.1 invariant@2.2.4: dependencies: loose-envify: 1.4.0 - ioredis@5.8.1: + ioredis@5.8.2: dependencies: '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 @@ -19781,7 +19783,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.18.10 + '@types/node': 22.18.12 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -19789,13 +19791,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -20241,7 +20243,7 @@ snapshots: tinyqueue: 2.0.3 vt-pbf: 3.1.3 - maplibre-gl@5.8.0: + maplibre-gl@5.9.0: dependencies: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/jsonlint-lines-primitives': 2.0.2 @@ -20250,7 +20252,7 @@ snapshots: '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 2.0.4 '@mapbox/whoots-js': 3.1.0 - '@maplibre/maplibre-gl-style-spec': 24.2.0 + '@maplibre/maplibre-gl-style-spec': 24.3.0 '@maplibre/vt-pbf': 4.0.3 '@types/geojson': 7946.0.16 '@types/geojson-vt': 3.2.5 @@ -21012,7 +21014,7 @@ snapshots: neo-async@2.6.2: {} - nest-commander@3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.10)(typescript@5.9.3): + nest-commander@3.20.1(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(@types/inquirer@8.2.11)(@types/node@22.18.12)(typescript@5.9.3): dependencies: '@fig/complete-commander': 3.2.0(commander@11.1.0) '@golevelup/nestjs-discovery': 5.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6) @@ -21021,7 +21023,7 @@ snapshots: '@types/inquirer': 8.2.11 commander: 11.1.0 cosmiconfig: 8.3.6(typescript@5.9.3) - inquirer: 8.2.7(@types/node@22.18.10) + inquirer: 8.2.7(@types/node@22.18.12) transitivePeerDependencies: - '@types/node' - typescript @@ -21033,7 +21035,7 @@ snapshots: reflect-metadata: 0.2.2 rxjs: 7.8.2 - nestjs-kysely@3.0.0(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2): + nestjs-kysely@3.1.2(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.6)(kysely@0.28.2)(reflect-metadata@0.2.2): dependencies: '@nestjs/common': 11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 11.1.6(@nestjs/common@11.1.6(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.6)(@nestjs/websockets@11.1.6)(reflect-metadata@0.2.2)(rxjs@7.8.2) @@ -21094,10 +21096,10 @@ snapshots: node-gyp-build@4.8.4: {} - node-gyp@11.4.2: + node-gyp@11.5.0: dependencies: env-paths: 2.2.1 - exponential-backoff: 3.1.2 + exponential-backoff: 3.1.3 graceful-fs: 4.2.11 make-fetch-happen: 14.0.3 nopt: 8.1.0 @@ -21516,7 +21518,7 @@ snapshots: pmtiles@3.2.1: dependencies: - '@types/leaflet': 1.9.20 + '@types/leaflet': 1.9.21 fflate: 0.8.2 pmtiles@4.3.0: @@ -22036,10 +22038,10 @@ snapshots: dependencies: prettier: 3.6.2 - prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.39.11): + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.40.1): dependencies: prettier: 3.6.2 - svelte: 5.39.11 + svelte: 5.40.1 prettier@3.6.2: {} @@ -22118,7 +22120,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.18.10 + '@types/node': 22.18.12 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -22233,7 +22235,7 @@ snapshots: chokidar: 4.0.3 commander: 13.1.0 debounce: 2.2.0 - esbuild: 0.25.10 + esbuild: 0.25.11 glob: 11.0.3 jiti: 2.4.2 log-symbols: 7.0.1 @@ -22580,41 +22582,41 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.4(rollup@4.52.4): + rollup-plugin-visualizer@6.0.4(rollup@4.52.5): dependencies: open: 8.4.2 picomatch: 4.0.3 source-map: 0.7.6 yargs: 17.7.2 optionalDependencies: - rollup: 4.52.4 + rollup: 4.52.5 - rollup@4.52.4: + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.4 - '@rollup/rollup-android-arm64': 4.52.4 - '@rollup/rollup-darwin-arm64': 4.52.4 - '@rollup/rollup-darwin-x64': 4.52.4 - '@rollup/rollup-freebsd-arm64': 4.52.4 - '@rollup/rollup-freebsd-x64': 4.52.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 - '@rollup/rollup-linux-arm64-musl': 4.52.4 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 - '@rollup/rollup-linux-x64-gnu': 4.52.4 - '@rollup/rollup-linux-x64-musl': 4.52.4 - '@rollup/rollup-openharmony-arm64': 4.52.4 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 - '@rollup/rollup-win32-x64-gnu': 4.52.4 - '@rollup/rollup-win32-x64-msvc': 4.52.4 + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 router@2.2.0: @@ -22645,10 +22647,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 - runed@0.29.2(svelte@5.39.11): + runed@0.29.2(svelte@5.40.1): dependencies: esm-env: 1.2.2 - svelte: 5.39.11 + svelte: 5.40.1 rw@1.3.3: {} @@ -22852,7 +22854,7 @@ snapshots: '@img/colour': 1.0.0 detect-libc: 2.1.2 node-addon-api: 8.5.0 - node-gyp: 11.4.2 + node-gyp: 11.5.0 semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.4 @@ -23262,19 +23264,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.39.11)(typescript@5.9.3): + svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.40.1)(typescript@5.9.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 chokidar: 4.0.3 fdir: 6.5.0(picomatch@4.0.3) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.39.11 + svelte: 5.40.1 typescript: 5.9.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.3.3(svelte@5.39.11): + svelte-eslint-parser@1.3.3(svelte@5.40.1): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -23283,7 +23285,7 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.39.11 + svelte: 5.40.1 svelte-gestures@5.2.2: {} @@ -23291,42 +23293,42 @@ snapshots: dependencies: highlight.js: 11.11.1 - svelte-i18n@4.0.1(svelte@5.39.11): + svelte-i18n@4.0.1(svelte@5.40.1): dependencies: cli-color: 2.0.4 deepmerge: 4.3.1 esbuild: 0.19.12 estree-walker: 2.0.2 - intl-messageformat: 10.7.17 + intl-messageformat: 10.7.18 sade: 1.8.1 - svelte: 5.39.11 + svelte: 5.40.1 tiny-glob: 0.2.9 - svelte-maplibre@1.2.1(svelte@5.39.11): + svelte-maplibre@1.2.3(svelte@5.40.1): dependencies: d3-geo: 3.1.1 dequal: 2.0.3 just-compare: 2.3.0 - maplibre-gl: 5.8.0 + maplibre-gl: 5.9.0 pmtiles: 3.2.1 - svelte: 5.39.11 + svelte: 5.40.1 - svelte-parse-markup@0.1.5(svelte@5.39.11): + svelte-parse-markup@0.1.5(svelte@5.40.1): dependencies: - svelte: 5.39.11 + svelte: 5.40.1 - svelte-persisted-store@0.12.0(svelte@5.39.11): + svelte-persisted-store@0.12.0(svelte@5.40.1): dependencies: - svelte: 5.39.11 + svelte: 5.40.1 - svelte-toolbelt@0.9.3(svelte@5.39.11): + svelte-toolbelt@0.9.3(svelte@5.40.1): dependencies: clsx: 2.1.1 - runed: 0.29.2(svelte@5.39.11) + runed: 0.29.2(svelte@5.40.1) style-to-object: 1.0.9 - svelte: 5.39.11 + svelte: 5.40.1 - svelte@5.39.11: + svelte@5.40.1: dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 @@ -23355,7 +23357,7 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 - swagger-ui-dist@5.21.0: + swagger-ui-dist@5.29.4: dependencies: '@scarf/scarf': 1.4.0 @@ -23380,19 +23382,19 @@ snapshots: optionalDependencies: tailwind-merge: 3.3.1 - tailwindcss-email-variants@3.0.4(tailwindcss@3.4.18(yaml@2.8.1)): + tailwindcss-email-variants@3.0.5(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: tailwindcss: 3.4.18(yaml@2.8.1) - tailwindcss-mso@2.0.2(tailwindcss@3.4.18(yaml@2.8.1)): + tailwindcss-mso@2.0.3(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: tailwindcss: 3.4.18(yaml@2.8.1) - tailwindcss-preset-email@1.4.0(tailwindcss@3.4.18(yaml@2.8.1)): + tailwindcss-preset-email@1.4.1(tailwindcss@3.4.18(yaml@2.8.1)): dependencies: tailwindcss: 3.4.18(yaml@2.8.1) - tailwindcss-email-variants: 3.0.4(tailwindcss@3.4.18(yaml@2.8.1)) - tailwindcss-mso: 2.0.2(tailwindcss@3.4.18(yaml@2.8.1)) + tailwindcss-email-variants: 3.0.5(tailwindcss@3.4.18(yaml@2.8.1)) + tailwindcss-mso: 2.0.3(tailwindcss@3.4.18(yaml@2.8.1)) tailwindcss@3.4.18(yaml@2.8.1): dependencies: @@ -23717,12 +23719,12 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.46.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3) eslint: 9.37.0(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -23734,12 +23736,11 @@ snapshots: ua-is-frozen@0.1.2: {} - ua-parser-js@2.0.5: + ua-parser-js@2.0.6: dependencies: detect-europe-js: 0.1.2 is-standalone-pwa: 0.1.1 ua-is-frozen: 0.1.2 - undici: 7.16.0 uglify-js@3.19.3: optional: true @@ -23861,9 +23862,9 @@ snapshots: unpipe@1.0.0: {} - unplugin-swc@1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.4): + unplugin-swc@1.5.7(@swc/core@1.13.5(@swc/helpers@0.5.17))(rollup@4.52.5): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) '@swc/core': 1.13.5(@swc/helpers@0.5.17) load-tsconfig: 0.2.5 unplugin: 2.3.10 @@ -23983,22 +23984,22 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-imagetools@8.0.0(rollup@4.52.4): + vite-imagetools@8.0.0(rollup@4.52.5): dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.4) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) imagetools-core: 8.0.0 sharp: 0.34.4 transitivePeerDependencies: - rollup - supports-color - vite-node@3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -24019,7 +24020,7 @@ snapshots: debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -24034,40 +24035,40 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: - vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.4 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 22.18.10 + '@types/node': 22.18.12 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.1 terser: 5.43.1 yaml: 2.8.1 - vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.4 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.7.2 @@ -24077,19 +24078,19 @@ snapshots: terser: 5.43.1 yaml: 2.8.1 - vitefu@1.1.1(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitefu@1.1.1(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): optionalDependencies: - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): + vitest-fetch-mock@0.4.5(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)): dependencies: - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2(encoding@0.1.13)))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24107,12 +24108,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.18.10 + '@types/node': 22.18.12 happy-dom: 20.0.2 jsdom: 26.1.0(canvas@2.11.2(encoding@0.1.13)) transitivePeerDependencies: @@ -24129,11 +24130,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.10)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.12)(happy-dom@20.0.2)(jiti@2.6.1)(jsdom@26.1.0(canvas@2.11.2))(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24151,12 +24152,12 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.9(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.10)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.18.10 + '@types/node': 22.18.12 happy-dom: 20.0.2 jsdom: 26.1.0(canvas@2.11.2) transitivePeerDependencies: @@ -24177,7 +24178,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -24195,7 +24196,7 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.9(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) + vite: 7.1.11(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) vite-node: 3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: diff --git a/server/Dockerfile.dev b/server/Dockerfile.dev index c37d9d3149..73afbe1a04 100644 --- a/server/Dockerfile.dev +++ b/server/Dockerfile.dev @@ -61,7 +61,7 @@ RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ # Flutter SDK # https://flutter.dev/docs/development/tools/sdk/releases?tab=linux ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.35.4" +ENV FLUTTER_VERSION="3.35.6" ENV FLUTTER_HOME=/flutter ENV PATH=${PATH}:${FLUTTER_HOME}/bin diff --git a/server/package.json b/server/package.json index 080452c615..694817591f 100644 --- a/server/package.json +++ b/server/package.json @@ -68,14 +68,14 @@ "cookie": "^1.0.2", "cookie-parser": "^1.4.7", "cron": "4.3.3", - "exiftool-vendored": "^28.8.0", + "exiftool-vendored": "^31.1.0", "express": "^5.1.0", "fast-glob": "^3.3.2", "fluent-ffmpeg": "^2.1.2", "geo-tz": "^8.0.0", "handlebars": "^4.7.8", "i18n-iso-countries": "^7.6.0", - "ioredis": "^5.3.2", + "ioredis": "^5.8.2", "js-yaml": "^4.1.0", "kysely": "0.28.2", "kysely-postgres-js": "^3.0.0", @@ -85,7 +85,7 @@ "multer": "^2.0.2", "nest-commander": "^3.16.0", "nestjs-cls": "^5.0.0", - "nestjs-kysely": "3.0.0", + "nestjs-kysely": "3.1.2", "nestjs-otel": "^7.0.0", "nodemailer": "^7.0.0", "openid-client": "^6.3.3", @@ -129,7 +129,7 @@ "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", "@types/multer": "^2.0.0", - "@types/node": "^22.18.8", + "@types/node": "^22.18.10", "@types/nodemailer": "^7.0.0", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", diff --git a/server/src/app.module.ts b/server/src/app.module.ts index a1cd1edfdf..8d261463e7 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -19,7 +19,6 @@ import { ConfigRepository } from 'src/repositories/config.repository'; import { EventRepository } from 'src/repositories/event.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { teardownTelemetry, TelemetryRepository } from 'src/repositories/telemetry.repository'; -import { UserRepository } from 'src/repositories/user.repository'; import { services } from 'src/services'; import { AuthService } from 'src/services/auth.service'; import { CliService } from 'src/services/cli.service'; @@ -56,7 +55,6 @@ class BaseModule implements OnModuleInit, OnModuleDestroy { private jobService: JobService, private telemetryRepository: TelemetryRepository, private authService: AuthService, - private userRepository: UserRepository, ) { logger.setAppName(this.worker); } diff --git a/server/src/controllers/user-admin.controller.ts b/server/src/controllers/user-admin.controller.ts index d50bd174ad..25a4691b75 100644 --- a/server/src/controllers/user-admin.controller.ts +++ b/server/src/controllers/user-admin.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, import { ApiTags } from '@nestjs/swagger'; import { AssetStatsDto, AssetStatsResponseDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SessionResponseDto } from 'src/dtos/session.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; import { UserAdminCreateDto, @@ -58,6 +59,12 @@ export class UserAdminController { return this.service.delete(auth, id, dto); } + @Get(':id/sessions') + @Authenticated({ permission: Permission.AdminSessionRead, admin: true }) + getUserSessionsAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { + return this.service.getSessions(auth, id); + } + @Get(':id/statistics') @Authenticated({ permission: Permission.AdminUserRead, admin: true }) getUserStatisticsAdmin( diff --git a/server/src/database.ts b/server/src/database.ts index f472c643ee..f60c2c228c 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -238,6 +238,7 @@ export type Session = { expiresAt: Date | null; deviceOS: string; deviceType: string; + appVersion: string | null; pinExpiresAt: Date | null; isPendingSyncReset: boolean; }; @@ -308,7 +309,7 @@ export const columns = { assetFiles: ['asset_file.id', 'asset_file.path', 'asset_file.type'], authUser: ['user.id', 'user.name', 'user.email', 'user.isAdmin', 'user.quotaUsageInBytes', 'user.quotaSizeInBytes'], authApiKey: ['api_key.id', 'api_key.permissions'], - authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt'], + authSession: ['session.id', 'session.updatedAt', 'session.pinExpiresAt', 'session.appVersion'], authSharedLink: [ 'shared_link.id', 'shared_link.userId', diff --git a/server/src/dtos/session.dto.ts b/server/src/dtos/session.dto.ts index 7ccc72a5f1..49351eda52 100644 --- a/server/src/dtos/session.dto.ts +++ b/server/src/dtos/session.dto.ts @@ -34,6 +34,7 @@ export class SessionResponseDto { current!: boolean; deviceType!: string; deviceOS!: string; + appVersion!: string | null; isPendingSyncReset!: boolean; } @@ -47,6 +48,7 @@ export const mapSession = (entity: Session, currentId?: string): SessionResponse updatedAt: entity.updatedAt.toISOString(), expiresAt: entity.expiresAt?.toISOString(), current: currentId === entity.id, + appVersion: entity.appVersion, deviceOS: entity.deviceOS, deviceType: entity.deviceType, isPendingSyncReset: entity.isPendingSyncReset, diff --git a/server/src/dtos/user.dto.ts b/server/src/dtos/user.dto.ts index 443178aa10..c5067f3e8d 100644 --- a/server/src/dtos/user.dto.ts +++ b/server/src/dtos/user.dto.ts @@ -173,6 +173,7 @@ export function mapUserAdmin(entity: UserAdmin): UserAdminResponseDto { const license = metadata.find( (item): item is UserMetadataItem => item.key === UserMetadataKey.License, )?.value; + return { ...mapUser(entity), storageLabel: entity.storageLabel, diff --git a/server/src/enum.ts b/server/src/enum.ts index b8e6e5209f..c056091f22 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -236,6 +236,8 @@ export enum Permission { AdminUserUpdate = 'adminUser.update', AdminUserDelete = 'adminUser.delete', + AdminSessionRead = 'adminSession.read', + AdminAuthUnlinkAll = 'adminAuth.unlinkAll', } diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 8af7bf7fb3..4964fefbbc 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -13,7 +13,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AuthService, LoginDetails } from 'src/services/auth.service'; -import { UAParser } from 'ua-parser-js'; +import { getUserAgentDetails } from 'src/utils/request'; type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; @@ -56,13 +56,14 @@ export const FileResponse = () => export const GetLoginDetails = createParamDecorator((data, context: ExecutionContext): LoginDetails => { const request = context.switchToHttp().getRequest(); - const userAgent = UAParser(request.headers['user-agent']); + const { deviceType, deviceOS, appVersion } = getUserAgentDetails(request.headers); return { clientIp: request.ip ?? '', isSecure: request.secure, - deviceType: userAgent.browser.name || userAgent.device.type || (request.headers.devicemodel as string) || '', - deviceOS: userAgent.os.name || (request.headers.devicetype as string) || '', + deviceType, + deviceOS, + appVersion, }; }); @@ -86,7 +87,6 @@ export class AuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const targets = [context.getHandler()]; - const options = this.reflector.getAllAndOverride(MetadataKey.AuthRoute, targets); if (!options) { return true; diff --git a/server/src/queries/session.repository.sql b/server/src/queries/session.repository.sql index 34d25cce8a..831a16342a 100644 --- a/server/src/queries/session.repository.sql +++ b/server/src/queries/session.repository.sql @@ -23,6 +23,7 @@ select "session"."id", "session"."updatedAt", "session"."pinExpiresAt", + "session"."appVersion", ( select to_json(obj) diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index ec4c8a8f52..420be0e1b4 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -17,7 +17,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { NotificationDto } from 'src/dtos/notification.dto'; import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { SyncAssetExifV1, SyncAssetV1 } from 'src/dtos/sync.dto'; -import { ImmichWorker, MetadataKey, QueueName } from 'src/enum'; +import { ImmichWorker, JobStatus, MetadataKey, QueueName, UserAvatarColor, UserStatus } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { JobItem, JobSource } from 'src/types'; @@ -66,8 +66,19 @@ type EventMap = { AssetDeleteAll: [{ assetIds: string[]; userId: string }]; AssetRestoreAll: [{ assetIds: string[]; userId: string }]; + /** a worker receives a job and emits this event to run it */ + JobRun: [QueueName, JobItem]; + /** job pre-hook */ JobStart: [QueueName, JobItem]; - JobFailed: [{ job: JobItem; error: Error | any }]; + /** job post-hook */ + JobComplete: [QueueName, JobItem]; + /** job finishes without error */ + JobSuccess: [JobSuccessEvent]; + /** job finishes with error */ + JobError: [JobErrorEvent]; + + // queue events + QueueStart: [QueueStartEvent]; // session events SessionDelete: [{ sessionId: string }]; @@ -82,11 +93,43 @@ type EventMap = { // user events UserSignup: [{ notify: boolean; id: string; password?: string }]; + UserCreate: [UserEvent]; + /** user is soft deleted */ + UserTrash: [UserEvent]; + /** user is permanently deleted */ + UserDelete: [UserEvent]; + UserRestore: [UserEvent]; // websocket events WebsocketConnect: [{ userId: string }]; }; +type JobSuccessEvent = { job: JobItem; response?: JobStatus }; +type JobErrorEvent = { job: JobItem; error: Error | any }; + +type QueueStartEvent = { + name: QueueName; +}; + +type UserEvent = { + name: string; + id: string; + createdAt: Date; + updatedAt: Date; + deletedAt: Date | null; + status: UserStatus; + email: string; + profileImagePath: string; + isAdmin: boolean; + shouldChangePassword: boolean; + avatarColor: UserAvatarColor | null; + oauthId: string; + storageLabel: string | null; + quotaSizeInBytes: number | null; + quotaUsageInBytes: number; + profileChangedAt: Date; +}; + export const serverEvents = ['ConfigUpdate'] as const; export type ServerEvents = (typeof serverEvents)[number]; diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index 5acd8d5746..cf2799a4cf 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -89,7 +89,7 @@ export class JobRepository { this.logger.debug(`Starting worker for queue: ${queueName}`); this.workers[queueName] = new Worker( queueName, - (job) => this.eventRepository.emit('JobStart', queueName, job as JobItem), + (job) => this.eventRepository.emit('JobRun', queueName, job as JobItem), { ...bull.config, concurrency: 1 }, ); } diff --git a/server/src/repositories/metadata.repository.ts b/server/src/repositories/metadata.repository.ts index e2360156e4..6073ddcb22 100644 --- a/server/src/repositories/metadata.repository.ts +++ b/server/src/repositories/metadata.repository.ts @@ -84,6 +84,7 @@ export class MetadataRepository { numericTags: [...DefaultReadTaskOptions.numericTags, 'FocalLength', 'FileSize'], /* eslint unicorn/no-array-callback-reference: off, unicorn/no-array-method-this-argument: off */ geoTz: (lat, lon) => geotz.find(lat, lon)[0], + geolocation: true, // Enable exiftool LFS to parse metadata for files larger than 2GB. readArgs: ['-api', 'largefilesupport=1'], writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'], diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 7d6b634845..c1bb9623eb 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import archiver from 'archiver'; import chokidar, { ChokidarOptions } from 'chokidar'; import { escapePath, glob, globStream } from 'fast-glob'; -import { constants, createReadStream, createWriteStream, existsSync, mkdirSync } from 'node:fs'; +import { constants, createReadStream, createWriteStream, existsSync, mkdirSync, ReadOptionsWithBuffer } from 'node:fs'; import fs from 'node:fs/promises'; import path from 'node:path'; import { Readable, Writable } from 'node:stream'; @@ -103,7 +103,7 @@ export class StorageRepository { }; } - async readFile(filepath: string, options?: fs.FileReadOptions): Promise { + async readFile(filepath: string, options?: ReadOptionsWithBuffer): Promise { const file = await fs.open(filepath); try { const { buffer } = await file.read(options); diff --git a/server/src/schema/migrations/1761078763279-AddAppVersionColumnToSession.ts b/server/src/schema/migrations/1761078763279-AddAppVersionColumnToSession.ts new file mode 100644 index 0000000000..8175788517 --- /dev/null +++ b/server/src/schema/migrations/1761078763279-AddAppVersionColumnToSession.ts @@ -0,0 +1,9 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "session" ADD "appVersion" character varying;`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "session" DROP COLUMN "appVersion";`.execute(db); +} diff --git a/server/src/schema/tables/session.table.ts b/server/src/schema/tables/session.table.ts index 706abdf887..466152d35d 100644 --- a/server/src/schema/tables/session.table.ts +++ b/server/src/schema/tables/session.table.ts @@ -42,6 +42,9 @@ export class SessionTable { @Column({ default: '' }) deviceOS!: Generated; + @Column({ nullable: true }) + appVersion!: string | null; + @UpdateIdColumn({ index: true }) updateId!: Generated; diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index d2b287cd5e..d8d7598593 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -41,6 +41,7 @@ const loginDetails = { clientIp: '127.0.0.1', deviceOS: '', deviceType: '', + appVersion: null, }; const fixtures = { @@ -243,6 +244,7 @@ describe(AuthService.name, () => { updatedAt: session.updatedAt, user: factory.authUser(), pinExpiresAt: null, + appVersion: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -408,6 +410,7 @@ describe(AuthService.name, () => { updatedAt: session.updatedAt, user: factory.authUser(), pinExpiresAt: null, + appVersion: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -435,6 +438,7 @@ describe(AuthService.name, () => { user: factory.authUser(), isPendingSyncReset: false, pinExpiresAt: null, + appVersion: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); @@ -456,6 +460,7 @@ describe(AuthService.name, () => { user: factory.authUser(), isPendingSyncReset: false, pinExpiresAt: null, + appVersion: null, }; mocks.session.getByToken.mockResolvedValue(sessionWithToken); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 535df779cd..d118f1809a 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -29,11 +29,13 @@ import { BaseService } from 'src/services/base.service'; import { isGranted } from 'src/utils/access'; import { HumanReadableSize } from 'src/utils/bytes'; import { mimeTypes } from 'src/utils/mime-types'; +import { getUserAgentDetails } from 'src/utils/request'; export interface LoginDetails { isSecure: boolean; clientIp: string; deviceType: string; deviceOS: string; + appVersion: string | null; } interface ClaimOptions { @@ -218,7 +220,7 @@ export class AuthService extends BaseService { } if (session) { - return this.validateSession(session); + return this.validateSession(session, headers); } if (apiKey) { @@ -463,15 +465,22 @@ export class AuthService extends BaseService { return this.cryptoRepository.compareBcrypt(inputSecret, existingHash); } - private async validateSession(tokenValue: string): Promise { + private async validateSession(tokenValue: string, headers: IncomingHttpHeaders): Promise { const hashedToken = this.cryptoRepository.hashSha256(tokenValue); const session = await this.sessionRepository.getByToken(hashedToken); if (session?.user) { + const { appVersion, deviceOS, deviceType } = getUserAgentDetails(headers); const now = DateTime.now(); const updatedAt = DateTime.fromJSDate(session.updatedAt); const diff = now.diff(updatedAt, ['hours']); - if (diff.hours > 1) { - await this.sessionRepository.update(session.id, { id: session.id, updatedAt: new Date() }); + if (diff.hours > 1 || appVersion != session.appVersion) { + await this.sessionRepository.update(session.id, { + id: session.id, + updatedAt: new Date(), + appVersion, + deviceOS, + deviceType, + }); } // Pin check @@ -529,6 +538,7 @@ export class AuthService extends BaseService { token: tokenHashed, deviceOS: loginDetails.deviceOS, deviceType: loginDetails.deviceType, + appVersion: loginDetails.appVersion, userId: user.id, }); diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index ad60e30425..8aa20aa868 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -209,6 +209,7 @@ describe(BackupService.name, () => { ${'15.3.3'} | ${15} ${'16.4.2'} | ${16} ${'17.15.1'} | ${17} + ${'18.0.0'} | ${18} `( `should use pg_dumpall $expectedVersion with postgres version $postgresVersion`, async ({ postgresVersion, expectedVersion }) => { @@ -224,7 +225,7 @@ describe(BackupService.name, () => { it.each` postgresVersion ${'13.99.99'} - ${'18.0.0'} + ${'19.0.0'} `(`should fail if postgres version $postgresVersion is not supported`, async ({ postgresVersion }) => { mocks.database.getPostgresVersion.mockResolvedValue(postgresVersion); const result = await sut.handleBackupDatabase(); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 3d99b6e522..6f8cc0e34a 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -103,7 +103,7 @@ export class BackupService extends BaseService { const databaseSemver = semver.coerce(databaseVersion); const databaseMajorVersion = databaseSemver?.major; - if (!databaseMajorVersion || !databaseSemver || !semver.satisfies(databaseSemver, '>=14.0.0 <18.0.0')) { + if (!databaseMajorVersion || !databaseSemver || !semver.satisfies(databaseSemver, '>=14.0.0 <19.0.0')) { this.logger.error(`Database Backup Failure: Unsupported PostgreSQL version: ${databaseVersion}`); return JobStatus.Failed; } diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 5a2dd42c3c..de5e5862c7 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -198,8 +198,8 @@ export class BaseService { } async createUser(dto: Insertable & { email: string }): Promise { - const user = await this.userRepository.getByEmail(dto.email); - if (user) { + const exists = await this.userRepository.getByEmail(dto.email); + if (exists) { throw new BadRequestException('User exists'); } @@ -218,7 +218,10 @@ export class BaseService { payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', '')); } - this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); - return this.userRepository.create(payload); + const user = await this.userRepository.create(payload); + + await this.eventRepository.emit('UserCreate', user); + + return user; } } diff --git a/server/src/services/index.ts b/server/src/services/index.ts index cad38ca1f4..d8af35e8dc 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -34,6 +34,7 @@ import { SyncService } from 'src/services/sync.service'; import { SystemConfigService } from 'src/services/system-config.service'; import { SystemMetadataService } from 'src/services/system-metadata.service'; import { TagService } from 'src/services/tag.service'; +import { TelemetryService } from 'src/services/telemetry.service'; import { TimelineService } from 'src/services/timeline.service'; import { TrashService } from 'src/services/trash.service'; import { UserAdminService } from 'src/services/user-admin.service'; @@ -78,6 +79,7 @@ export const services = [ SystemConfigService, SystemMetadataService, TagService, + TelemetryService, TimelineService, TrashService, UserAdminService, diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index 6b85cdff4d..26c7260889 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -222,18 +222,16 @@ describe(JobService.name, () => { }); }); - describe('onJobStart', () => { + describe('onJobRun', () => { it('should process a successful job', async () => { mocks.job.run.mockResolvedValue(JobStatus.Success); - await sut.onJobStart(QueueName.BackgroundTask, { - name: JobName.FileDelete, - data: { files: ['path/to/file'] }, - }); + const job: JobItem = { name: JobName.FileDelete, data: { files: ['path/to/file'] } }; + await sut.onJobRun(QueueName.BackgroundTask, job); - expect(mocks.telemetry.jobs.addToGauge).toHaveBeenCalledWith('immich.queues.background_task.active', 1); - expect(mocks.telemetry.jobs.addToGauge).toHaveBeenCalledWith('immich.queues.background_task.active', -1); - expect(mocks.telemetry.jobs.addToCounter).toHaveBeenCalledWith('immich.jobs.file_delete.success', 1); + expect(mocks.event.emit).toHaveBeenCalledWith('JobStart', QueueName.BackgroundTask, job); + expect(mocks.event.emit).toHaveBeenCalledWith('JobSuccess', { job, response: JobStatus.Success }); + expect(mocks.event.emit).toHaveBeenCalledWith('JobComplete', QueueName.BackgroundTask, job); expect(mocks.logger.error).not.toHaveBeenCalled(); }); @@ -300,7 +298,7 @@ describe(JobService.name, () => { mocks.job.run.mockResolvedValue(JobStatus.Success); - await sut.onJobStart(QueueName.BackgroundTask, item); + await sut.onJobRun(QueueName.BackgroundTask, item); if (jobs.length > 1) { expect(mocks.job.queueAll).toHaveBeenCalledWith( @@ -317,7 +315,7 @@ describe(JobService.name, () => { it(`should not queue any jobs when ${item.name} fails`, async () => { mocks.job.run.mockResolvedValue(JobStatus.Failed); - await sut.onJobStart(QueueName.BackgroundTask, item); + await sut.onJobRun(QueueName.BackgroundTask, item); expect(mocks.job.queueAll).not.toHaveBeenCalled(); }); diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index dc48c03bd1..658e5dbd7a 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { ClassConstructor } from 'class-transformer'; -import { snakeCase } from 'lodash'; import { SystemConfig } from 'src/config'; import { OnEvent } from 'src/decorators'; import { mapAsset } from 'src/dtos/asset-response.dto'; @@ -186,7 +185,7 @@ export class JobService extends BaseService { throw new BadRequestException(`Job is already running`); } - this.telemetryRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); + await this.eventRepository.emit('QueueStart', { name }); switch (name) { case QueueName.VideoConversion: { @@ -243,21 +242,19 @@ export class JobService extends BaseService { } } - @OnEvent({ name: 'JobStart' }) - async onJobStart(...[queueName, job]: ArgsOf<'JobStart'>) { - const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; - this.telemetryRepository.jobs.addToGauge(queueMetric, 1); + @OnEvent({ name: 'JobRun' }) + async onJobRun(...[queueName, job]: ArgsOf<'JobRun'>) { try { - const status = await this.jobRepository.run(job); - const jobMetric = `immich.jobs.${snakeCase(job.name)}.${status}`; - this.telemetryRepository.jobs.addToCounter(jobMetric, 1); - if (status === JobStatus.Success || status == JobStatus.Skipped) { + await this.eventRepository.emit('JobStart', queueName, job); + const response = await this.jobRepository.run(job); + await this.eventRepository.emit('JobSuccess', { job, response }); + if (response && typeof response === 'string' && [JobStatus.Success, JobStatus.Skipped].includes(response)) { await this.onDone(job); } } catch (error: Error | any) { - await this.eventRepository.emit('JobFailed', { job, error }); + await this.eventRepository.emit('JobError', { job, error }); } finally { - this.telemetryRepository.jobs.addToGauge(queueMetric, -1); + await this.eventRepository.emit('JobComplete', queueName, job); } } @@ -424,11 +421,6 @@ export class JobService extends BaseService { } break; } - - case JobName.UserDelete: { - this.eventRepository.clientBroadcast('on_user_delete', item.data.id); - break; - } } } } diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index 7d3de76550..b73a9b6bf0 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -447,7 +447,10 @@ export class MetadataService extends BaseService { * For RAW images in the CR2 or RAF format, the "ImageSize" value seems to be correct, * but ImageWidth and ImageHeight are not correct (they contain the dimensions of the preview image). */ - let [width, height] = exifTags.ImageSize?.split('x').map((dim) => Number.parseInt(dim) || undefined) || []; + let [width, height] = + exifTags.ImageSize?.toString() + ?.split('x') + ?.map((dim) => Number.parseInt(dim) || undefined) ?? []; if (!width || !height) { [width, height] = [exifTags.ImageWidth, exifTags.ImageHeight]; } diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index af0c1b981e..51cd51811f 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -78,8 +78,8 @@ export class NotificationService extends BaseService { await this.notificationRepository.cleanup(); } - @OnEvent({ name: 'JobFailed' }) - async onJobFailed({ job, error }: ArgOf<'JobFailed'>) { + @OnEvent({ name: 'JobError' }) + async onJobError({ job, error }: ArgOf<'JobError'>) { const admin = await this.userRepository.getAdmin(); if (!admin) { return; @@ -202,6 +202,11 @@ export class NotificationService extends BaseService { } } + @OnEvent({ name: 'UserDelete' }) + onUserDelete({ id }: ArgOf<'UserDelete'>) { + this.eventRepository.clientBroadcast('on_user_delete', id); + } + @OnEvent({ name: 'AlbumUpdate' }) async onAlbumUpdate({ id, recipientId }: ArgOf<'AlbumUpdate'>) { await this.jobRepository.removeJob(JobName.NotifyAlbumUpdate, `${id}/${recipientId}`); diff --git a/server/src/services/telemetry.service.ts b/server/src/services/telemetry.service.ts new file mode 100644 index 0000000000..7c4fe43214 --- /dev/null +++ b/server/src/services/telemetry.service.ts @@ -0,0 +1,59 @@ +import { snakeCase } from 'lodash'; +import { OnEvent } from 'src/decorators'; +import { ImmichWorker, JobStatus } from 'src/enum'; +import { ArgOf, ArgsOf } from 'src/repositories/event.repository'; +import { BaseService } from 'src/services/base.service'; + +export class TelemetryService extends BaseService { + @OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.Api] }) + async onBootstrap(): Promise { + const userCount = await this.userRepository.getCount(); + this.telemetryRepository.api.addToGauge('immich.users.total', userCount); + } + + @OnEvent({ name: 'UserCreate' }) + onUserCreate() { + this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); + } + + @OnEvent({ name: 'UserTrash' }) + onUserTrash() { + this.telemetryRepository.api.addToGauge(`immich.users.total`, -1); + } + + @OnEvent({ name: 'UserRestore' }) + onUserRestore() { + this.telemetryRepository.api.addToGauge(`immich.users.total`, 1); + } + + @OnEvent({ name: 'JobStart' }) + onJobStart(...[queueName]: ArgsOf<'JobStart'>) { + const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; + this.telemetryRepository.jobs.addToGauge(queueMetric, 1); + } + + @OnEvent({ name: 'JobSuccess' }) + onJobSuccess({ job, response }: ArgOf<'JobSuccess'>) { + if (response && Object.values(JobStatus).includes(response as JobStatus)) { + const jobMetric = `immich.jobs.${snakeCase(job.name)}.${response}`; + this.telemetryRepository.jobs.addToCounter(jobMetric, 1); + } + } + + @OnEvent({ name: 'JobError' }) + onJobError({ job }: ArgOf<'JobError'>) { + const jobMetric = `immich.jobs.${snakeCase(job.name)}.${JobStatus.Failed}`; + this.telemetryRepository.jobs.addToCounter(jobMetric, 1); + } + + @OnEvent({ name: 'JobComplete' }) + onJobComplete(...[queueName]: ArgsOf<'JobComplete'>) { + const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; + this.telemetryRepository.jobs.addToGauge(queueMetric, -1); + } + + @OnEvent({ name: 'QueueStart' }) + onQueueStart({ name }: ArgOf<'QueueStart'>) { + this.telemetryRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); + } +} diff --git a/server/src/services/user-admin.service.ts b/server/src/services/user-admin.service.ts index a57072e496..58b4221cc9 100644 --- a/server/src/services/user-admin.service.ts +++ b/server/src/services/user-admin.service.ts @@ -2,6 +2,7 @@ import { BadRequestException, ForbiddenException, Injectable } from '@nestjs/com import { SALT_ROUNDS } from 'src/constants'; import { AssetStatsDto, AssetStatsResponseDto, mapStats } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SessionResponseDto, mapSession } from 'src/dtos/session.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto'; import { UserAdminCreateDto, @@ -102,7 +103,8 @@ export class UserAdminService extends BaseService { const status = force ? UserStatus.Removing : UserStatus.Deleted; const user = await this.userRepository.update(id, { status, deletedAt: new Date() }); - this.telemetryRepository.api.addToGauge(`immich.users.total`, -1); + + await this.eventRepository.emit('UserTrash', user); if (force) { await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } }); @@ -115,10 +117,15 @@ export class UserAdminService extends BaseService { await this.findOrFail(id, { withDeleted: true }); await this.albumRepository.restoreAll(id); const user = await this.userRepository.restore(id); - this.telemetryRepository.api.addToGauge('immich.users.total', 1); + await this.eventRepository.emit('UserRestore', user); return mapUserAdmin(user); } + async getSessions(auth: AuthDto, id: string): Promise { + const sessions = await this.sessionRepository.getByUserId(id); + return sessions.map((session) => mapSession(session)); + } + async getStatistics(auth: AuthDto, id: string, dto: AssetStatsDto): Promise { const stats = await this.assetRepository.getStatistics(id, dto); return mapStats(stats); diff --git a/server/src/services/user.service.ts b/server/src/services/user.service.ts index fc71777673..9fb1f45e54 100644 --- a/server/src/services/user.service.ts +++ b/server/src/services/user.service.ts @@ -3,14 +3,14 @@ import { Updateable } from 'kysely'; import { DateTime } from 'luxon'; import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { OnEvent, OnJob } from 'src/decorators'; +import { OnJob } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto'; import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto, mapPreferences } from 'src/dtos/user-preferences.dto'; import { CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto'; -import { CacheControl, ImmichWorker, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; +import { CacheControl, JobName, JobStatus, QueueName, StorageFolder, UserMetadataKey } from 'src/enum'; import { UserFindOptions } from 'src/repositories/user.repository'; import { UserTable } from 'src/schema/tables/user.table'; import { BaseService } from 'src/services/base.service'; @@ -213,12 +213,6 @@ export class UserService extends BaseService { }; } - @OnEvent({ name: 'AppBootstrap', workers: [ImmichWorker.Api] }) - async onBootstrap(): Promise { - const userCount = await this.userRepository.getCount(); - this.telemetryRepository.api.addToGauge('immich.users.total', userCount); - } - @OnJob({ name: JobName.UserSyncUsage, queue: QueueName.BackgroundTask }) async handleUserSyncUsage(): Promise { await this.userRepository.syncUsage(); @@ -234,17 +228,17 @@ export class UserService extends BaseService { } @OnJob({ name: JobName.UserDelete, queue: QueueName.BackgroundTask }) - async handleUserDelete({ id, force }: JobOf): Promise { + async handleUserDelete({ id, force }: JobOf) { const config = await this.getConfig({ withCache: false }); const user = await this.userRepository.get(id, { withDeleted: true }); if (!user) { - return JobStatus.Failed; + return; } // just for extra protection here if (!force && !this.isReadyForDeletion(user, config.user.deleteDelay)) { this.logger.warn(`Skipped user that was not ready for deletion: id=${id}`); - return JobStatus.Skipped; + return; } this.logger.log(`Deleting user: ${user.id}`); @@ -266,7 +260,7 @@ export class UserService extends BaseService { await this.albumRepository.deleteAll(user.id); await this.userRepository.delete(user, true); - return JobStatus.Success; + await this.eventRepository.emit('UserDelete', user); } private isReadyForDeletion(user: { id: string; deletedAt?: Date | null }, deleteDelay: number): boolean { diff --git a/server/src/utils/request.ts b/server/src/utils/request.ts index 19d3cac661..c64c980520 100644 --- a/server/src/utils/request.ts +++ b/server/src/utils/request.ts @@ -1,5 +1,22 @@ +import { IncomingHttpHeaders } from 'node:http'; +import { UAParser } from 'ua-parser-js'; + export const fromChecksum = (checksum: string): Buffer => { return Buffer.from(checksum, checksum.length === 28 ? 'base64' : 'hex'); }; export const fromMaybeArray = (param: T | T[]) => (Array.isArray(param) ? param[0] : param); + +const getAppVersionFromUA = (ua: string) => + ua.match(/^Immich_(?:Android|iOS)_(?.+)$/)?.groups?.appVersion ?? null; + +export const getUserAgentDetails = (headers: IncomingHttpHeaders) => { + const userAgent = UAParser(headers['user-agent']); + const appVersion = getAppVersionFromUA(headers['user-agent'] ?? ''); + + return { + deviceType: userAgent.browser.name || userAgent.device.type || (headers['devicemodel'] as string) || '', + deviceOS: userAgent.os.name || (headers['devicetype'] as string) || '', + appVersion, + }; +}; diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index 3f021f3eb7..a8d3f9df78 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -628,7 +628,7 @@ const syncStream = () => { }; const loginDetails = () => { - return { isSecure: false, clientIp: '', deviceType: '', deviceOS: '' }; + return { isSecure: false, clientIp: '', deviceType: '', deviceOS: '', appVersion: null }; }; const loginResponse = (): LoginResponseDto => { diff --git a/server/test/medium/specs/services/auth.service.spec.ts b/server/test/medium/specs/services/auth.service.spec.ts index 60d3210f4c..f8bc3f1259 100644 --- a/server/test/medium/specs/services/auth.service.spec.ts +++ b/server/test/medium/specs/services/auth.service.spec.ts @@ -44,7 +44,8 @@ beforeAll(async () => { describe(AuthService.name, () => { describe('adminSignUp', () => { it(`should sign up the admin`, async () => { - const { sut } = setup(); + const { sut, ctx } = setup(); + ctx.getMock(EventRepository).emit.mockResolvedValue(); const dto = { name: 'Admin', email: 'admin@immich.cloud', password: 'password' }; await expect(sut.adminSignUp(dto)).resolves.toEqual( diff --git a/server/test/medium/specs/services/user.service.spec.ts b/server/test/medium/specs/services/user.service.spec.ts index 0d72d39950..24a06404b1 100644 --- a/server/test/medium/specs/services/user.service.spec.ts +++ b/server/test/medium/specs/services/user.service.spec.ts @@ -3,10 +3,10 @@ import { DateTime } from 'luxon'; import { ImmichEnvironment, JobName, JobStatus } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { CryptoRepository } from 'src/repositories/crypto.repository'; +import { EventRepository } from 'src/repositories/event.repository'; import { JobRepository } from 'src/repositories/job.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; -import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { DB } from 'src/schema'; import { UserService } from 'src/services/user.service'; @@ -22,7 +22,7 @@ const setup = (db?: Kysely) => { return newMediumService(UserService, { database: db || defaultDatabase, real: [CryptoRepository, ConfigRepository, SystemMetadataRepository, UserRepository], - mock: [LoggingRepository, JobRepository, TelemetryRepository], + mock: [LoggingRepository, JobRepository, EventRepository], }); }; @@ -35,7 +35,8 @@ beforeAll(async () => { describe(UserService.name, () => { describe('create', () => { it('should create a user', async () => { - const { sut } = setup(); + const { sut, ctx } = setup(); + ctx.getMock(EventRepository).emit.mockResolvedValue(); const user = mediumFactory.userInsert(); await expect(sut.createUser({ name: user.name, email: user.email })).resolves.toEqual( expect.objectContaining({ name: user.name, email: user.email }), @@ -43,14 +44,16 @@ describe(UserService.name, () => { }); it('should reject user with duplicate email', async () => { - const { sut } = setup(); + const { sut, ctx } = setup(); + ctx.getMock(EventRepository).emit.mockResolvedValue(); const user = mediumFactory.userInsert(); await expect(sut.createUser({ email: user.email })).resolves.toMatchObject({ email: user.email }); await expect(sut.createUser({ email: user.email })).rejects.toThrow('User exists'); }); it('should not return password', async () => { - const { sut } = setup(); + const { sut, ctx } = setup(); + ctx.getMock(EventRepository).emit.mockResolvedValue(); const dto = mediumFactory.userInsert({ password: 'password' }); const user = await sut.createUser({ email: dto.email, password: 'password' }); expect((user as any).password).toBeUndefined(); diff --git a/server/test/small.factory.ts b/server/test/small.factory.ts index 04654552a3..09e7988f8f 100644 --- a/server/test/small.factory.ts +++ b/server/test/small.factory.ts @@ -135,6 +135,7 @@ const sessionFactory = (session: Partial = {}) => ({ userId: newUuid(), pinExpiresAt: newDate(), isPendingSyncReset: false, + appVersion: session.appVersion ?? null, ...session, }); diff --git a/web/package.json b/web/package.json index 5eacb72b85..fad799813d 100644 --- a/web/package.json +++ b/web/package.json @@ -27,7 +27,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.34.0", + "@immich/ui": "^0.37.1", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -95,7 +95,7 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "5.39.11", + "svelte": "5.40.1", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.3.3", "tailwindcss": "^4.1.7", diff --git a/web/src/lib/components/layouts/AdminPageLayout.svelte b/web/src/lib/components/layouts/AdminPageLayout.svelte index 5a580dbde8..a74a6aee35 100644 --- a/web/src/lib/components/layouts/AdminPageLayout.svelte +++ b/web/src/lib/components/layouts/AdminPageLayout.svelte @@ -20,7 +20,7 @@ - + diff --git a/web/src/lib/components/onboarding-page/onboarding-mobile-app.svelte b/web/src/lib/components/onboarding-page/onboarding-mobile-app.svelte index 4c46ad76d1..27e24d17e1 100644 --- a/web/src/lib/components/onboarding-page/onboarding-mobile-app.svelte +++ b/web/src/lib/components/onboarding-page/onboarding-mobile-app.svelte @@ -6,22 +6,26 @@ import { t } from 'svelte-i18n'; - +

{$t('mobile_app_download_onboarding_note')}

+ + + + - -

{$t('mobile_app_download_onboarding_note')}

diff --git a/web/src/lib/components/shared-components/upload-asset-preview.svelte b/web/src/lib/components/shared-components/upload-asset-preview.svelte index 43855a9e28..6a9549ab17 100644 --- a/web/src/lib/components/shared-components/upload-asset-preview.svelte +++ b/web/src/lib/components/shared-components/upload-asset-preview.svelte @@ -40,7 +40,11 @@ }; -
+
{#if uploadAsset.state === UploadState.PENDING} @@ -91,12 +95,13 @@
{#if uploadAsset.state === UploadState.STARTED} -
-
-

- {#if uploadAsset.message} +

+
+

+ {#if uploadAsset.message === $t('asset_hashing')} {uploadAsset.message} {:else} + {uploadAsset.message} {uploadAsset.progress}% - {getByteUnitString(uploadAsset.speed || 0, $locale)}/s - {uploadAsset.eta}s {/if}

diff --git a/web/src/lib/components/shared-components/upload-panel.svelte b/web/src/lib/components/shared-components/upload-panel.svelte index 658d48ae6a..68b39e163d 100644 --- a/web/src/lib/components/shared-components/upload-panel.svelte +++ b/web/src/lib/components/shared-components/upload-panel.svelte @@ -52,7 +52,7 @@ {#if showDetail}
diff --git a/web/src/lib/components/timeline/Scrubber.svelte b/web/src/lib/components/timeline/Scrubber.svelte index 069b62002e..827f9df587 100644 --- a/web/src/lib/components/timeline/Scrubber.svelte +++ b/web/src/lib/components/timeline/Scrubber.svelte @@ -141,14 +141,18 @@ }; const calculateSegments = (months: ScrubberMonth[]) => { - let height = 0; - let dotHeight = 0; + let verticalSpanWithoutLabel = 0; + let verticalSpanWithoutDot = 0; let segments: Segment[] = []; let previousLabeledSegment: Segment | undefined; let top = 0; - for (const [i, scrubMonth] of months.entries()) { + + // Process months in reverse order to pick labels, then reverse for display + const reversed = [...months].reverse(); + + for (const scrubMonth of reversed) { const scrollBarPercentage = scrubMonth.height / timelineFullHeight; const segment = { @@ -162,25 +166,26 @@ hasDot: false, }; top += segment.height; - if (i === 0) { - segment.hasDot = true; - segment.hasLabel = true; - previousLabeledSegment = segment; - } else { - if (previousLabeledSegment?.year !== segment.year && height > MIN_YEAR_LABEL_DISTANCE) { - height = 0; + if (previousLabeledSegment) { + if (previousLabeledSegment.year !== segment.year && verticalSpanWithoutLabel > MIN_YEAR_LABEL_DISTANCE) { + verticalSpanWithoutLabel = 0; segment.hasLabel = true; previousLabeledSegment = segment; } - if (segment.height > 5 && dotHeight > MIN_DOT_DISTANCE) { + if (segment.height > 5 && verticalSpanWithoutDot > MIN_DOT_DISTANCE) { segment.hasDot = true; - dotHeight = 0; + verticalSpanWithoutDot = 0; } - height += segment.height; + } else { + segment.hasDot = true; + segment.hasLabel = true; + previousLabeledSegment = segment; } - dotHeight += segment.height; + verticalSpanWithoutLabel += segment.height; + verticalSpanWithoutDot += segment.height; segments.push(segment); } + segments.reverse(); return segments; }; @@ -576,7 +581,7 @@ > {#if !usingMobileDevice} {#if segment.hasLabel} -
+
{segment.year}
{/if} diff --git a/web/src/lib/components/user-settings-page/device-card.svelte b/web/src/lib/components/user-settings-page/device-card.svelte index 15d9ede219..f3156b7e7d 100644 --- a/web/src/lib/components/user-settings-page/device-card.svelte +++ b/web/src/lib/components/user-settings-page/device-card.svelte @@ -18,11 +18,11 @@ import { t } from 'svelte-i18n'; interface Props { - device: SessionResponseDto; + session: SessionResponseDto; onDelete?: (() => void) | undefined; } - let { device, onDelete = undefined }: Props = $props(); + const { session, onDelete = undefined }: Props = $props(); const options: ToRelativeCalendarOptions = { unit: 'days', @@ -32,21 +32,21 @@