diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 708ab8122..4473b70d1 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -188,7 +188,7 @@ jobs: # arm64 layer never poisons an amd64 build). - name: Build & push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: ./Dockerfile diff --git a/messages/de.json b/messages/de.json index 94ffa09ed..8084f470b 100644 --- a/messages/de.json +++ b/messages/de.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ Überspringen" + }, + "moodReminders": { + "dailyTitle": "Stimmung erfassen", + "dailyBody": "Wie geht es dir heute?" } } diff --git a/messages/en.json b/messages/en.json index cc8043984..84e4fea2d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ Skip" + }, + "moodReminders": { + "dailyTitle": "Log your mood", + "dailyBody": "How are you feeling today?" } } diff --git a/messages/es.json b/messages/es.json index e4cdb79c9..65cb0d819 100644 --- a/messages/es.json +++ b/messages/es.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ Omitir" + }, + "moodReminders": { + "dailyTitle": "Registrar el Γ‘nimo", + "dailyBody": "ΒΏCΓ³mo te sientes hoy?" } } diff --git a/messages/fr.json b/messages/fr.json index 1917e8ff6..3cdfe68da 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ Ignorer" + }, + "moodReminders": { + "dailyTitle": "Enregistrer votre humeur", + "dailyBody": "Comment vous sentez-vous aujourdhui ?" } } diff --git a/messages/it.json b/messages/it.json index 8c0c39723..12d69c952 100644 --- a/messages/it.json +++ b/messages/it.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ Salta" + }, + "moodReminders": { + "dailyTitle": "Registra il tuo umore", + "dailyBody": "Come ti senti oggi?" } } diff --git a/messages/pl.json b/messages/pl.json index 87d5ad7dc..7afc2bf37 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -2987,5 +2987,9 @@ "buttonSnoozeOneHour": "πŸ• 1h", "buttonSnoozeThreeHours": "πŸ• 3h", "buttonSkip": "⏭ PomiΕ„" + }, + "moodReminders": { + "dailyTitle": "Zapisz nastrΓ³j", + "dailyBody": "Jak siΔ™ dzisiaj czujesz?" } } diff --git a/package.json b/package.json index 0eb8a81c3..fbeb8ffa5 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "openapi:check": "npx tsx scripts/check-openapi.ts" }, "dependencies": { - "@aws-sdk/client-s3": "^3.1045.0", + "@aws-sdk/client-s3": "^3.1048.0", "@hookform/resolvers": "^5.2.2", "@node-rs/argon2": "^2.0.2", "@parse/node-apn": "^8.1.0", @@ -55,7 +55,7 @@ "@prisma/client": "^7.8.0", "@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/server": "^13.3.0", - "@tanstack/react-query": "^5.100.9", + "@tanstack/react-query": "^5.100.10", "@types/sax": "^1.2.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -85,7 +85,7 @@ "devDependencies": { "@axe-core/playwright": "^4.11.3", "@next/bundle-analyzer": "^16.2.6", - "@playwright/test": "^1.59.1", + "@playwright/test": "^1.60.0", "@tailwindcss/postcss": "^4", "@testcontainers/postgresql": "^11.14.0", "@types/node": "^25", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96ad67a09..10b724fc4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@aws-sdk/client-s3': - specifier: ^3.1045.0 - version: 3.1045.0 + specifier: ^3.1048.0 + version: 3.1048.0 '@hookform/resolvers': specifier: ^5.2.2 version: 5.2.2(react-hook-form@7.75.0(react@19.2.5)) @@ -33,8 +33,8 @@ importers: specifier: ^13.3.0 version: 13.3.0 '@tanstack/react-query': - specifier: ^5.100.9 - version: 5.100.9(react@19.2.5) + specifier: ^5.100.10 + version: 5.100.10(react@19.2.5) '@types/sax': specifier: ^1.2.7 version: 1.2.7 @@ -61,7 +61,7 @@ importers: version: 3.0.2 next: specifier: 16.2.6 - version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.59.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -113,13 +113,13 @@ importers: devDependencies: '@axe-core/playwright': specifier: ^4.11.3 - version: 4.11.3(playwright-core@1.59.1) + version: 4.11.3(playwright-core@1.60.0) '@next/bundle-analyzer': specifier: ^16.2.6 version: 16.2.6 '@playwright/test': - specifier: ^1.59.1 - version: 1.59.1 + specifier: ^1.60.0 + version: 1.60.0 '@tailwindcss/postcss': specifier: ^4 version: 4.3.0 @@ -128,7 +128,7 @@ importers: version: 11.14.0 '@types/node': specifier: ^25 - version: 25.6.2 + version: 25.8.0 '@types/pg': specifier: ^8.20.0 version: 8.20.0 @@ -143,7 +143,7 @@ importers: version: 3.6.4 '@vitejs/plugin-react': specifier: ^5.1.4 - version: 5.1.4(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) + version: 5.1.4(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) '@vitest/coverage-v8': specifier: ^4.1.5 version: 4.1.5(vitest@4.1.5) @@ -167,7 +167,7 @@ importers: version: 0.8.0(prettier@3.8.3) shadcn: specifier: ^4.7.0 - version: 4.7.0(@types/node@25.6.2)(typescript@6.0.3) + version: 4.7.0(@types/node@25.8.0)(typescript@6.0.3) tailwindcss: specifier: ^4 version: 4.3.0 @@ -182,7 +182,7 @@ importers: version: 6.0.3 vitest: specifier: ^4.1.5 - version: 4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) + version: 4.1.5(@types/node@25.8.0)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3))(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) packages: @@ -213,136 +213,96 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1045.0': - resolution: {integrity: sha512-fsuO3Y6t+3Ro9Bsg41DKj4Sfy53CGSrhnMldNplWmG8Tx0UbYk+YDa4RD1hVlJpERw4JBmPkl0+J9qlxMh1pcA==} + '@aws-sdk/client-s3@3.1048.0': + resolution: {integrity: sha512-SrJn5FteqqtcDBgQIvqLKk3Qn/2vSsi5XR03I53EDDR4CbCdLysVSNgUnjVncEECMua9Pz+nxO0/lEx3TP+6mA==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.974.8': - resolution: {integrity: sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==} + '@aws-sdk/core@3.974.11': + resolution: {integrity: sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==} engines: {node: '>=20.0.0'} - '@aws-sdk/crc64-nvme@3.972.7': - resolution: {integrity: sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==} + '@aws-sdk/crc64-nvme@3.972.8': + resolution: {integrity: sha512-fVfUCL/Xh2zINYMPZvj+iBn6XWouQf0DAnjaWCI9MkmqXzL2Iy5FoQB8O7syFe6gN6AH1ecDDU58T51Ou0kFkA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.34': - resolution: {integrity: sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==} + '@aws-sdk/credential-provider-env@3.972.37': + resolution: {integrity: sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.36': - resolution: {integrity: sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==} + '@aws-sdk/credential-provider-http@3.972.39': + resolution: {integrity: sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.38': - resolution: {integrity: sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==} + '@aws-sdk/credential-provider-ini@3.972.41': + resolution: {integrity: sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.38': - resolution: {integrity: sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==} + '@aws-sdk/credential-provider-login@3.972.41': + resolution: {integrity: sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.39': - resolution: {integrity: sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==} + '@aws-sdk/credential-provider-node@3.972.42': + resolution: {integrity: sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.34': - resolution: {integrity: sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==} + '@aws-sdk/credential-provider-process@3.972.37': + resolution: {integrity: sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.38': - resolution: {integrity: sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==} + '@aws-sdk/credential-provider-sso@3.972.41': + resolution: {integrity: sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.38': - resolution: {integrity: sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==} + '@aws-sdk/credential-provider-web-identity@3.972.41': + resolution: {integrity: sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.972.10': - resolution: {integrity: sha512-Vbc2frZH7wXlMNd+ZZSXUEs/l1Sv8Jj4zUnIfwrYF5lwaLdXHZ9xx4U3rjUcaye3HRhFVc+E5DbBxpRAbB16BA==} + '@aws-sdk/middleware-bucket-endpoint@3.972.13': + resolution: {integrity: sha512-JDaukix+kt5KwF7FzNSkfZHpqiPJajVkKJLJexF6z5B44+CN70BXGiQaCEAiCtKtRZNvC16eF3SY9L0bDJPlbA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.972.10': - resolution: {integrity: sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==} + '@aws-sdk/middleware-expect-continue@3.972.12': + resolution: {integrity: sha512-dA5pKTom/Ls9mgeyeaRBNQrRIVOLVjv4AmKOB0/e4yaiXEUy0gSz2d3liP8JHtYoCAEWySU1jWnyzwLOREN+4g==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.16': - resolution: {integrity: sha512-6ru8doI0/XzszqLIPXf0E/V7HhAw1Pu94010XCKYtBUfD0LxF0BuOzrUf8OQGR6j2o6wgKTHUniOmndQycHwCA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-host-header@3.972.10': - resolution: {integrity: sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==} + '@aws-sdk/middleware-flexible-checksums@3.974.19': + resolution: {integrity: sha512-GLciZVIvWM3C+ffuqnUqlAZwRjQdLt+KXiqr9+aRwZyKVyF2J5lrJAzzSqwweNl9hUWBN00BhilWXdMI5DjNcw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-location-constraint@3.972.10': resolution: {integrity: sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.972.10': - resolution: {integrity: sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-recursion-detection@3.972.11': - resolution: {integrity: sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.972.37': - resolution: {integrity: sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==} + '@aws-sdk/middleware-sdk-s3@3.972.40': + resolution: {integrity: sha512-vyFY4EsAGySqqd87Z7n4qcCYXJO3QArB8VIJzuupY5XuLHIp579HTZldIUGGABvAOzLptfPb9+lJBJcB+3/cvA==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.10': resolution: {integrity: sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.38': - resolution: {integrity: sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==} + '@aws-sdk/nested-clients@3.997.9': + resolution: {integrity: sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.997.6': - resolution: {integrity: sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==} + '@aws-sdk/signature-v4-multi-region@3.996.27': + resolution: {integrity: sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.13': - resolution: {integrity: sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.996.25': - resolution: {integrity: sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.1041.0': - resolution: {integrity: sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==} + '@aws-sdk/token-providers@3.1048.0': + resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.8': resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-arn-parser@3.972.3': - resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-endpoints@3.996.8': - resolution: {integrity: sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==} - engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.965.5': resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.972.10': - resolution: {integrity: sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==} - - '@aws-sdk/util-user-agent-node@3.973.24': - resolution: {integrity: sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==} - engines: {node: '>=20.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - - '@aws-sdk/xml-builder@3.972.22': - resolution: {integrity: sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==} + '@aws-sdk/xml-builder@3.972.24': + resolution: {integrity: sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': @@ -1351,8 +1311,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.59.1': - resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==} + '@playwright/test@1.60.0': + resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} engines: {node: '>=18'} hasBin: true @@ -2156,141 +2116,141 @@ packages: '@rolldown/pluginutils@1.0.0-rc.3': resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} - '@rollup/rollup-android-arm-eabi@4.60.3': - resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.3': - resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.3': - resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.3': - resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.3': - resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.3': - resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': - resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.3': - resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.3': - resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.3': - resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.3': - resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.3': - resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.3': - resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.3': - resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.3': - resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.3': - resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.3': - resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.3': - resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.3': - resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.3': - resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.3': - resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.3': - resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.3': - resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.3': - resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.3': - resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} cpu: [x64] os: [win32] @@ -2311,218 +2271,42 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} - '@smithy/chunked-blob-reader-native@4.2.3': - resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} + '@smithy/core@3.24.3': + resolution: {integrity: sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.2.2': - resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} + '@smithy/credential-provider-imds@4.3.3': + resolution: {integrity: sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.17': - resolution: {integrity: sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==} - engines: {node: '>=18.0.0'} - - '@smithy/core@3.23.17': - resolution: {integrity: sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.2.14': - resolution: {integrity: sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-codec@4.2.14': - resolution: {integrity: sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-browser@4.2.14': - resolution: {integrity: sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-config-resolver@4.3.14': - resolution: {integrity: sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-node@4.2.14': - resolution: {integrity: sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-universal@4.2.14': - resolution: {integrity: sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.3.17': - resolution: {integrity: sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-blob-browser@4.2.15': - resolution: {integrity: sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-node@4.2.14': - resolution: {integrity: sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-stream-node@4.2.14': - resolution: {integrity: sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ==} - engines: {node: '>=18.0.0'} - - '@smithy/invalid-dependency@4.2.14': - resolution: {integrity: sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==} + '@smithy/fetch-http-handler@5.4.3': + resolution: {integrity: sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.2.2': - resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} + '@smithy/node-http-handler@4.7.3': + resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.14': - resolution: {integrity: sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA==} + '@smithy/signature-v4@5.4.3': + resolution: {integrity: sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.14': - resolution: {integrity: sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-endpoint@4.4.32': - resolution: {integrity: sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-retry@4.5.7': - resolution: {integrity: sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-serde@4.2.20': - resolution: {integrity: sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-stack@4.2.14': - resolution: {integrity: sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==} - engines: {node: '>=18.0.0'} - - '@smithy/node-config-provider@4.3.14': - resolution: {integrity: sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.6.1': - resolution: {integrity: sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==} - engines: {node: '>=18.0.0'} - - '@smithy/property-provider@4.2.14': - resolution: {integrity: sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==} - engines: {node: '>=18.0.0'} - - '@smithy/protocol-http@5.3.14': - resolution: {integrity: sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-builder@4.2.14': - resolution: {integrity: sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-parser@4.2.14': - resolution: {integrity: sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==} - engines: {node: '>=18.0.0'} - - '@smithy/service-error-classification@4.3.1': - resolution: {integrity: sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==} - engines: {node: '>=18.0.0'} - - '@smithy/shared-ini-file-loader@4.4.9': - resolution: {integrity: sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.3.14': - resolution: {integrity: sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==} - engines: {node: '>=18.0.0'} - - '@smithy/smithy-client@4.12.13': - resolution: {integrity: sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.14.1': - resolution: {integrity: sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==} - engines: {node: '>=18.0.0'} - - '@smithy/url-parser@4.2.14': - resolution: {integrity: sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-base64@4.3.2': - resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-browser@4.2.2': - resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-node@4.2.3': - resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} + '@smithy/types@4.14.2': + resolution: {integrity: sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.2.2': - resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-config-provider@4.2.2': - resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-browser@4.3.49': - resolution: {integrity: sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-node@4.2.54': - resolution: {integrity: sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-endpoints@3.4.2': - resolution: {integrity: sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-hex-encoding@4.2.2': - resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-middleware@4.2.14': - resolution: {integrity: sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-retry@4.3.8': - resolution: {integrity: sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-stream@4.5.25': - resolution: {integrity: sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-uri-escape@4.2.2': - resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} - engines: {node: '>=18.0.0'} - '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.2': - resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-waiter@4.3.0': - resolution: {integrity: sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==} - engines: {node: '>=18.0.0'} - - '@smithy/uuid@1.1.2': - resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} - engines: {node: '>=18.0.0'} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2624,11 +2408,11 @@ packages: '@tailwindcss/postcss@4.3.0': resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==} - '@tanstack/query-core@5.100.9': - resolution: {integrity: sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==} + '@tanstack/query-core@5.100.10': + resolution: {integrity: sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w==} - '@tanstack/react-query@5.100.9': - resolution: {integrity: sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==} + '@tanstack/react-query@5.100.10': + resolution: {integrity: sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q==} peerDependencies: react: ^18 || ^19 @@ -2704,8 +2488,8 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@25.6.2': - resolution: {integrity: sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==} + '@types/node@25.8.0': + resolution: {integrity: sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==} '@types/pako@2.0.4': resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==} @@ -3958,11 +3742,11 @@ packages: fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} - fast-xml-builder@1.1.9: - resolution: {integrity: sha512-jcyKVSEX13iseJqg7n/KWw+xnu/7fdrZ333Fac54KjHDIELVCfDDJXYIm6DTJ0Su4gSzrhqiK0DzY/wZbF40mw==} + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} - fast-xml-parser@5.7.2: - resolution: {integrity: sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==} + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} hasBin: true fastq@1.20.1: @@ -5157,13 +4941,13 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.59.1: - resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} + playwright-core@1.60.0: + resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} engines: {node: '>=18'} hasBin: true - playwright@1.59.1: - resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} + playwright@1.60.0: + resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} engines: {node: '>=18'} hasBin: true @@ -5533,8 +5317,8 @@ packages: resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} engines: {node: '>= 0.8.15'} - rollup@4.60.3: - resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -5994,8 +5778,8 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@7.19.2: - resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@7.25.0: resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} @@ -6244,6 +6028,10 @@ packages: resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -6364,410 +6152,228 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1045.0': + '@aws-sdk/client-s3@3.1048.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-node': 3.972.39 - '@aws-sdk/middleware-bucket-endpoint': 3.972.10 - '@aws-sdk/middleware-expect-continue': 3.972.10 - '@aws-sdk/middleware-flexible-checksums': 3.974.16 - '@aws-sdk/middleware-host-header': 3.972.10 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/credential-provider-node': 3.972.42 + '@aws-sdk/middleware-bucket-endpoint': 3.972.13 + '@aws-sdk/middleware-expect-continue': 3.972.12 + '@aws-sdk/middleware-flexible-checksums': 3.974.19 '@aws-sdk/middleware-location-constraint': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-sdk-s3': 3.972.37 + '@aws-sdk/middleware-sdk-s3': 3.972.40 '@aws-sdk/middleware-ssec': 3.972.10 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/signature-v4-multi-region': 3.996.25 + '@aws-sdk/signature-v4-multi-region': 3.996.27 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/eventstream-serde-browser': 4.2.14 - '@smithy/eventstream-serde-config-resolver': 4.3.14 - '@smithy/eventstream-serde-node': 4.2.14 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-blob-browser': 4.2.15 - '@smithy/hash-node': 4.2.14 - '@smithy/hash-stream-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/md5-js': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - '@smithy/util-waiter': 4.3.0 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/core@3.974.8': + '@aws-sdk/core@3.974.11': dependencies: '@aws-sdk/types': 3.973.8 - '@aws-sdk/xml-builder': 3.972.22 - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 + '@aws-sdk/xml-builder': 3.972.24 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.24.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 + bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/crc64-nvme@3.972.7': + '@aws-sdk/crc64-nvme@3.972.8': dependencies: - '@smithy/types': 4.14.1 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.34': + '@aws-sdk/credential-provider-env@3.972.37': dependencies: - '@aws-sdk/core': 3.974.8 + '@aws-sdk/core': 3.974.11 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.36': + '@aws-sdk/credential-provider-http@3.972.39': dependencies: - '@aws-sdk/core': 3.974.8 + '@aws-sdk/core': 3.974.11 '@aws-sdk/types': 3.973.8 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.1 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.25 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.38': + '@aws-sdk/credential-provider-ini@3.972.41': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/credential-provider-env': 3.972.34 - '@aws-sdk/credential-provider-http': 3.972.36 - '@aws-sdk/credential-provider-login': 3.972.38 - '@aws-sdk/credential-provider-process': 3.972.34 - '@aws-sdk/credential-provider-sso': 3.972.38 - '@aws-sdk/credential-provider-web-identity': 3.972.38 - '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/credential-provider-env': 3.972.37 + '@aws-sdk/credential-provider-http': 3.972.39 + '@aws-sdk/credential-provider-login': 3.972.41 + '@aws-sdk/credential-provider-process': 3.972.37 + '@aws-sdk/credential-provider-sso': 3.972.41 + '@aws-sdk/credential-provider-web-identity': 3.972.41 + '@aws-sdk/nested-clients': 3.997.9 '@aws-sdk/types': 3.973.8 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/credential-provider-imds': 4.3.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-login@3.972.38': + '@aws-sdk/credential-provider-login@3.972.41': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-node@3.972.39': + '@aws-sdk/credential-provider-node@3.972.42': dependencies: - '@aws-sdk/credential-provider-env': 3.972.34 - '@aws-sdk/credential-provider-http': 3.972.36 - '@aws-sdk/credential-provider-ini': 3.972.38 - '@aws-sdk/credential-provider-process': 3.972.34 - '@aws-sdk/credential-provider-sso': 3.972.38 - '@aws-sdk/credential-provider-web-identity': 3.972.38 + '@aws-sdk/credential-provider-env': 3.972.37 + '@aws-sdk/credential-provider-http': 3.972.39 + '@aws-sdk/credential-provider-ini': 3.972.41 + '@aws-sdk/credential-provider-process': 3.972.37 + '@aws-sdk/credential-provider-sso': 3.972.41 + '@aws-sdk/credential-provider-web-identity': 3.972.41 '@aws-sdk/types': 3.973.8 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/credential-provider-imds': 4.3.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-process@3.972.34': + '@aws-sdk/credential-provider-process@3.972.37': dependencies: - '@aws-sdk/core': 3.974.8 + '@aws-sdk/core': 3.974.11 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.38': + '@aws-sdk/credential-provider-sso@3.972.41': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 - '@aws-sdk/token-providers': 3.1041.0 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/token-providers': 3.1048.0 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.38': + '@aws-sdk/credential-provider-web-identity@3.972.41': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/middleware-bucket-endpoint@3.972.10': + '@aws-sdk/middleware-bucket-endpoint@3.972.13': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-expect-continue@3.972.12': dependencies: '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-flexible-checksums@3.974.16': + '@aws-sdk/middleware-flexible-checksums@3.974.19': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/crc64-nvme': 3.972.7 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/crc64-nvme': 3.972.8 '@aws-sdk/types': 3.973.8 - '@smithy/is-array-buffer': 4.2.2 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@aws-sdk/middleware-host-header@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 '@aws-sdk/middleware-location-constraint@3.972.10': dependencies: '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.972.10': + '@aws-sdk/middleware-sdk-s3@3.972.40': dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/signature-v4-multi-region': 3.996.27 '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-recursion-detection@3.972.11': - dependencies: - '@aws-sdk/types': 3.973.8 - '@aws/lambda-invoke-store': 0.2.4 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-s3@3.972.37': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 + '@smithy/core': 3.24.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 '@aws-sdk/middleware-ssec@3.972.10': dependencies: '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.38': - dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@smithy/core': 3.23.17 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-retry': 4.3.8 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.997.6': + '@aws-sdk/nested-clients@3.997.9': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.8 - '@aws-sdk/middleware-host-header': 3.972.10 - '@aws-sdk/middleware-logger': 3.972.10 - '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/region-config-resolver': 3.972.13 - '@aws-sdk/signature-v4-multi-region': 3.996.25 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/signature-v4-multi-region': 3.996.27 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.8 - '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.24 - '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.17 - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/hash-node': 4.2.14 - '@smithy/invalid-dependency': 4.2.14 - '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-retry': 4.5.7 - '@smithy/middleware-serde': 4.2.20 - '@smithy/middleware-stack': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.1 - '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.49 - '@smithy/util-defaults-mode-node': 4.2.54 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/region-config-resolver@3.972.13': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/config-resolver': 4.4.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.25': + '@aws-sdk/signature-v4-multi-region@3.996.27': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.37 '@aws-sdk/types': 3.973.8 - '@smithy/protocol-http': 5.3.14 - '@smithy/signature-v4': 5.3.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1041.0': + '@aws-sdk/token-providers@3.1048.0': dependencies: - '@aws-sdk/core': 3.974.8 - '@aws-sdk/nested-clients': 3.997.6 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 '@aws-sdk/types': 3.973.8 - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt '@aws-sdk/types@3.973.8': dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@aws-sdk/util-arn-parser@3.972.3': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-endpoints@3.996.8': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-endpoints': 3.4.2 + '@smithy/types': 4.14.2 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.965.5': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.972.10': - dependencies: - '@aws-sdk/types': 3.973.8 - '@smithy/types': 4.14.1 - bowser: 2.14.1 - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-node@3.973.24': - dependencies: - '@aws-sdk/middleware-user-agent': 3.972.38 - '@aws-sdk/types': 3.973.8 - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.972.22': + '@aws-sdk/xml-builder@3.972.24': dependencies: '@nodable/entities': 2.1.0 - '@smithy/types': 4.14.1 - fast-xml-parser: 5.7.2 + '@smithy/types': 4.14.2 + fast-xml-parser: 5.7.3 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.4': {} - '@axe-core/playwright@4.11.3(playwright-core@1.59.1)': + '@axe-core/playwright@4.11.3(playwright-core@1.60.0)': dependencies: axe-core: 4.11.4 - playwright-core: 1.59.1 + playwright-core: 1.60.0 '@babel/code-frame@7.29.0': dependencies: @@ -7306,30 +6912,30 @@ snapshots: '@inquirer/ansi@2.0.5': {} - '@inquirer/confirm@6.0.12(@types/node@25.6.2)': + '@inquirer/confirm@6.0.12(@types/node@25.8.0)': dependencies: - '@inquirer/core': 11.1.9(@types/node@25.6.2) - '@inquirer/type': 4.0.5(@types/node@25.6.2) + '@inquirer/core': 11.1.9(@types/node@25.8.0) + '@inquirer/type': 4.0.5(@types/node@25.8.0) optionalDependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 - '@inquirer/core@11.1.9(@types/node@25.6.2)': + '@inquirer/core@11.1.9(@types/node@25.8.0)': dependencies: '@inquirer/ansi': 2.0.5 '@inquirer/figures': 2.0.5 - '@inquirer/type': 4.0.5(@types/node@25.6.2) + '@inquirer/type': 4.0.5(@types/node@25.8.0) cli-width: 4.1.0 fast-wrap-ansi: 0.2.0 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@inquirer/figures@2.0.5': {} - '@inquirer/type@4.0.5(@types/node@25.6.2)': + '@inquirer/type@4.0.5(@types/node@25.8.0)': optionalDependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@isaacs/cliui@8.0.2': dependencies: @@ -7693,9 +7299,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.59.1': + '@playwright/test@1.60.0': dependencies: - playwright: 1.59.1 + playwright: 1.60.0 '@polka/url@1.0.0-next.29': {} @@ -8582,79 +8188,79 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.3': {} - '@rollup/rollup-android-arm-eabi@4.60.3': + '@rollup/rollup-android-arm-eabi@4.60.4': optional: true - '@rollup/rollup-android-arm64@4.60.3': + '@rollup/rollup-android-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-arm64@4.60.3': + '@rollup/rollup-darwin-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-x64@4.60.3': + '@rollup/rollup-darwin-x64@4.60.4': optional: true - '@rollup/rollup-freebsd-arm64@4.60.3': + '@rollup/rollup-freebsd-arm64@4.60.4': optional: true - '@rollup/rollup-freebsd-x64@4.60.3': + '@rollup/rollup-freebsd-x64@4.60.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.3': + '@rollup/rollup-linux-arm-musleabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.3': + '@rollup/rollup-linux-arm64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.3': + '@rollup/rollup-linux-arm64-musl@4.60.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.3': + '@rollup/rollup-linux-loong64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.3': + '@rollup/rollup-linux-loong64-musl@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.3': + '@rollup/rollup-linux-ppc64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.3': + '@rollup/rollup-linux-ppc64-musl@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.3': + '@rollup/rollup-linux-riscv64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.3': + '@rollup/rollup-linux-riscv64-musl@4.60.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.3': + '@rollup/rollup-linux-s390x-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.3': + '@rollup/rollup-linux-x64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-musl@4.60.3': + '@rollup/rollup-linux-x64-musl@4.60.4': optional: true - '@rollup/rollup-openbsd-x64@4.60.3': + '@rollup/rollup-openbsd-x64@4.60.4': optional: true - '@rollup/rollup-openharmony-arm64@4.60.3': + '@rollup/rollup-openharmony-arm64@4.60.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.3': + '@rollup/rollup-win32-arm64-msvc@4.60.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.3': + '@rollup/rollup-win32-ia32-msvc@4.60.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.3': + '@rollup/rollup-win32-x64-gnu@4.60.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.3': + '@rollup/rollup-win32-x64-msvc@4.60.4': optional: true '@rtsao/scc@1.1.0': {} @@ -8676,250 +8282,41 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@smithy/chunked-blob-reader-native@4.2.3': - dependencies: - '@smithy/util-base64': 4.3.2 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader@5.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/config-resolver@4.4.17': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-config-provider': 4.2.2 - '@smithy/util-endpoints': 3.4.2 - '@smithy/util-middleware': 4.2.14 - tslib: 2.8.1 - - '@smithy/core@3.23.17': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-base64': 4.3.2 - '@smithy/util-body-length-browser': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.25 - '@smithy/util-utf8': 4.2.2 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.2.14': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - tslib: 2.8.1 - - '@smithy/eventstream-codec@4.2.14': + '@smithy/core@3.24.3': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.14.1 - '@smithy/util-hex-encoding': 4.2.2 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.14': + '@smithy/credential-provider-imds@4.3.3': dependencies: - '@smithy/eventstream-serde-universal': 4.2.14 - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.14': + '@smithy/fetch-http-handler@5.4.3': dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-node@4.2.14': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/eventstream-serde-universal@4.2.14': - dependencies: - '@smithy/eventstream-codec': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.3.17': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/querystring-builder': 4.2.14 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - tslib: 2.8.1 - - '@smithy/hash-blob-browser@4.2.15': - dependencies: - '@smithy/chunked-blob-reader': 5.2.2 - '@smithy/chunked-blob-reader-native': 4.2.3 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/hash-node@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/hash-stream-node@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/invalid-dependency@4.2.14': - dependencies: - '@smithy/types': 4.14.1 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.2': + '@smithy/node-http-handler@4.7.3': dependencies: + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@smithy/md5-js@4.2.14': + '@smithy/signature-v4@5.4.3': dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-utf8': 4.2.2 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.14': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/middleware-endpoint@4.4.32': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/middleware-serde': 4.2.20 - '@smithy/node-config-provider': 4.3.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - '@smithy/url-parser': 4.2.14 - '@smithy/util-middleware': 4.2.14 - tslib: 2.8.1 - - '@smithy/middleware-retry@4.5.7': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/node-config-provider': 4.3.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/service-error-classification': 4.3.1 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.8 - '@smithy/uuid': 1.1.2 - tslib: 2.8.1 - - '@smithy/middleware-serde@4.2.20': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/middleware-stack@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/node-config-provider@4.3.14': - dependencies: - '@smithy/property-provider': 4.2.14 - '@smithy/shared-ini-file-loader': 4.4.9 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.6.1': - dependencies: - '@smithy/protocol-http': 5.3.14 - '@smithy/querystring-builder': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/property-provider@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/protocol-http@5.3.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/querystring-builder@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - '@smithy/util-uri-escape': 4.2.2 - tslib: 2.8.1 - - '@smithy/querystring-parser@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/service-error-classification@4.3.1': - dependencies: - '@smithy/types': 4.14.1 - - '@smithy/shared-ini-file-loader@4.4.9': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/signature-v4@5.3.14': - dependencies: - '@smithy/is-array-buffer': 4.2.2 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-middleware': 4.2.14 - '@smithy/util-uri-escape': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/smithy-client@4.12.13': - dependencies: - '@smithy/core': 3.23.17 - '@smithy/middleware-endpoint': 4.4.32 - '@smithy/middleware-stack': 4.2.14 - '@smithy/protocol-http': 5.3.14 - '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.25 - tslib: 2.8.1 - - '@smithy/types@4.14.1': - dependencies: - tslib: 2.8.1 - - '@smithy/url-parser@4.2.14': - dependencies: - '@smithy/querystring-parser': 4.2.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-base64@4.3.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-body-length-browser@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-body-length-node@4.2.3': + '@smithy/types@4.14.2': dependencies: tslib: 2.8.1 @@ -8928,87 +8325,11 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.2': - dependencies: - '@smithy/is-array-buffer': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-config-provider@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-defaults-mode-browser@4.3.49': - dependencies: - '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-defaults-mode-node@4.2.54': - dependencies: - '@smithy/config-resolver': 4.4.17 - '@smithy/credential-provider-imds': 4.2.14 - '@smithy/node-config-provider': 4.3.14 - '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.13 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-endpoints@3.4.2': - dependencies: - '@smithy/node-config-provider': 4.3.14 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-hex-encoding@4.2.2': - dependencies: - tslib: 2.8.1 - - '@smithy/util-middleware@4.2.14': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-retry@4.3.8': - dependencies: - '@smithy/service-error-classification': 4.3.1 - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/util-stream@4.5.25': - dependencies: - '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.1 - '@smithy/types': 4.14.1 - '@smithy/util-base64': 4.3.2 - '@smithy/util-buffer-from': 4.2.2 - '@smithy/util-hex-encoding': 4.2.2 - '@smithy/util-utf8': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-uri-escape@4.2.2': - dependencies: - tslib: 2.8.1 - '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.2': - dependencies: - '@smithy/util-buffer-from': 4.2.2 - tslib: 2.8.1 - - '@smithy/util-waiter@4.3.0': - dependencies: - '@smithy/types': 4.14.1 - tslib: 2.8.1 - - '@smithy/uuid@1.1.2': - dependencies: - tslib: 2.8.1 - '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -9086,11 +8407,11 @@ snapshots: postcss: 8.5.14 tailwindcss: 4.3.0 - '@tanstack/query-core@5.100.9': {} + '@tanstack/query-core@5.100.10': {} - '@tanstack/react-query@5.100.9(react@19.2.5)': + '@tanstack/react-query@5.100.10(react@19.2.5)': dependencies: - '@tanstack/query-core': 5.100.9 + '@tanstack/query-core': 5.100.10 react: 19.2.5 '@testcontainers/postgresql@11.14.0': @@ -9167,13 +8488,13 @@ snapshots: '@types/docker-modem@3.0.6': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/ssh2': 1.15.5 '@types/dockerode@4.0.1': dependencies: '@types/docker-modem': 3.0.6 - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/ssh2': 1.15.5 '@types/estree@1.0.8': {} @@ -9186,15 +8507,15 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@25.6.2': + '@types/node@25.8.0': dependencies: - undici-types: 7.19.2 + undici-types: 7.24.6 '@types/pako@2.0.4': {} '@types/pg@8.20.0': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -9211,19 +8532,19 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/set-cookie-parser@2.4.10': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/ssh2-streams@0.1.13': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/ssh2@0.5.52': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@types/ssh2-streams': 0.1.13 '@types/ssh2@1.15.5': @@ -9241,7 +8562,7 @@ snapshots: '@types/web-push@3.6.4': dependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.3(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.3(jiti@2.7.0))(typescript@6.0.3)': dependencies: @@ -9393,7 +8714,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': + '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -9401,7 +8722,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) + vite: 7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) transitivePeerDependencies: - supports-color @@ -9417,7 +8738,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) + vitest: 4.1.5(@types/node@25.8.0)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3))(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) '@vitest/expect@4.1.5': dependencies: @@ -9428,14 +8749,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': + '@vitest/mocker@4.1.5(msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3))(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4))': dependencies: '@vitest/spy': 4.1.5 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.14.5(@types/node@25.6.2)(typescript@6.0.3) - vite: 7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) + msw: 2.14.5(@types/node@25.8.0)(typescript@6.0.3) + vite: 7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) '@vitest/pretty-format@4.1.5': dependencies: @@ -10666,14 +9987,15 @@ snapshots: dependencies: fast-string-width: 3.0.2 - fast-xml-builder@1.1.9: + fast-xml-builder@1.2.0: dependencies: path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 - fast-xml-parser@5.7.2: + fast-xml-parser@5.7.3: dependencies: '@nodable/entities': 2.1.0 - fast-xml-builder: 1.1.9 + fast-xml-builder: 1.2.0 path-expression-matcher: 1.5.0 strnum: 2.3.0 @@ -11445,9 +10767,9 @@ snapshots: ms@2.1.3: {} - msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3): + msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3): dependencies: - '@inquirer/confirm': 6.0.12(@types/node@25.6.2) + '@inquirer/confirm': 6.0.12(@types/node@25.8.0) '@mswjs/interceptors': 0.41.8 '@open-draft/deferred-promise': 3.0.0 '@types/statuses': 2.0.6 @@ -11504,7 +10826,7 @@ snapshots: react: 19.2.5 react-dom: 19.2.5(react@19.2.5) - next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.59.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next@16.2.6(@babel/core@7.29.0)(@playwright/test@1.60.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: '@next/env': 16.2.6 '@swc/helpers': 0.5.15 @@ -11523,7 +10845,7 @@ snapshots: '@next/swc-linux-x64-musl': 16.2.6 '@next/swc-win32-arm64-msvc': 16.2.6 '@next/swc-win32-x64-msvc': 16.2.6 - '@playwright/test': 1.59.1 + '@playwright/test': 1.60.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -11786,11 +11108,11 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - playwright-core@1.59.1: {} + playwright-core@1.60.0: {} - playwright@1.59.1: + playwright@1.60.0: dependencies: - playwright-core: 1.59.1 + playwright-core: 1.60.0 optionalDependencies: fsevents: 2.3.2 @@ -11898,7 +11220,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 25.6.2 + '@types/node': 25.8.0 long: 5.3.2 proxy-addr@2.0.7: @@ -12184,35 +11506,35 @@ snapshots: rgbcolor@1.0.1: optional: true - rollup@4.60.3: + rollup@4.60.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.3 - '@rollup/rollup-android-arm64': 4.60.3 - '@rollup/rollup-darwin-arm64': 4.60.3 - '@rollup/rollup-darwin-x64': 4.60.3 - '@rollup/rollup-freebsd-arm64': 4.60.3 - '@rollup/rollup-freebsd-x64': 4.60.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 - '@rollup/rollup-linux-arm-musleabihf': 4.60.3 - '@rollup/rollup-linux-arm64-gnu': 4.60.3 - '@rollup/rollup-linux-arm64-musl': 4.60.3 - '@rollup/rollup-linux-loong64-gnu': 4.60.3 - '@rollup/rollup-linux-loong64-musl': 4.60.3 - '@rollup/rollup-linux-ppc64-gnu': 4.60.3 - '@rollup/rollup-linux-ppc64-musl': 4.60.3 - '@rollup/rollup-linux-riscv64-gnu': 4.60.3 - '@rollup/rollup-linux-riscv64-musl': 4.60.3 - '@rollup/rollup-linux-s390x-gnu': 4.60.3 - '@rollup/rollup-linux-x64-gnu': 4.60.3 - '@rollup/rollup-linux-x64-musl': 4.60.3 - '@rollup/rollup-openbsd-x64': 4.60.3 - '@rollup/rollup-openharmony-arm64': 4.60.3 - '@rollup/rollup-win32-arm64-msvc': 4.60.3 - '@rollup/rollup-win32-ia32-msvc': 4.60.3 - '@rollup/rollup-win32-x64-gnu': 4.60.3 - '@rollup/rollup-win32-x64-msvc': 4.60.3 + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 router@2.2.0: @@ -12322,7 +11644,7 @@ snapshots: setprototypeof@1.2.0: {} - shadcn@4.7.0(@types/node@25.6.2)(typescript@6.0.3): + shadcn@4.7.0(@types/node@25.8.0)(typescript@6.0.3): dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.3 @@ -12343,7 +11665,7 @@ snapshots: fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 - msw: 2.14.5(@types/node@25.6.2)(typescript@6.0.3) + msw: 2.14.5(@types/node@25.8.0)(typescript@6.0.3) node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 @@ -12843,7 +12165,7 @@ snapshots: undici-types@5.26.5: {} - undici-types@7.19.2: {} + undici-types@7.24.6: {} undici@7.25.0: {} @@ -12948,25 +12270,25 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4): + vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.14 - rollup: 4.60.3 + rollup: 4.60.4 tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 yaml: 2.8.4 - vitest@4.1.5(@types/node@25.6.2)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)): + vitest@4.1.5(@types/node@25.8.0)(@vitest/coverage-v8@4.1.5)(msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3))(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)): dependencies: '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(msw@2.14.5(@types/node@25.6.2)(typescript@6.0.3))(vite@7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.5(msw@2.14.5(@types/node@25.8.0)(typescript@6.0.3))(vite@7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4)) '@vitest/pretty-format': 4.1.5 '@vitest/runner': 4.1.5 '@vitest/snapshot': 4.1.5 @@ -12983,10 +12305,10 @@ snapshots: tinyexec: 1.1.1 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) + vite: 7.3.1(@types/node@25.8.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.6.2 + '@types/node': 25.8.0 '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) transitivePeerDependencies: - msw @@ -13099,6 +12421,8 @@ snapshots: is-wsl: 3.1.1 powershell-utils: 0.1.0 + xml-naming@0.1.0: {} + xtend@4.0.2: {} y18n@5.0.8: {} diff --git a/prisma/migrations/0069_v054_mood_reminder/migration.sql b/prisma/migrations/0069_v054_mood_reminder/migration.sql new file mode 100644 index 000000000..5550a94ef --- /dev/null +++ b/prisma/migrations/0069_v054_mood_reminder/migration.sql @@ -0,0 +1,39 @@ +-- v0.5.4 ios-coord β€” daily mood reminder. +-- +-- Two additions: +-- 1. `users.mood_reminder_enabled` β€” per-user opt-in flag. Default +-- `false` because mood capture is an emotionally-loaded surface; +-- we never want a fresh-install user to receive a 22:00 nudge +-- without first toggling the preference in Settings. +-- 2. `mood_reminder_dispatches` β€” per-(user, local date) ledger +-- enforcing one push per day. Unique constraint doubles as the +-- idempotency anchor for parallel workers + cron re-ticks inside +-- the 22:00 window (cron fires every 15 min). +-- +-- Both additions are additive β€” existing rows stay on the +-- "opt-out-of-everything" path byte-identical to v1.4.38 behaviour. +ALTER TABLE "users" + ADD COLUMN IF NOT EXISTS "mood_reminder_enabled" BOOLEAN NOT NULL DEFAULT FALSE; + +CREATE TABLE IF NOT EXISTS "mood_reminder_dispatches" ( + "id" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "date" TEXT NOT NULL, + "dispatched_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "mood_reminder_dispatches_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX IF NOT EXISTS "mood_reminder_dispatches_user_id_date_key" + ON "mood_reminder_dispatches"("user_id", "date"); + +CREATE INDEX IF NOT EXISTS "mood_reminder_dispatches_user_id_dispatched_at_idx" + ON "mood_reminder_dispatches"("user_id", "dispatched_at"); + +DO $$ BEGIN + ALTER TABLE "mood_reminder_dispatches" + ADD CONSTRAINT "mood_reminder_dispatches_user_id_fkey" + FOREIGN KEY ("user_id") REFERENCES "users"("id") + ON DELETE CASCADE ON UPDATE CASCADE; +EXCEPTION + WHEN duplicate_object THEN NULL; +END $$; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 824bb8bf9..52d63f97a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -198,6 +198,17 @@ model User { // a full refresh on the next pair". lastSyncedAt DateTime? @map("last_synced_at") + // v0.5.4 ios-coord β€” daily mood-reminder opt-in. + // + // When `true` the reminder worker dispatches a `MOOD_REMINDER` + // notification at 22:00 in the user's local timezone if no + // `MoodEntry` row exists for the local date yet. Default `false` + // because mood capture is an emotional surface β€” the user has to + // explicitly opt in from Settings β†’ Notifications before the + // server starts nudging them. The dedup is handled by the + // `MoodReminderDispatch` table (one row per user + local date). + moodReminderEnabled Boolean @default(false) @map("mood_reminder_enabled") + // v1.4.25 W6c β€” Doctor-report section toggles. // Shape: // { "bp": boolean, @@ -267,6 +278,12 @@ model User { // granularity from the measurement write hooks; WEEK/MONTH/YEAR // are enqueued through pg-boss for the worker to fold. measurementRollups MeasurementRollup[] + // v0.5.4 ios-coord β€” per-day dedup ledger for MOOD_REMINDER pushes. + // One row per (user, local date) the worker has nudged. Prevents + // a second push when the cron rechecks the same hour later in the + // 22:00 window (the cron fires every 15 min β€” without the ledger + // a user could see three pushes back-to-back). + moodReminderDispatches MoodReminderDispatch[] @@map("users") } @@ -1173,6 +1190,27 @@ model MoodEntry { @@map("mood_entries") } +// ─── Mood Reminder Dispatch Ledger (v0.5.4 ios-coord) ───── +// +// Idempotency guard for the daily 22:00 MOOD_REMINDER push. The +// reminder cron fires every 15 min β€” when it lands inside the 22:00 +// window the handler looks up `(userId, date)` here and bails if a row +// already exists, so the user sees exactly one nudge per local day. +// `date` is a YYYY-MM-DD string anchored to the user's timezone at +// dispatch time (same encoding convention as MoodEntry.date). +model MoodReminderDispatch { + id String @id @default(cuid()) + userId String @map("user_id") + date String // YYYY-MM-DD anchored to user.timezone + dispatchedAt DateTime @default(now()) @map("dispatched_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([userId, date]) + @@index([userId, dispatchedAt]) + @@map("mood_reminder_dispatches") +} + // ─── Integration Status (v1.4.15) ───────────────────────── // // Per-(user, integration) row that captures the most recent sync attempt diff --git a/src/lib/jobs/__tests__/mood-reminder.test.ts b/src/lib/jobs/__tests__/mood-reminder.test.ts new file mode 100644 index 000000000..d6b633105 --- /dev/null +++ b/src/lib/jobs/__tests__/mood-reminder.test.ts @@ -0,0 +1,382 @@ +/** + * v0.5.4 ios-coord β€” mood-reminder dispatcher unit tests. + * + * Pins the contract that motivated the v0.5.4 server-side patch: + * - Opt-in gating: `moodReminderEnabled = false` β‡’ never dispatch. + * - Window gating: only fires inside the 22:00 local-time hour. + * - Logged-today skip: a `MoodEntry` row for the local date short- + * circuits the dispatch. + * - Idempotency: a `MoodReminderDispatch` row blocks a second push + * for the same (user, date) β€” even when the cron re-ticks inside + * the same 22:00 window. + * - Locale: title + body come from `messages/{de,en}.json` and fall + * back to the app default when the user's locale is null/unknown. + * + * The tests stub the Prisma surface manually to keep this file free of + * a testcontainer boot. + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +import { + buildMoodReminderPayload, + evaluateMoodReminderWindow, + runMoodReminderTick, + MOOD_REMINDER_LOCAL_HOUR, +} from "../mood-reminder"; +import type { NotificationPayload } from "@/lib/notifications/types"; + +type DispatchFn = (payload: NotificationPayload) => Promise; + +interface FakePrismaState { + candidates: Array<{ + id: string; + timezone: string; + locale: string | null; + moodReminderEnabled: boolean; + }>; + moodEntries: Array<{ userId: string; date: string }>; + dispatches: Array<{ userId: string; date: string }>; + /** Force the next `moodReminderDispatch.create` to throw P2002. */ + raceUserIds: Set; +} + +function makePrisma(state: FakePrismaState) { + return { + user: { + findMany: vi.fn( + async ({ where }: { where: { moodReminderEnabled: boolean } }) => { + return state.candidates.filter( + (c) => c.moodReminderEnabled === where.moodReminderEnabled, + ); + }, + ), + }, + moodEntry: { + findFirst: vi.fn( + async ({ where }: { where: { userId: string; date: string } }) => { + return ( + state.moodEntries.find( + (m) => m.userId === where.userId && m.date === where.date, + ) ?? null + ); + }, + ), + }, + moodReminderDispatch: { + findUnique: vi.fn( + async ({ + where, + }: { + where: { userId_date: { userId: string; date: string } }; + }) => { + return ( + state.dispatches.find( + (d) => + d.userId === where.userId_date.userId && + d.date === where.userId_date.date, + ) ?? null + ); + }, + ), + create: vi.fn( + async ({ data }: { data: { userId: string; date: string } }) => { + if (state.raceUserIds.has(data.userId)) { + // Mirrors the Prisma `P2002` shape closely enough that the + // handler's substring match (`includes("P2002")`) catches it. + throw new Error( + "Invalid `prisma.moodReminderDispatch.create()` invocation: " + + "Unique constraint failed on the fields: (`user_id`,`date`). " + + "code: P2002", + ); + } + state.dispatches.push(data); + return { id: `dispatch-${state.dispatches.length}`, ...data }; + }, + ), + }, + }; +} + +beforeEach(() => { + vi.useRealTimers(); +}); + +describe("evaluateMoodReminderWindow", () => { + it("returns fire=false when the user is opted out", () => { + const at22 = new Date("2026-05-17T20:00:00Z"); // 22:00 Europe/Berlin + const r = evaluateMoodReminderWindow( + { timezone: "Europe/Berlin", moodReminderEnabled: false }, + at22, + ); + expect(r.fire).toBe(false); + expect(r.localDate).toBeNull(); + }); + + it("fires inside the 22:00 hour and returns the local date string", () => { + const at22 = new Date("2026-05-17T20:00:00Z"); // 22:00 Europe/Berlin (CEST) + const r = evaluateMoodReminderWindow( + { timezone: "Europe/Berlin", moodReminderEnabled: true }, + at22, + ); + expect(r.fire).toBe(true); + expect(r.localDate).toBe("2026-05-17"); + expect(r.localHour).toBe(MOOD_REMINDER_LOCAL_HOUR); + }); + + it("does NOT fire at 21:59 local time (off-by-one guard)", () => { + const at2159 = new Date("2026-05-17T19:59:00Z"); // 21:59 Europe/Berlin + const r = evaluateMoodReminderWindow( + { timezone: "Europe/Berlin", moodReminderEnabled: true }, + at2159, + ); + expect(r.fire).toBe(false); + expect(r.localHour).toBe(21); + }); + + it("does NOT fire at 23:00 local time (window end)", () => { + const at23 = new Date("2026-05-17T21:00:00Z"); // 23:00 Europe/Berlin + const r = evaluateMoodReminderWindow( + { timezone: "Europe/Berlin", moodReminderEnabled: true }, + at23, + ); + expect(r.fire).toBe(false); + expect(r.localHour).toBe(23); + }); + + it("respects a non-Berlin timezone (America/New_York is 6h behind in DST)", () => { + // 02:00 UTC on 2026-05-18 = 22:00 EDT on 2026-05-17 (UTC-4 during DST). + const utc = new Date("2026-05-18T02:00:00Z"); + const r = evaluateMoodReminderWindow( + { timezone: "America/New_York", moodReminderEnabled: true }, + utc, + ); + expect(r.fire).toBe(true); + expect(r.localDate).toBe("2026-05-17"); + }); +}); + +describe("buildMoodReminderPayload", () => { + it("returns German strings for locale=de", () => { + const p = buildMoodReminderPayload("de"); + expect(p.title).toBe("Stimmung erfassen"); + expect(p.body).toBe("Wie geht es dir heute?"); + }); + + it("returns English strings for locale=en", () => { + const p = buildMoodReminderPayload("en"); + expect(p.title).toBe("Log your mood"); + expect(p.body).toBe("How are you feeling today?"); + }); + + it("falls back to the app default for null / unknown locales", () => { + const p1 = buildMoodReminderPayload(null); + const p2 = buildMoodReminderPayload("zz-ZZ"); + // Both should resolve to the same content (the app default). + expect(p1.title).toBe(p2.title); + expect(p1.title.length).toBeGreaterThan(0); + }); +}); + +describe("runMoodReminderTick", () => { + // 22:00 Europe/Berlin (CEST) β‡’ 20:00 UTC. + const inWindow = new Date("2026-05-17T20:00:00Z"); + // 21:00 Europe/Berlin (CEST) β‡’ 19:00 UTC. + const outOfWindow = new Date("2026-05-17T19:00:00Z"); + + it("dispatches MOOD_REMINDER for opted-in users in the 22:00 window", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-1", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + ], + moodEntries: [], + dispatches: [], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, inWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(1); + expect(summary.inWindow).toBe(1); + expect(dispatch).toHaveBeenCalledTimes(1); + const firstCall = dispatch.mock.calls[0]; + if (!firstCall) throw new Error("dispatch was not called"); + const call = firstCall[0]; + expect(call.eventType).toBe("MOOD_REMINDER"); + expect(call.userId).toBe("u-1"); + expect(call.title).toBe("Stimmung erfassen"); + expect(call.message).toBe("Wie geht es dir heute?"); + expect(call.metadata?.localDate).toBe("2026-05-17"); + expect(typeof call.metadata?.scheduledAt).toBe("string"); + expect(state.dispatches).toHaveLength(1); + expect(state.dispatches[0]).toEqual({ userId: "u-1", date: "2026-05-17" }); + }); + + it("does NOT dispatch for users outside the 22:00 window", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-1", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + ], + moodEntries: [], + dispatches: [], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, outOfWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(0); + expect(summary.skippedOutsideWindow).toBe(1); + expect(dispatch).not.toHaveBeenCalled(); + }); + + it("never reads moodReminderEnabled=false users (opt-in gating)", async () => { + const state: FakePrismaState = { + candidates: [], // Prisma `where` already filters; we assert the call shape + moodEntries: [], + dispatches: [], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + await runMoodReminderTick(prisma as never, inWindow, { dispatch }); + + expect(prisma.user.findMany).toHaveBeenCalledWith( + expect.objectContaining({ where: { moodReminderEnabled: true } }), + ); + }); + + it("skips users who already logged a mood for the local date", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-1", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + ], + moodEntries: [{ userId: "u-1", date: "2026-05-17" }], + dispatches: [], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, inWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(0); + expect(summary.skippedAlreadyLogged).toBe(1); + expect(dispatch).not.toHaveBeenCalled(); + expect(state.dispatches).toHaveLength(0); + }); + + it("idempotency: a second tick inside the same window does not re-send", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-1", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + ], + moodEntries: [], + // Existing dispatch row blocks the push. + dispatches: [{ userId: "u-1", date: "2026-05-17" }], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, inWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(0); + expect(summary.skippedAlreadyDispatched).toBe(1); + expect(dispatch).not.toHaveBeenCalled(); + }); + + it("idempotency: lost P2002 race is swallowed as 'already dispatched'", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-1", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + ], + moodEntries: [], + dispatches: [], + raceUserIds: new Set(["u-1"]), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, inWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(0); + expect(summary.skippedAlreadyDispatched).toBe(1); + expect(dispatch).not.toHaveBeenCalled(); + }); + + it("dispatches per-user with their own locale (DE + EN mix)", async () => { + const state: FakePrismaState = { + candidates: [ + { + id: "u-de", + timezone: "Europe/Berlin", + locale: "de", + moodReminderEnabled: true, + }, + { + id: "u-en", + timezone: "Europe/Berlin", + locale: "en", + moodReminderEnabled: true, + }, + ], + moodEntries: [], + dispatches: [], + raceUserIds: new Set(), + }; + const prisma = makePrisma(state); + const dispatch = vi.fn(async () => {}); + + const summary = await runMoodReminderTick(prisma as never, inWindow, { + dispatch, + }); + + expect(summary.dispatched).toBe(2); + expect(dispatch).toHaveBeenCalledTimes(2); + const titles = dispatch.mock.calls.map((c) => { + const [payload] = c; + return payload?.title ?? ""; + }); + expect(titles).toContain("Stimmung erfassen"); + expect(titles).toContain("Log your mood"); + }); +}); diff --git a/src/lib/jobs/mood-reminder.ts b/src/lib/jobs/mood-reminder.ts new file mode 100644 index 000000000..9ad52ca8a --- /dev/null +++ b/src/lib/jobs/mood-reminder.ts @@ -0,0 +1,235 @@ +/** + * v0.5.4 ios-coord β€” daily mood-reminder dispatcher. + * + * The reminder cron fires every 15 minutes (same cadence as the + * medication-reminder loop in `reminder-worker.ts`). When the cron lands + * inside the 22:00 hour in a user's local timezone, this module: + * + * 1. Skips users who haven't opted in (`User.moodReminderEnabled = false`). + * 2. Skips users who already logged a mood entry for the local date. + * 3. Skips users who already received a mood reminder today + * (idempotency via `MoodReminderDispatch` row). + * 4. Dispatches a `MOOD_REMINDER` notification with a locale-aware + * title + body and records the dispatch ledger row in the same + * transaction so a concurrent worker can't double-fire. + * + * Why a separate module instead of folding it into reminder-worker.ts: + * the worker is already 1800 LOC. Splitting the mood-reminder logic out + * keeps the test surface focused and lets the unit tests run without + * booting pg-boss. + */ +import type { PrismaClient } from "@/generated/prisma/client"; +import { getServerTranslator } from "@/lib/i18n/server-translator"; +import type { Locale } from "@/lib/i18n/config"; +import { defaultLocale } from "@/lib/i18n/config"; +import { getLocalDateParts } from "@/lib/timezone"; +import { dispatchNotification } from "@/lib/notifications/dispatcher"; + +/** + * Hour of day (in the user's local timezone) at which the mood reminder + * should fire. 22:00 is late enough that a "did you forget to log?" nudge + * lands in the user's wind-down window without colliding with dinner or + * the medication-reminder hot path. + * + * Exported so the unit tests can pin the contract without scraping the + * handler internals. + */ +export const MOOD_REMINDER_LOCAL_HOUR = 22; + +/** + * Minimal user shape needed to decide whether to dispatch. Defined + * explicitly so the handler can run against either the global Prisma + * client or the per-worker singleton without re-deriving the type. + */ +export interface MoodReminderCandidate { + id: string; + timezone: string; + locale: string | null; + moodReminderEnabled: boolean; +} + +export interface MoodReminderSummary { + candidatesScanned: number; + inWindow: number; + dispatched: number; + skippedAlreadyLogged: number; + skippedAlreadyDispatched: number; + skippedOutsideWindow: number; +} + +function resolveLocale(locale: string | null | undefined): Locale { + return locale === "en" || locale === "de" ? locale : defaultLocale; +} + +/** + * Pure decision predicate: should the worker fire a mood reminder for + * this user at this instant? + * + * Returns the local YYYY-MM-DD date string when the answer is "fire"; + * returns `null` when the answer is "skip" β€” covers both + * "outside-window" and "opt-out". Callers branch on the return value. + * + * Pulled out as a pure helper so the unit tests can pin the + * window-boundary contract (21:59 β†’ null, 22:00 β†’ date, 22:59 β†’ date, + * 23:00 β†’ null) without touching the DB. + */ +export function evaluateMoodReminderWindow( + user: Pick, + now: Date, +): { fire: boolean; localDate: string | null; localHour: number } { + if (!user.moodReminderEnabled) { + return { fire: false, localDate: null, localHour: -1 }; + } + const parts = getLocalDateParts(now, user.timezone || "Europe/Berlin"); + const inWindow = parts.hour === MOOD_REMINDER_LOCAL_HOUR; + const isoDate = `${parts.year.toString().padStart(4, "0")}-${parts.month + .toString() + .padStart(2, "0")}-${parts.day.toString().padStart(2, "0")}`; + return { + fire: inWindow, + localDate: inWindow ? isoDate : null, + localHour: parts.hour, + }; +} + +/** + * Build the localised title + body for the mood reminder push. Pulled + * out so a future template change doesn't require re-running the whole + * handler in tests. + */ +export function buildMoodReminderPayload(locale: string | null | undefined): { + title: string; + body: string; +} { + const t = getServerTranslator(resolveLocale(locale)).t; + return { + title: t("moodReminders.dailyTitle"), + body: t("moodReminders.dailyBody"), + }; +} + +/** + * Run one mood-reminder cron tick. Iterates every user with + * `moodReminderEnabled = true`, checks the per-user local-time window, + * and dispatches a `MOOD_REMINDER` notification when warranted. + * + * The dedup ledger (`MoodReminderDispatch`) is the idempotency anchor: + * the `(userId, date)` unique constraint guarantees that two parallel + * workers (or a re-tick inside the same hour) won't double-fire. + * + * Returns a summary so the worker can attach it to the wide-event for + * the observability dashboards. + */ +export async function runMoodReminderTick( + prisma: PrismaClient, + now: Date, + options: { + dispatch?: typeof dispatchNotification; + } = {}, +): Promise { + const dispatchImpl = options.dispatch ?? dispatchNotification; + + const summary: MoodReminderSummary = { + candidatesScanned: 0, + inWindow: 0, + dispatched: 0, + skippedAlreadyLogged: 0, + skippedAlreadyDispatched: 0, + skippedOutsideWindow: 0, + }; + + const candidates = await prisma.user.findMany({ + where: { moodReminderEnabled: true }, + select: { + id: true, + timezone: true, + locale: true, + moodReminderEnabled: true, + }, + }); + + for (const user of candidates) { + summary.candidatesScanned += 1; + const decision = evaluateMoodReminderWindow( + { + timezone: user.timezone, + moodReminderEnabled: user.moodReminderEnabled, + }, + now, + ); + + if (!decision.fire || !decision.localDate) { + summary.skippedOutsideWindow += 1; + continue; + } + + summary.inWindow += 1; + + // Already nudged today? The dedup row is the source of truth β€” if + // a previous tick succeeded, this branch swallows the work. We + // check before the MoodEntry lookup because the ledger query is + // strictly cheaper (single indexed row) than scanning today's + // mood entries. + const existingDispatch = await prisma.moodReminderDispatch.findUnique({ + where: { userId_date: { userId: user.id, date: decision.localDate } }, + select: { id: true }, + }); + if (existingDispatch) { + summary.skippedAlreadyDispatched += 1; + continue; + } + + // Skip when the user already logged a mood for the local date. Same + // anchoring convention as `MoodEntry.date` (YYYY-MM-DD in the user's + // current timezone). Legacy rows that pre-date the `tz` column are + // already pinned to Europe/Berlin via the storage convention; here + // we accept whatever `date` string the row carries because the + // user-experience contract is "I logged today, don't nudge me". + const existingMood = await prisma.moodEntry.findFirst({ + where: { userId: user.id, date: decision.localDate }, + select: { id: true }, + }); + if (existingMood) { + summary.skippedAlreadyLogged += 1; + continue; + } + + // Reserve the dedup row FIRST so a parallel worker that races us + // hits the unique-constraint violation and bails before fanning a + // duplicate push. We catch the P2002 unique-violation path + // explicitly because a "lost the race" outcome is not a worker + // error β€” it's the idempotency contract doing its job. + try { + await prisma.moodReminderDispatch.create({ + data: { userId: user.id, date: decision.localDate }, + }); + } catch (err: unknown) { + // Prisma encodes unique-violation as `code === 'P2002'`. Detect + // by string-includes rather than instanceof so this module + // doesn't pull in the Prisma runtime types just for the guard. + const message = err instanceof Error ? err.message : String(err); + if (message.includes("P2002") || message.includes("Unique constraint")) { + summary.skippedAlreadyDispatched += 1; + continue; + } + throw err; + } + + const { title, body } = buildMoodReminderPayload(user.locale); + + await dispatchImpl({ + eventType: "MOOD_REMINDER", + userId: user.id, + title, + message: body, + metadata: { + scheduledAt: now.toISOString(), + localDate: decision.localDate, + }, + }); + + summary.dispatched += 1; + } + + return summary; +} diff --git a/src/lib/jobs/reminder-worker.ts b/src/lib/jobs/reminder-worker.ts index fe422d7a0..b03c24c62 100644 --- a/src/lib/jobs/reminder-worker.ts +++ b/src/lib/jobs/reminder-worker.ts @@ -83,6 +83,7 @@ import { getPhaseMessage, getPhaseKeyboard, } from "@/lib/jobs/reminder-phases"; +import { runMoodReminderTick } from "@/lib/jobs/mood-reminder"; import { withBackgroundEvent } from "@/lib/logging/background"; import { assertSubsystemEnabled } from "@/lib/process-type"; import { runOffhostBackup } from "@/lib/jobs/offhost-backup"; @@ -179,6 +180,17 @@ const FEEDBACK_AGGREGATOR_CRON = "0 4 * * *"; // fall to the drain. const DRAIN_CUMULATIVE_QUEUE = "drain-per-sample-cumulative"; const DRAIN_CUMULATIVE_CRON = "45 3 * * *"; +// v0.5.4 ios-coord β€” daily mood-reminder cron. +// +// Runs every 15 minutes so the handler can pick up users whose local +// time has just crossed 22:00 across any IANA timezone without having +// to schedule one cron entry per zone. The handler short-circuits when +// the user's local hour isn't 22, so the 15-min cadence translates to +// ~4 ticks-per-hour Γ— 1 actual-dispatch-window-per-user = at most one +// push per user per day. Idempotency is enforced by the +// `MoodReminderDispatch` ledger inside the handler. +const MOOD_REMINDER_QUEUE = "mood-reminder-check"; +const MOOD_REMINDER_CRON = "*/15 * * * *"; // v1.4.38 β€” the per-sample cutoff hours constant now lives on the // helper module so the worker, the admin route, and the CLI all read // the same source of truth. Re-export pulled in alongside @@ -281,6 +293,10 @@ interface GeoBackfillPayload { triggeredAt: string; } +interface MoodReminderPayload { + triggeredAt: string; +} + // Re-export timezone utilities under local names for backward compatibility const getUserTodayBounds = getUserTodayBoundsUtil; const getDayOfWeekInTz = getDayOfWeekInTzUtil; @@ -559,6 +575,17 @@ async function handleReminderCheck(jobs: Job[]) { med.user.locale, ); + // v0.5.4 β€” surface the window-start as an ISO 8601 string so + // the iOS notification handler can pin a "snooze 15 min" + // action against the actual schedule slot rather than the + // wall-clock moment APNs delivered. Computed in UTC from the + // user's localised window-start; mirrors the RED-phase + // `scheduledFor` field on the intake event. + const [winH, winM] = schedule.windowStart.split(":").map(Number); + const scheduledAtIso = new Date( + todayStart.getTime() + winH * 3600000 + winM * 60000, + ).toISOString(); + evt.addMeta( "notification_phase", `${currentPhase}:${med.name}:${schedule.windowStart}-${schedule.windowEnd}`, @@ -575,6 +602,7 @@ async function handleReminderCheck(jobs: Job[]) { scheduleId: schedule.id, phase: currentPhase, date: localDateStr, + scheduledAt: scheduledAtIso, replyMarkup: keyboard, }, }); @@ -717,9 +745,7 @@ async function handleWithingsActivitySync( * activity handler: per-user when the webhook fires, full-iteration * when the cron ticks. */ -async function handleWithingsSleepSync( - jobs: Job[], -) { +async function handleWithingsSleepSync(jobs: Job[]) { await withBackgroundEvent("job.withings_sleep_sync", async (evt) => { const prisma = getWorkerPrisma(); try { @@ -1107,6 +1133,39 @@ async function handleMoodLogSync(jobs: Job[]) { }); } +/** + * v0.5.4 ios-coord β€” daily mood-reminder dispatcher. + * + * Delegates the dispatch decision to `runMoodReminderTick` in + * `mood-reminder.ts` so the unit tests can exercise the logic without + * spinning up pg-boss. The handler is a thin shim that wires the worker + * Prisma singleton + the wide-event sink to the pure function. + */ +async function handleMoodReminderCheck(jobs: Job[]) { + void jobs; + await withBackgroundEvent("job.mood_reminder", async (evt) => { + const prisma = getWorkerPrisma(); + try { + const summary = await runMoodReminderTick(prisma, new Date()); + evt.setBackground({ + task_name: "job.mood_reminder", + result: { + candidates_scanned: summary.candidatesScanned, + in_window: summary.inWindow, + dispatched: summary.dispatched, + skipped_already_logged: summary.skippedAlreadyLogged, + skipped_already_dispatched: summary.skippedAlreadyDispatched, + skipped_outside_window: summary.skippedOutsideWindow, + }, + }); + } catch (err) { + evt.setError(err); + recordError(); + throw err; + } + }); +} + async function handleRateLimitCleanup(jobs: Job[]) { void jobs; await withBackgroundEvent("job.rate_limit_cleanup", async (evt) => { @@ -1248,8 +1307,10 @@ async function handlePrDetection( // users in that case. The push-suppression flag is irrelevant // for the cron path (the dispatcher's per-user opt-in handles // the loud/quiet decision once the row is written). - const payloadUserId = (job.data as PrDetectionPayload | undefined)?.userId; - const silent = (job.data as PrDetectionPayload | undefined)?.silent ?? false; + const payloadUserId = (job.data as PrDetectionPayload | undefined) + ?.userId; + const silent = + (job.data as PrDetectionPayload | undefined)?.silent ?? false; const userIds: string[] = payloadUserId ? [payloadUserId] : (await p.user.findMany({ select: { id: true } })).map((u) => u.id); @@ -1560,6 +1621,11 @@ export async function startReminderWorker() { // this entry the drain schedule silently no-ops and the // per-sample APPLE_HEALTH rows never collapse. DRAIN_CUMULATIVE_QUEUE, + // v0.5.4 ios-coord β€” mood-reminder cron tick. Same pg-boss v12 + // createQueue contract as the drain queue above; without this + // entry the every-15-min schedule silently no-ops and the + // dispatcher never fires. + MOOD_REMINDER_QUEUE, ]; for (const q of allQueues) { @@ -1573,11 +1639,7 @@ export async function startReminderWorker() { try { await reconcileOrphanImportJobs(); } catch (err) { - workerLog( - "error", - "Failed to reconcile orphan ImportJob rows", - err, - ); + workerLog("error", "Failed to reconcile orphan ImportJob rows", err); } // Schedule recurring cron jobs @@ -1620,6 +1682,11 @@ export async function startReminderWorker() { // rows into one row per day per type. Slots between the // audit-log cleanup (03:15) and the feedback aggregator (04:00). [DRAIN_CUMULATIVE_QUEUE, DRAIN_CUMULATIVE_CRON], + // v0.5.4 ios-coord β€” every-15-min tick for the daily mood reminder. + // The handler short-circuits unless the candidate user's local + // time is the 22:00 hour, so the cron costs ~one user-row scan + // per tick for the entire opted-in cohort. + [MOOD_REMINDER_QUEUE, MOOD_REMINDER_CRON], ]; for (const [name, cron] of schedules) { @@ -1727,6 +1794,15 @@ export async function startReminderWorker() { { localConcurrency: 1 }, handleGeoBackfill, ); + // v0.5.4 ios-coord β€” single-flight worker. localConcurrency=1 keeps + // two reminder ticks from interleaving against the same user row; + // the dedup ledger would still save us, but skipping the race here + // avoids spurious P2002 errors in the wide-event log. + await boss.work( + MOOD_REMINDER_QUEUE, + { localConcurrency: 1 }, + handleMoodReminderCheck, + ); await boss.work( PR_DETECTION_QUEUE, { localConcurrency: PR_DETECTION_CONCURRENCY }, diff --git a/src/lib/notifications/senders/__tests__/apns.test.ts b/src/lib/notifications/senders/__tests__/apns.test.ts index bc73f7930..151677353 100644 --- a/src/lib/notifications/senders/__tests__/apns.test.ts +++ b/src/lib/notifications/senders/__tests__/apns.test.ts @@ -39,6 +39,8 @@ vi.mock("@parse/node-apn", () => { public threadId: string | undefined; public collapseId: string | undefined; public payload: unknown; + public category: string | undefined; + public mutableContent: boolean | undefined; constructor() { notificationCtorMock(); } @@ -419,4 +421,68 @@ describe("sendViaApns β€” dispatcher fan-out", () => { const note = sendMock.mock.calls[0][0]; expect(note.alert.body).toBe("take meds"); }); + + it("forwards eventType as aps.category so iOS renders action buttons", async () => { + // Wires the v0.5.4 contract: MEDICATION_REMINDER on the server must + // surface as `aps.category = "MEDICATION_REMINDER"` so iOS looks up + // the UNNotificationCategory the app registered at launch and + // renders Take / Snooze 15 / Skip on the lock-screen. + vi.mocked(prisma.device.findMany).mockResolvedValueOnce([ + { id: "d1", apnsToken: "tok-a", apnsEnvironment: "sandbox" }, + ] as never); + sendMock.mockResolvedValueOnce({ sent: [{ device: "tok-a" }], failed: [] }); + await sendViaApns("u-1", { + title: "t", + message: "m", + eventType: "MEDICATION_REMINDER", + metadata: { medicationId: "med-1", scheduleId: "sched-1" }, + }); + const note = sendMock.mock.calls[0][0]; + expect(note.category).toBe("MEDICATION_REMINDER"); + expect(note.mutableContent).toBe(true); + expect(note.threadId).toBe("MEDICATION_REMINDER"); + expect(note.payload.medicationId).toBe("med-1"); + expect(note.payload.scheduleId).toBe("sched-1"); + expect(note.payload.eventType).toBe("MEDICATION_REMINDER"); + }); + + it("forwards MOOD_REMINDER eventType as aps.category too", async () => { + // SR-2 contract: the mood-reminder daily job emits this event-type; + // the iOS app registers a `MOOD_REMINDER` category whose only + // action opens the mood-entry sheet. + vi.mocked(prisma.device.findMany).mockResolvedValueOnce([ + { id: "d1", apnsToken: "tok-a", apnsEnvironment: "sandbox" }, + ] as never); + sendMock.mockResolvedValueOnce({ sent: [{ device: "tok-a" }], failed: [] }); + await sendViaApns("u-1", { + title: "Stimmung erfassen", + message: "Wie geht es dir heute?", + eventType: "MOOD_REMINDER", + metadata: { scheduledAt: "2026-05-17T20:00:00.000Z" }, + }); + const note = sendMock.mock.calls[0][0]; + expect(note.category).toBe("MOOD_REMINDER"); + expect(note.threadId).toBe("MOOD_REMINDER"); + expect(note.payload.scheduledAt).toBe("2026-05-17T20:00:00.000Z"); + }); + + it("forwards explicit category override on sendApnsPush", async () => { + // Lower-level entry β€” directly setting `payload.category` must + // propagate to `note.category` so callers that bypass `sendViaApns` + // (e.g. an admin test endpoint) keep full control. + setEnv(VALID_ENV); + sendMock.mockResolvedValueOnce({ sent: [{ device: "abc" }], failed: [] }); + await sendApnsPush({ + deviceToken: "abc", + environment: "sandbox", + payload: { + alert: { title: "t", body: "b" }, + category: "CUSTOM_CATEGORY", + mutableContent: true, + }, + }); + const note = sendMock.mock.calls[0][0]; + expect(note.category).toBe("CUSTOM_CATEGORY"); + expect(note.mutableContent).toBe(true); + }); }); diff --git a/src/lib/notifications/senders/apns.ts b/src/lib/notifications/senders/apns.ts index aa4afeac2..cce0a0d20 100644 --- a/src/lib/notifications/senders/apns.ts +++ b/src/lib/notifications/senders/apns.ts @@ -40,6 +40,25 @@ export interface ApnsPayload { data?: Record; /** APNs `aps.thread-id` β€” groups related alerts in Notification Center. */ threadId?: string; + /** + * APNs `aps.category` β€” names a `UNNotificationCategory` registered by + * the iOS app at launch. The category drives the action-button row on + * the lock-screen / Notification Center entry. iOS HealthLog v0.5.3 + * registers `MEDICATION_REMINDER` (Take / Snooze 15 / Skip) and + * `MOOD_REMINDER` (Log mood). Omit the field for plain alerts without + * actions. + */ + category?: string; + /** + * Sets `aps.mutable-content = 1`. iOS routes the payload through the + * app's Notification Service Extension before the system renders it, + * giving the extension a chance to enrich the alert (e.g. download a + * thumbnail, decrypt a body). HealthLog doesn't ship an NSE yet, but + * the flag is cheap to set and a no-op without one β€” wiring it now + * means the iOS team can drop in an NSE later without a server-side + * roundtrip. + */ + mutableContent?: boolean; } export interface ApnsSendInput { @@ -229,6 +248,27 @@ export async function sendApnsPush( if (input.payload.threadId) { note.threadId = input.payload.threadId; } + if (input.payload.category) { + // node-apn's `category` setter writes through to `aps.category` β€” + // that's the key the iOS notification system reads to look up a + // `UNNotificationCategory` and render its action-button row. The + // category name is a plain string that must match an identifier + // registered by the iOS app at launch (see iOS HealthLog v0.5.3 + // `NotificationCategories`). + // + // The cast is because node-apn 8.1's d.ts file omits the public + // setter (it only documents the matching `aps.category` field on + // the `Aps` interface), but the runtime setter on + // `apsProperties.js` writes the value through unchanged. We assign + // via the `category` setter rather than poking `note.aps.category` + // directly so we stay forward-compatible with future d.ts fixes. + (note as unknown as { category: string }).category = input.payload.category; + } + if (input.payload.mutableContent) { + // node-apn writes this through to `aps.mutable-content = 1`, which + // unlocks the iOS Notification Service Extension hook. + note.mutableContent = true; + } if (input.collapseId) { note.collapseId = input.collapseId; } @@ -356,6 +396,17 @@ export async function sendViaApns( ...(payload.metadata ?? {}), }, threadId: payload.eventType, + // The iOS app registers a `UNNotificationCategory` per event-type + // so the same string doubles as the category identifier. iOS + // ignores categories it doesn't know about, so adding the key + // here is safe for event-types that don't have an actionable + // category registered yet β€” they fall back to a plain alert. + category: payload.eventType, + // Future-proof for an iOS Notification Service Extension. iOS + // ignores the flag when no extension is registered, so it's a + // no-op on today's binary but unblocks NSE work without a + // server-side change. + mutableContent: true, }, collapseId: payload.eventType, }); diff --git a/src/lib/notifications/types.ts b/src/lib/notifications/types.ts index 241a6fac0..4b0bc9676 100644 --- a/src/lib/notifications/types.ts +++ b/src/lib/notifications/types.ts @@ -17,6 +17,12 @@ export const EVENT_TYPES = [ // below) so a multi-year Apple Health backfill doesn't fire hundreds // of pushes during initial sync. "PERSONAL_RECORD", + // v0.5.4 ios-coord β€” daily mood-reminder nudge. Default OFF on the + // per-event policy (see DEFAULT_EVENT_PREFS) and additionally gated + // on the per-user `moodReminderEnabled` flag, so a user has to opt + // in twice before the server starts nudging them about an + // emotionally-loaded surface. + "MOOD_REMINDER", ] as const; export type EventType = (typeof EVENT_TYPES)[number]; @@ -38,6 +44,11 @@ export const EVENT_DEFAULT_ENABLED: Record = { WITHINGS_SYNC_FAILED: true, SYSTEM_ALERT: true, PERSONAL_RECORD: false, + // v0.5.4 ios-coord β€” see note above on `EVENT_TYPES`. Default OFF + // is the safer posture: even if a future surface forgets to gate on + // `User.moodReminderEnabled`, this per-event default also has to be + // flipped on before the dispatcher will surface the channel. + MOOD_REMINDER: false, }; export const CHANNEL_TYPE_LABELS: Record = {