From eb1b021f00a5d7155bc9a6e09d4308313e516486 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:04:33 +0000 Subject: [PATCH 01/59] chore(deps): bump actions/setup-python from 5 to 6 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 135d926d..8a497a4b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.x" From df530e8c1f4e613572d2a7bf7d6207c208248f2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:04:38 +0000 Subject: [PATCH 02/59] chore(deps): bump anchore/sbom-action from 0.23.1 to 0.24.0 Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.23.1 to 0.24.0. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/v0.23.1...v0.24.0) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-version: 0.24.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb3fe6fb..e1d9495d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -105,7 +105,7 @@ jobs: - name: Generate SBOM if: steps.tag.outputs.should_release == 'true' - uses: anchore/sbom-action@v0.23.1 + uses: anchore/sbom-action@v0.24.0 with: image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.version }} artifact-name: sbom-tracker-tracker.spdx.json From c87bf981977886e342d0bdb0de1f7d388264a58d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:04:43 +0000 Subject: [PATCH 03/59] chore(deps): bump marocchino/sticky-pull-request-comment from 2 to 3 Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2 to 3. - [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases) - [Commits](https://github.com/marocchino/sticky-pull-request-comment/compare/v2...v3) --- updated-dependencies: - dependency-name: marocchino/sticky-pull-request-comment dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/knip-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/knip-report.yml b/.github/workflows/knip-report.yml index cbebce4a..85bad135 100644 --- a/.github/workflows/knip-report.yml +++ b/.github/workflows/knip-report.yml @@ -105,7 +105,7 @@ jobs: fi - name: Post PR Comment - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@v3 with: header: knip-report path: knip-report.md From a42a8f8667abc8ddeafdd1ab7769592e3ee1cb7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:05:15 +0000 Subject: [PATCH 04/59] chore(deps): bump @tanstack/react-query from 5.91.2 to 5.95.0 Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.91.2 to 5.95.0. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.95.0/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.95.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 26 +++++++++----------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 09df15c7..7694c1a1 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.9.0", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.91.2", + "@tanstack/react-query": "^5.95.0", "argon2": "^0.44.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21b10099..9971d134 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.2.2) '@tanstack/react-query': - specifier: ^5.91.2 - version: 5.91.2(react@19.2.4) + specifier: ^5.95.0 + version: 5.95.0(react@19.2.4) argon2: specifier: ^0.44.0 version: 0.44.0 @@ -407,9 +407,6 @@ packages: '@emnapi/core@1.9.1': resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - '@emnapi/runtime@1.9.0': - resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} - '@emnapi/runtime@1.9.1': resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} @@ -1336,11 +1333,11 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - '@tanstack/query-core@5.91.2': - resolution: {integrity: sha512-Uz2pTgPC1mhqrrSGg18RKCWT/pkduAYtxbcyIyKBhw7dTWjXZIzqmpzO2lBkyWr4hlImQgpu1m1pei3UnkFRWw==} + '@tanstack/query-core@5.95.0': + resolution: {integrity: sha512-H1/CWCe8tGL3YIVeo770Z6kPbt0B3M1d/iQXIIK1qlFiFt6G2neYdkHgLapOC8uMYNt9DmHjmGukEKgdMk1P+A==} - '@tanstack/react-query@5.91.2': - resolution: {integrity: sha512-GClLPzbM57iFXv+FlvOUL56XVe00PxuTaVEyj1zAObhRiKF008J5vedmaq7O6ehs+VmPHe8+PUQhMuEyv8d9wQ==} + '@tanstack/react-query@5.95.0': + resolution: {integrity: sha512-EMP8B+BK9zvnAemT8M/y3z/WO0NjZ7fIUY3T3wnHYK6AA3qK/k33i7tPgCXCejhX0cd4I6bJIXN2GmjrHjDBzg==} peerDependencies: react: ^18 || ^19 @@ -3722,11 +3719,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.0': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 @@ -4289,11 +4281,11 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tanstack/query-core@5.91.2': {} + '@tanstack/query-core@5.95.0': {} - '@tanstack/react-query@5.91.2(react@19.2.4)': + '@tanstack/react-query@5.95.0(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.91.2 + '@tanstack/query-core': 5.95.0 react: 19.2.4 '@testing-library/dom@10.4.1': From bdfca1f2e5716d86e2db6f277cfb361321595d75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:06:01 +0000 Subject: [PATCH 05/59] chore(deps): bump jose from 6.2.1 to 6.2.2 Bumps [jose](https://github.com/panva/jose) from 6.2.1 to 6.2.2. - [Release notes](https://github.com/panva/jose/releases) - [Changelog](https://github.com/panva/jose/blob/main/CHANGELOG.md) - [Commits](https://github.com/panva/jose/compare/v6.2.1...v6.2.2) --- updated-dependencies: - dependency-name: jose dependency-version: 6.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 09df15c7..e1c3c8af 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "echarts-gl": "^2.0.9", "emoji-picker-react": "^4.18.0", "https-proxy-agent": "^8.0.0", - "jose": "^6.2.1", + "jose": "^6.2.2", "next": "16.2.0", "node-cron": "^4.2.1", "otpauth": "^9.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21b10099..7c532993 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,8 +60,8 @@ importers: specifier: ^8.0.0 version: 8.0.0 jose: - specifier: ^6.2.1 - version: 6.2.1 + specifier: ^6.2.2 + version: 6.2.2 next: specifier: 16.2.0 version: 16.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -407,9 +407,6 @@ packages: '@emnapi/core@1.9.1': resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - '@emnapi/runtime@1.9.0': - resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} - '@emnapi/runtime@1.9.1': resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} @@ -2279,8 +2276,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.1: - resolution: {integrity: sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -3722,11 +3719,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.0': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 @@ -5161,7 +5153,7 @@ snapshots: jiti@2.6.1: {} - jose@6.2.1: {} + jose@6.2.2: {} joycon@3.1.1: {} From 9250d58a8ee983be1c8e4bbde8d6b340e97c5a17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 08:16:23 +0000 Subject: [PATCH 06/59] chore(deps): bump next from 16.2.0 to 16.2.1 Bumps [next](https://github.com/vercel/next.js) from 16.2.0 to 16.2.1. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v16.2.0...v16.2.1) --- updated-dependencies: - dependency-name: next dependency-version: 16.2.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 98 +++++++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index 0099cd59..a2aa4ba8 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "emoji-picker-react": "^4.18.0", "https-proxy-agent": "^8.0.0", "jose": "^6.2.2", - "next": "16.2.0", + "next": "16.2.1", "node-cron": "^4.2.1", "otpauth": "^9.5.0", "pino": "^10.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4110e457..a5e4ffff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: specifier: ^6.2.2 version: 6.2.2 next: - specifier: 16.2.0 - version: 16.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: 16.2.1 + version: 16.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) node-cron: specifier: ^4.2.1 version: 4.2.1 @@ -768,57 +768,57 @@ packages: '@napi-rs/wasm-runtime@1.1.1': resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} - '@next/env@16.2.0': - resolution: {integrity: sha512-OZIbODWWAi0epQRCRjNe1VO45LOFBzgiyqmTLzIqWq6u1wrxKnAyz1HH6tgY/Mc81YzIjRPoYsPAEr4QV4l9TA==} + '@next/env@16.2.1': + resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==} - '@next/swc-darwin-arm64@16.2.0': - resolution: {integrity: sha512-/JZsqKzKt01IFoiLLAzlNqys7qk2F3JkcUhj50zuRhKDQkZNOz9E5N6wAQWprXdsvjRP4lTFj+/+36NSv5AwhQ==} + '@next/swc-darwin-arm64@16.2.1': + resolution: {integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.0': - resolution: {integrity: sha512-/hV8erWq4SNlVgglUiW5UmQ5Hwy5EW/AbbXlJCn6zkfKxTy/E/U3V8U1Ocm2YCTUoFgQdoMxRyRMOW5jYy4ygg==} + '@next/swc-darwin-x64@16.2.1': + resolution: {integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.0': - resolution: {integrity: sha512-GkjL/Q7MWOwqWR9zoxu1TIHzkOI2l2BHCf7FzeQG87zPgs+6WDh+oC9Sw9ARuuL/FUk6JNCgKRkA6rEQYadUaw==} + '@next/swc-linux-arm64-gnu@16.2.1': + resolution: {integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.0': - resolution: {integrity: sha512-1ffhC6KY5qWLg5miMlKJp3dZbXelEfjuXt1qcp5WzSCQy36CV3y+JT7OC1WSFKizGQCDOcQbfkH/IjZP3cdRNA==} + '@next/swc-linux-arm64-musl@16.2.1': + resolution: {integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.0': - resolution: {integrity: sha512-FmbDcZQ8yJRq93EJSL6xaE0KK/Rslraf8fj1uViGxg7K4CKBCRYSubILJPEhjSgZurpcPQq12QNOJQ0DRJl6Hg==} + '@next/swc-linux-x64-gnu@16.2.1': + resolution: {integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.0': - resolution: {integrity: sha512-HzjIHVkmGAwRbh/vzvoBWWEbb8BBZPxBvVbDQDvzHSf3D8RP/4vjw7MNLDXFF9Q1WEzeQyEj2zdxBtVAHu5Oyw==} + '@next/swc-linux-x64-musl@16.2.1': + resolution: {integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.0': - resolution: {integrity: sha512-UMiFNQf5H7+1ZsZPxEsA064WEuFbRNq/kEXyepbCnSErp4f5iut75dBA8UeerFIG3vDaQNOfCpevnERPp2V+nA==} + '@next/swc-win32-arm64-msvc@16.2.1': + resolution: {integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.0': - resolution: {integrity: sha512-DRrNJKW+/eimrZgdhVN1uvkN1OI4j6Lpefwr44jKQ0YQzztlmOBUUzHuV5GxOMPK3nmodAYElUVCY8ZXo/IWeA==} + '@next/swc-win32-x64-msvc@16.2.1': + resolution: {integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1523,8 +1523,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.10.9: - resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} + baseline-browser-mapping@2.10.10: + resolution: {integrity: sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1553,8 +1553,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001780: - resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} + caniuse-lite@1.0.30001781: + resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2648,8 +2648,8 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next@16.2.0: - resolution: {integrity: sha512-NLBVrJy1pbV1Yn00L5sU4vFyAHt5XuSjzrNyFnxo6Com0M0KrL6hHM5B99dbqXb2bE9pm4Ow3Zl1xp6HVY9edQ==} + next@16.2.1: + resolution: {integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -3950,30 +3950,30 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.2.0': {} + '@next/env@16.2.1': {} - '@next/swc-darwin-arm64@16.2.0': + '@next/swc-darwin-arm64@16.2.1': optional: true - '@next/swc-darwin-x64@16.2.0': + '@next/swc-darwin-x64@16.2.1': optional: true - '@next/swc-linux-arm64-gnu@16.2.0': + '@next/swc-linux-arm64-gnu@16.2.1': optional: true - '@next/swc-linux-arm64-musl@16.2.0': + '@next/swc-linux-arm64-musl@16.2.1': optional: true - '@next/swc-linux-x64-gnu@16.2.0': + '@next/swc-linux-x64-gnu@16.2.1': optional: true - '@next/swc-linux-x64-musl@16.2.0': + '@next/swc-linux-x64-musl@16.2.1': optional: true - '@next/swc-win32-arm64-msvc@16.2.0': + '@next/swc-win32-arm64-msvc@16.2.1': optional: true - '@next/swc-win32-x64-msvc@16.2.0': + '@next/swc-win32-x64-msvc@16.2.1': optional: true '@noble/hashes@2.0.1': {} @@ -4476,7 +4476,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.10.9: {} + baseline-browser-mapping@2.10.10: {} bidi-js@1.0.3: dependencies: @@ -4503,7 +4503,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001780: {} + caniuse-lite@1.0.30001781: {} ccount@2.0.1: {} @@ -5720,25 +5720,25 @@ snapshots: neo-async@2.6.2: {} - next@16.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - '@next/env': 16.2.0 + '@next/env': 16.2.1 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.9 - caniuse-lite: 1.0.30001780 + baseline-browser-mapping: 2.10.10 + caniuse-lite: 1.0.30001781 postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) styled-jsx: 5.1.6(react@19.2.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.0 - '@next/swc-darwin-x64': 16.2.0 - '@next/swc-linux-arm64-gnu': 16.2.0 - '@next/swc-linux-arm64-musl': 16.2.0 - '@next/swc-linux-x64-gnu': 16.2.0 - '@next/swc-linux-x64-musl': 16.2.0 - '@next/swc-win32-arm64-msvc': 16.2.0 - '@next/swc-win32-x64-msvc': 16.2.0 + '@next/swc-darwin-arm64': 16.2.1 + '@next/swc-darwin-x64': 16.2.1 + '@next/swc-linux-arm64-gnu': 16.2.1 + '@next/swc-linux-arm64-musl': 16.2.1 + '@next/swc-linux-x64-gnu': 16.2.1 + '@next/swc-linux-x64-musl': 16.2.1 + '@next/swc-win32-arm64-msvc': 16.2.1 + '@next/swc-win32-x64-msvc': 16.2.1 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' From 4e09b53c018e5a81351c6fb301d5a8cbe92ad97d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:51:46 +0000 Subject: [PATCH 07/59] chore(deps-dev): bump knip from 6.0.2 to 6.0.3 Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.0.3/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.0.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a2aa4ba8..526d4af5 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "dotenv": "^17.3.1", "husky": "^9.1.7", "jsdom": "^29.0.1", - "knip": "^6.0.2", + "knip": "^6.0.3", "postcss": "^8.5.8", "prettier": "^3.8.1", "tailwindcss": "^4.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5e4ffff..ca41af9b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,8 +148,8 @@ importers: specifier: ^29.0.1 version: 29.0.1(@noble/hashes@2.0.1) knip: - specifier: ^6.0.2 - version: 6.0.2 + specifier: ^6.0.3 + version: 6.0.3 postcss: specifier: ^8.5.8 version: 8.5.8 @@ -2319,8 +2319,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@6.0.2: - resolution: {integrity: sha512-W17Bo5N9AYn0ZkgWHGBmK/01SrSmr3B6iStr3zudDa2eqi+Kc8VmPjSpTYKDV2Uy/kojrlcH/gS1wypAXfXRRA==} + knip@6.0.3: + resolution: {integrity: sha512-6Ai+Iv41dVpBYH6mReFejhniWq4eiaKrBw4kghqz2Ew5psQMYEqYxJtXLdj/7vRJ3nVaHpakhYUCKO8p3ftNsQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -5201,7 +5201,7 @@ snapshots: kind-of@6.0.3: {} - knip@6.0.2: + knip@6.0.3: dependencies: '@nodelib/fs.walk': 1.2.8 fast-glob: 3.3.3 From 69e6a3d490c9e98141335ab7a4cbf550eb1778e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:52:03 +0000 Subject: [PATCH 08/59] chore(deps): bump @tanstack/react-query from 5.95.0 to 5.95.1 Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.95.0 to 5.95.1. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.95.1/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.95.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a2aa4ba8..e80a4d64 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.9.0", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.95.0", + "@tanstack/react-query": "^5.95.1", "argon2": "^0.44.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5e4ffff..77919e61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.2.2) '@tanstack/react-query': - specifier: ^5.95.0 - version: 5.95.0(react@19.2.4) + specifier: ^5.95.1 + version: 5.95.1(react@19.2.4) argon2: specifier: ^0.44.0 version: 0.44.0 @@ -1333,11 +1333,11 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - '@tanstack/query-core@5.95.0': - resolution: {integrity: sha512-H1/CWCe8tGL3YIVeo770Z6kPbt0B3M1d/iQXIIK1qlFiFt6G2neYdkHgLapOC8uMYNt9DmHjmGukEKgdMk1P+A==} + '@tanstack/query-core@5.95.1': + resolution: {integrity: sha512-RTa+CECs9hNMDR2qH4x6bZEjXXDqnjolwecCgwk7ItW1K9WDjUrnyTrtGv4o6aZc1/6uUMkF6DWlYt78uJpgxw==} - '@tanstack/react-query@5.95.0': - resolution: {integrity: sha512-EMP8B+BK9zvnAemT8M/y3z/WO0NjZ7fIUY3T3wnHYK6AA3qK/k33i7tPgCXCejhX0cd4I6bJIXN2GmjrHjDBzg==} + '@tanstack/react-query@5.95.1': + resolution: {integrity: sha512-lqWkRuVtcz9p68/LxxKlWS+M4ACuizJUkVPZryKj0RKE8Se6TD8HU7FNy1c1Mv9C1CPBfzj/p/xiXWK9VCsKJQ==} peerDependencies: react: ^18 || ^19 @@ -4281,11 +4281,11 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tanstack/query-core@5.95.0': {} + '@tanstack/query-core@5.95.1': {} - '@tanstack/react-query@5.95.0(react@19.2.4)': + '@tanstack/react-query@5.95.1(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.95.0 + '@tanstack/query-core': 5.95.1 react: 19.2.4 '@testing-library/dom@10.4.1': From 3b96ff964058f33d3fe8fd65bf7a6fcde9dbcd3b Mon Sep 17 00:00:00 2001 From: Jordy Date: Mon, 23 Mar 2026 22:58:15 -0500 Subject: [PATCH 09/59] refactor(Dockerfile): cleaned up build stages --- Dockerfile | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1ebeb2d4..bfab0f1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,4 @@ # Dockerfile -# -# Multi-stage build for Tracker Tracker. -# Stages: deps → builder → prod-deps → runner -# -# Uses Next.js standalone output for minimal image size (~150MB vs ~1GB). # --------------------------------------------------------------------------- # Base @@ -23,7 +18,7 @@ COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile # --------------------------------------------------------------------------- -# Stage 2 — Build the Next.js application +# Stage 2 — Build the Next.js app # --------------------------------------------------------------------------- FROM base AS builder WORKDIR /app @@ -31,23 +26,25 @@ COPY --from=deps /app/node_modules ./node_modules COPY . . ENV NEXT_TELEMETRY_DISABLED=1 # Dummy DATABASE_URL so Next.js can evaluate route modules during build -# (the DB is never actually queried at build time) +# (In case DB is never actually queried at build time) ENV DATABASE_URL=postgresql://build:build@localhost:5432/build RUN pnpm build # --------------------------------------------------------------------------- -# Stage 3 — Prune to production deps for schema-sync +# Stage 3 — deps for drizzle-kit # --------------------------------------------------------------------------- -FROM deps AS prod-deps -WORKDIR /app -RUN pnpm prune --prod --ignore-scripts +FROM base AS schema-deps +WORKDIR /schema-sync +RUN echo '{"private":true}' > package.json \ + && pnpm add drizzle-kit drizzle-orm postgres # --------------------------------------------------------------------------- # Stage 4 — Production runner # --------------------------------------------------------------------------- FROM node:24-alpine AS runner -# Targeted upgrade for CVE-2026-22184 (zlib). Remove once node:24-alpine ships zlib >= 1.3.2-r0. -RUN apk add --no-cache libc6-compat bash && apk upgrade --no-cache zlib +RUN apk add --no-cache libc6-compat bash && apk upgrade --no-cache \ + && rm -rf /usr/lib/node_modules /usr/local/lib/node_modules \ + /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/corepack WORKDIR /app ENV NODE_ENV=production @@ -55,22 +52,21 @@ ENV NEXT_TELEMETRY_DISABLED=1 ENV PORT=3000 ENV HOSTNAME=0.0.0.0 -# Non-root user for security +# Non-root user RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs RUN mkdir -p /data/backups /data/logs && chown -R nextjs:nodejs /data -# --- Standalone server (traced dependencies only) --- +# --- Standalone server --- COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/public ./public # --- Drizzle schema-sync (for drizzle-kit push at startup) --- -# Use production-only dependencies to minimize image size and CVE surface. RUN mkdir -p /schema-sync/src/lib -COPY --from=prod-deps /app/node_modules /schema-sync/node_modules -COPY --from=builder /app/package.json /schema-sync/ +COPY --from=schema-deps /schema-sync/node_modules /schema-sync/node_modules +COPY --from=schema-deps /schema-sync/package.json /schema-sync/ COPY --from=builder /app/drizzle.config.ts /schema-sync/ COPY --from=builder /app/src/lib/db /schema-sync/src/lib/db COPY --from=builder /app/tsconfig.json /schema-sync/ @@ -86,6 +82,6 @@ USER nextjs EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 ENTRYPOINT ["./docker-entrypoint.sh"] From bce0854d8ea18cf4a99488ed6d9243b25fc71658 Mon Sep 17 00:00:00 2001 From: Jordy Date: Mon, 23 Mar 2026 23:54:12 -0500 Subject: [PATCH 10/59] fix(Dockerfile): update package.json for drizzle-kit with esbuild overrides --- .trivyignore | 15 ++++++++++----- Dockerfile | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.trivyignore b/.trivyignore index 6fcc4463..9caa1f77 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,19 +1,20 @@ -# Alpine zlib — fix requires base image rebuild. Will resolve when node:24-alpine or node:25-alpine ships with zlib 1.3.2-r0. +# Alpine zlib — fix requires base image rebuild. Will resolve when node:24-alpine ships with zlib 1.3.2-r0. CVE-2026-22184 -# minimatch — transitive via npm internals. No direct dep to bump. +# minimatch — transitive via npm internals baked into base image. Stripped from runner but may appear in intermediate stages. CVE-2026-26996 CVE-2026-27903 CVE-2026-27904 -# node-tar — transitive via npm internals. No direct dep to bump. +# node-tar — transitive via npm internals baked into base image. Stripped from runner but may appear in intermediate stages. CVE-2026-26960 CVE-2026-29786 CVE-2026-31802 -# esbuild Go stdlib — build tool binary, not runtime; CVEs apply to the Go runtime bundled in esbuild (transitive via drizzle-kit). -# Cannot fix until esbuild releases a version compiled with a patched Go toolchain. +# esbuild Go stdlib (1.23.x) — build tool binary in schema-sync, not a runtime service. Runs once at startup for drizzle-kit push. +# Cannot fix until esbuild ships a release built with Go 1.24.13+. CVE-2025-47912 +CVE-2025-58183 CVE-2025-58185 CVE-2025-58186 CVE-2025-58187 @@ -22,8 +23,12 @@ CVE-2025-58189 CVE-2025-61723 CVE-2025-61724 CVE-2025-61725 +CVE-2025-61726 CVE-2025-61727 +CVE-2025-61728 +CVE-2025-61729 CVE-2025-61730 +CVE-2025-68121 CVE-2026-25679 CVE-2026-27139 CVE-2026-27142 diff --git a/Dockerfile b/Dockerfile index bfab0f1a..a629cb17 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,11 +31,11 @@ ENV DATABASE_URL=postgresql://build:build@localhost:5432/build RUN pnpm build # --------------------------------------------------------------------------- -# Stage 3 — deps for drizzle-kit +# Stage 3 — Minimal deps for drizzle-kit # --------------------------------------------------------------------------- FROM base AS schema-deps WORKDIR /schema-sync -RUN echo '{"private":true}' > package.json \ +RUN printf '{"private":true,"pnpm":{"overrides":{"esbuild":">=0.25.0"}}}\n' > package.json \ && pnpm add drizzle-kit drizzle-orm postgres # --------------------------------------------------------------------------- From d70a9b8f4282e244745936af20c9a7aaeeefd9ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:29:21 +0000 Subject: [PATCH 11/59] chore(deps-dev): bump typescript from 5.9.3 to 6.0.2 Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.2. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2) --- updated-dependencies: - dependency-name: typescript dependency-version: 6.0.2 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 46 ++++++++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 1b44af3e..22f87a02 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "prettier": "^3.8.1", "tailwindcss": "^4.2.2", "tsx": "^4.21.0", - "typescript": "^5.9.3", + "typescript": "^6.0.2", "vitest": "^4.1.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df6eb239..653af45d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,7 +107,7 @@ importers: version: 2.4.8 '@commitlint/cli': specifier: ^20.5.0 - version: 20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@5.9.3) + version: 20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2) '@commitlint/config-conventional': specifier: ^20.5.0 version: 20.5.0 @@ -163,8 +163,8 @@ importers: specifier: ^4.21.0 version: 4.21.0 typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.2 + version: 6.0.2 vitest: specifier: ^4.1.0 version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) @@ -2799,6 +2799,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3241,8 +3245,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.2: + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} engines: {node: '>=14.17'} hasBin: true @@ -3540,11 +3544,11 @@ snapshots: dependencies: css-tree: 3.2.1 - '@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@5.9.3)': + '@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2)': dependencies: '@commitlint/format': 20.5.0 '@commitlint/lint': 20.5.0 - '@commitlint/load': 20.5.0(@types/node@25.5.0)(typescript@5.9.3) + '@commitlint/load': 20.5.0(@types/node@25.5.0)(typescript@6.0.2) '@commitlint/read': 20.5.0(conventional-commits-parser@6.3.0) '@commitlint/types': 20.5.0 tinyexec: 1.0.4 @@ -3593,14 +3597,14 @@ snapshots: '@commitlint/rules': 20.5.0 '@commitlint/types': 20.5.0 - '@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@5.9.3)': + '@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@6.0.2)': dependencies: '@commitlint/config-validator': 20.5.0 '@commitlint/execute-rule': 20.0.0 '@commitlint/resolve-extends': 20.5.0 '@commitlint/types': 20.5.0 - cosmiconfig: 9.0.1(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) + cosmiconfig: 9.0.1(typescript@6.0.2) + cosmiconfig-typescript-loader: 6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 @@ -4698,21 +4702,21 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): + cosmiconfig-typescript-loader@6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2): dependencies: '@types/node': 25.5.0 - cosmiconfig: 9.0.1(typescript@5.9.3) + cosmiconfig: 9.0.1(typescript@6.0.2) jiti: 2.6.1 - typescript: 5.9.3 + typescript: 6.0.2 - cosmiconfig@9.0.1(typescript@5.9.3): + cosmiconfig@9.0.1(typescript@6.0.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.2 cross-env@10.1.0: dependencies: @@ -4931,6 +4935,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -5908,6 +5916,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -6409,7 +6419,7 @@ snapshots: typedarray@0.0.6: {} - typescript@5.9.3: {} + typescript@6.0.2: {} uglify-js@3.19.3: optional: true @@ -6473,8 +6483,8 @@ snapshots: vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 rollup: 4.60.0 tinyglobby: 0.2.15 From f9650b08bd6814dc2c2cfc5c53b4a849e6eaf0b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:29:51 +0000 Subject: [PATCH 12/59] chore(deps): bump @tanstack/react-query from 5.95.1 to 5.95.2 Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.95.1 to 5.95.2. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.95.2/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.95.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 32 +++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 1b44af3e..da5ac998 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@dnd-kit/utilities": "^3.2.2", "@formkit/auto-animate": "^0.9.0", "@tailwindcss/typography": "^0.5.19", - "@tanstack/react-query": "^5.95.1", + "@tanstack/react-query": "^5.95.2", "argon2": "^0.44.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df6eb239..94393e53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.2.2) '@tanstack/react-query': - specifier: ^5.95.1 - version: 5.95.1(react@19.2.4) + specifier: ^5.95.2 + version: 5.95.2(react@19.2.4) argon2: specifier: ^0.44.0 version: 0.44.0 @@ -1333,11 +1333,11 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - '@tanstack/query-core@5.95.1': - resolution: {integrity: sha512-RTa+CECs9hNMDR2qH4x6bZEjXXDqnjolwecCgwk7ItW1K9WDjUrnyTrtGv4o6aZc1/6uUMkF6DWlYt78uJpgxw==} + '@tanstack/query-core@5.95.2': + resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==} - '@tanstack/react-query@5.95.1': - resolution: {integrity: sha512-lqWkRuVtcz9p68/LxxKlWS+M4ACuizJUkVPZryKj0RKE8Se6TD8HU7FNy1c1Mv9C1CPBfzj/p/xiXWK9VCsKJQ==} + '@tanstack/react-query@5.95.2': + resolution: {integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==} peerDependencies: react: ^18 || ^19 @@ -2799,6 +2799,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -4281,11 +4285,11 @@ snapshots: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tanstack/query-core@5.95.1': {} + '@tanstack/query-core@5.95.2': {} - '@tanstack/react-query@5.95.1(react@19.2.4)': + '@tanstack/react-query@5.95.2(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.95.1 + '@tanstack/query-core': 5.95.2 react: 19.2.4 '@testing-library/dom@10.4.1': @@ -4931,6 +4935,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -5908,6 +5916,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -6473,8 +6483,8 @@ snapshots: vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 rollup: 4.60.0 tinyglobby: 0.2.15 From 79f9baa2dfc3d2258e904710191a7f01aa42a61b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:30:08 +0000 Subject: [PATCH 13/59] chore(deps-dev): bump knip from 6.0.3 to 6.0.4 Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.0.3 to 6.0.4. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.0.4/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.0.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 46 ++++++++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 1b44af3e..e9984759 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "dotenv": "^17.3.1", "husky": "^9.1.7", "jsdom": "^29.0.1", - "knip": "^6.0.3", + "knip": "^6.0.4", "postcss": "^8.5.8", "prettier": "^3.8.1", "tailwindcss": "^4.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df6eb239..b86eadeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,8 +148,8 @@ importers: specifier: ^29.0.1 version: 29.0.1(@noble/hashes@2.0.1) knip: - specifier: ^6.0.3 - version: 6.0.3 + specifier: ^6.0.4 + version: 6.0.4 postcss: specifier: ^8.5.8 version: 8.5.8 @@ -2319,8 +2319,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@6.0.3: - resolution: {integrity: sha512-6Ai+Iv41dVpBYH6mReFejhniWq4eiaKrBw4kghqz2Ew5psQMYEqYxJtXLdj/7vRJ3nVaHpakhYUCKO8p3ftNsQ==} + knip@6.0.4: + resolution: {integrity: sha512-r/9F7wcxiFM71WgDFQiToE2hQHwZ/UkGmr74o8eiNFPIg80f7rlQHVrZiRX46Tj2yE3s96wUVNGMnsDMylgInw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2791,14 +2791,18 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3031,8 +3035,8 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smol-toml@1.6.0: - resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} socks-proxy-agent@9.0.0: @@ -4927,9 +4931,9 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 figures@3.2.0: dependencies: @@ -5201,7 +5205,7 @@ snapshots: kind-of@6.0.3: {} - knip@6.0.3: + knip@6.0.4: dependencies: '@nodelib/fs.walk': 1.2.8 fast-glob: 3.3.3 @@ -5212,8 +5216,8 @@ snapshots: oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.3 - smol-toml: 1.6.0 + picomatch: 4.0.4 + smol-toml: 1.6.1 strip-json-comments: 5.0.3 unbash: 2.2.0 yaml: 2.8.3 @@ -5696,7 +5700,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 min-indent@1.0.1: {} @@ -5904,10 +5908,12 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -6226,7 +6232,7 @@ snapshots: smart-buffer@4.2.0: {} - smol-toml@1.6.0: {} + smol-toml@1.6.1: {} socks-proxy-agent@9.0.0: dependencies: @@ -6361,8 +6367,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinyrainbow@3.1.0: {} @@ -6473,8 +6479,8 @@ snapshots: vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 rollup: 4.60.0 tinyglobby: 0.2.15 From b2a790245ca76f1ec3ef8220c273a4ab9ca508fd Mon Sep 17 00:00:00 2001 From: Jordy Date: Wed, 25 Mar 2026 11:02:12 -0500 Subject: [PATCH 14/59] fix(auth): decouple cookie secure flag from node_env for self-hosted http deployments. Closes #101 --- .env.example | 1 + .vscode/settings.json | 3 +- README.md | 1 + SECURITY.md | 8 +- docker-compose.yml | 1 + docs/kb/docs/getting-started/docker-config.md | 3 +- docs/kb/docs/reference/settings.md | 1 + docs/kb/docs/troubleshooting/common-errors.md | 9 ++ pnpm-lock.yaml | 68 ++++++--------- src/instrumentation.ts | 8 ++ src/lib/auth-session.test.ts | 73 +++++++++++++++- src/lib/auth.ts | 7 +- src/lib/cookie-security.test.ts | 84 +++++++++++++++++++ src/lib/cookie-security.ts | 7 ++ src/proxy.test.ts | 8 +- src/proxy.ts | 7 +- 16 files changed, 233 insertions(+), 56 deletions(-) create mode 100644 src/lib/cookie-security.test.ts create mode 100644 src/lib/cookie-security.ts diff --git a/.env.example b/.env.example index ee1f7925..aaf22d7e 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,7 @@ TZ=America/Chicago # PORT=3000 # BASE_URL=https://trackertracker.example.com +# SECURE_COOKIES=true # LOG_LEVEL=debug # POSTGRES_DB=tracker_tracker diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e30bb88..f92fe287 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,5 +15,6 @@ }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - } + }, + "conventionalCommits.scopes": ["auth"] } diff --git a/README.md b/README.md index 1791a47a..40dd2272 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ pnpm dev | `POSTGRES_USER` | No | `postgres` | Database user | | `POSTGRES_DB` | No | `tracker_tracker` | Database name | | `DATABASE_URL` | No\* | _(auto-built)_ | Override to use an external Postgres instance | +| `SECURE_COOKIES` | No | _(auto)_ | Set `true` for HTTPS. Auto-enabled by `BASE_URL`. | \* Set either `POSTGRES_PASSWORD` (bundled DB) or `DATABASE_URL` (external DB). diff --git a/SECURITY.md b/SECURITY.md index e4f87644..2cc996a6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -62,7 +62,7 @@ Last audited: `2026-03-17` - **Password hashing**: Argon2 (memory-hard KDF) — `src/lib/auth.ts` - **Session tokens**: Encrypted JWE (A256GCM) via `jose` — `src/lib/auth.ts` -- **Cookie security**: httpOnly, secure (in production), sameSite=strict, 7-day hard expiry +- **Cookie security**: httpOnly, secure (when BASE_URL is HTTPS or SECURE_COOKIES=true), sameSite=strict, 7-day hard expiry - **Password policy**: 8-128 characters enforced on setup and login - **Username**: Optional login username (6-100 chars), case-insensitive - **TOTP 2FA**: Optional TOTP via `otpauth` (SHA1, 6 digits, 30s period, ±1 window). Secret encrypted at rest with AES-256-GCM. Stateless enrollment via JWE setup tokens (5min TTL). @@ -307,7 +307,7 @@ When `backupEncryptionEnabled` is true, the entire backup JSON is wrapped in an - **Read-only filesystem:** Mount the application container root as read-only (`read_only: true` in docker-compose) with tmpfs for `/tmp`. - **Network isolation:** Place PostgreSQL on an internal Docker network with no published ports. - **Reverse proxy:** Deploy behind Nginx, Caddy, or Traefik with TLS termination and rate limiting on `/api/auth/login`. -- **`NODE_ENV=production`:** Required for the `secure` cookie flag and Next.js production optimizations. +- **`NODE_ENV=production`:** Required for Next.js production optimizations. Cookie `secure` flag is controlled separately via `BASE_URL` scheme or `SECURE_COOKIES=true`. - **`SESSION_SECRET`:** Minimum 32 characters of cryptographically random data. Generate with: `openssl rand -base64 48`. --- @@ -474,7 +474,7 @@ npx tsx scripts/security-audit.ts # Static security audit (28 checks) #### 5. Session Security -- [ ] Session cookie set with: `httpOnly: true`, `sameSite: "strict"`, `secure: NODE_ENV === "production"`, `path: "/"` +- [ ] Session cookie set with: `httpOnly: true`, `sameSite: "strict"`, `secure: shouldSecureCookies()`, `path: "/"` - [ ] Session has hard expiry (7 days) encoded in the JWE payload — not just cookie `maxAge` - [ ] Destructive operations (lockdown, nuke, password change, restore) zero-fill the encryption key buffer and stop the scheduler. Logout preserves the scheduler for 24/7 polling. - [ ] Login returns the encryption key only inside the JWE session — never in the response body @@ -516,7 +516,7 @@ Verify in `next.config.ts`: #### 10. Docker & Deployment - [ ] Container runs as non-root user (UID 1001 `nextjs`) -- [ ] `NODE_ENV=production` set in Docker Compose +- [ ] `NODE_ENV=production` set in Dockerfile (cookie `secure` flag derived from `BASE_URL`/`SECURE_COOKIES`, not `NODE_ENV`) - [ ] PostgreSQL on internal network — no published ports - [ ] No `.env` files tracked by git (checked by security audit #7) - [ ] `scripts/reset-password-nuclear.mjs` not included in production Docker image diff --git a/docker-compose.yml b/docker-compose.yml index 583cbb6f..8e214687 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,6 +26,7 @@ services: # LOG_LEVEL: ${LOG_LEVEL} # optional, defaults to 'info' # LOG_FILE: /data/logs/tracker-tracker.log # optional BASE_URL: ${BASE_URL:-} # optional, i.e https://trackertracker.example.com + # SECURE_COOKIES: ${SECURE_COOKIES:-} # set to 'true' if serving over HTTPS volumes: - ./data:/data depends_on: diff --git a/docs/kb/docs/getting-started/docker-config.md b/docs/kb/docs/getting-started/docker-config.md index ff5bf27f..e8d440de 100644 --- a/docs/kb/docs/getting-started/docker-config.md +++ b/docs/kb/docs/getting-started/docker-config.md @@ -29,6 +29,7 @@ Everything you need to customize how Tracker Tracker runs: environment variables | `DATABASE_URL` | _(auto-built)_ | Full connection string. Set this to use an external Postgres instance instead of the `POSTGRES_*` variables. Format: `postgresql://user:password@host:5432/dbname` | | `PORT` | `3000` | Port the app listens on inside the container. The host-side port mapping in `docker-compose.yml` follows this value. | | `BASE_URL` | _(empty)_ | The public URL where your app is reachable, e.g. `https://trackertracker.example.com`. Used in backup file metadata and notification links. | +| `SECURE_COOKIES` | _(auto)_ | Set to `true` to mark session cookies as `Secure`. Auto-enabled when `BASE_URL` starts with `https://`. Only needed if you serve over HTTPS without setting `BASE_URL`. | | `TZ` | `UTC` | Timezone for scheduled tasks and log timestamps. Uses standard tz database names, e.g. `America/Chicago`, `Europe/London`. | | `LOG_LEVEL` | `info` | Log verbosity. Options: `error`, `warn`, `info`, `debug`. | | `LOG_FILE` | _(none)_ | Absolute path inside the container to write logs to disk, e.g. `/data/logs/tracker-tracker.log`. | @@ -143,7 +144,7 @@ If Tracker Tracker sits behind Nginx, Caddy, or Traefik, you don't need to expos This assumes Traefik is already running with a `websecure` entrypoint and a `letsencrypt` certificate resolver. !!! info "Set BASE_URL when using a reverse proxy" -Set `BASE_URL=https://trackertracker.example.com` in `.env` so backup files and notification links use your public address instead of localhost. +Set `BASE_URL=https://trackertracker.example.com` in `.env`. This enables secure session cookies automatically and ensures backup files and notification links use your public address. --- diff --git a/docs/kb/docs/reference/settings.md b/docs/kb/docs/reference/settings.md index a9be5140..ad296585 100644 --- a/docs/kb/docs/reference/settings.md +++ b/docs/kb/docs/reference/settings.md @@ -90,3 +90,4 @@ Notification targets are configured individually. Each target is an independent - **Encrypted storage** — Your proxy password, backup password, API tokens, and download client credentials are all encrypted at rest. Changing your master password re-encrypts everything automatically. - **Restoring a backup** — Your master password and its associated encryption salt are never included in a backup and are never overwritten when you restore one. Your session stays valid after a restore. - **Lockout and restores** — Restoring a backup always clears any active lockout, regardless of what was in the backup file. +- **Secure cookies** — Session cookies are marked `Secure` (HTTPS-only) when `BASE_URL` starts with `https://` or `SECURE_COOKIES=true` is set. If you access the app over plain HTTP, cookies are not marked `Secure` and this is expected. diff --git a/docs/kb/docs/troubleshooting/common-errors.md b/docs/kb/docs/troubleshooting/common-errors.md index df447152..4f05fb58 100644 --- a/docs/kb/docs/troubleshooting/common-errors.md +++ b/docs/kb/docs/troubleshooting/common-errors.md @@ -165,6 +165,15 @@ Wait for the lockout duration to expire — the remaining time is shown on the l --- +### Login succeeds but redirects back to login + +**Cause:** The server logs show "Login successful" but the browser keeps returning to the login screen. This happens when session cookies are marked `Secure` but the app is accessed over plain HTTP — browsers silently discard `Secure` cookies on non-HTTPS connections. + +!!! success "Solution" +If you access Tracker Tracker over plain HTTP (e.g. `http://192.168.1.x:3000`), no action is needed on recent versions — cookies default to non-secure. If you are on an older version, update to the latest image. If you serve over HTTPS via a reverse proxy, set `BASE_URL=https://your-domain.com` in `.env` to enable secure cookies. + +--- + ### TOTP — Invalid code **Cause:** The 6-digit code was incorrect or expired. TOTP codes are valid for 30 seconds. Clock drift between your authenticator app and the server can cause valid-looking codes to fail. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6969f259..2256ecc5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -149,7 +149,7 @@ importers: version: 29.0.1(@noble/hashes@2.0.1) knip: specifier: ^6.0.4 - version: 6.0.4 + version: 6.0.5 postcss: specifier: ^8.5.8 version: 8.5.8 @@ -1523,8 +1523,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.10.10: - resolution: {integrity: sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==} + baseline-browser-mapping@2.10.9: + resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1553,8 +1553,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001781: - resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} + caniuse-lite@1.0.30001780: + resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2319,8 +2319,8 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - knip@6.0.4: - resolution: {integrity: sha512-r/9F7wcxiFM71WgDFQiToE2hQHwZ/UkGmr74o8eiNFPIg80f7rlQHVrZiRX46Tj2yE3s96wUVNGMnsDMylgInw==} + knip@6.0.5: + resolution: {integrity: sha512-+i9e/ZKuYlECB5iIK82NQwnYso4oNLBhzsTbXhSqCG1qfGi6D84GNtRENafmS3C0lABX8Wf3BKM434nPXi2AbQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2791,18 +2791,14 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3035,8 +3031,8 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smol-toml@1.6.1: - resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + smol-toml@1.6.0: + resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} engines: {node: '>= 18'} socks-proxy-agent@9.0.0: @@ -4480,7 +4476,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.10.10: {} + baseline-browser-mapping@2.10.9: {} bidi-js@1.0.3: dependencies: @@ -4507,7 +4503,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001781: {} + caniuse-lite@1.0.30001780: {} ccount@2.0.1: {} @@ -4931,17 +4927,9 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.3 figures@3.2.0: dependencies: @@ -5213,7 +5201,7 @@ snapshots: kind-of@6.0.3: {} - knip@6.0.4: + knip@6.0.5: dependencies: '@nodelib/fs.walk': 1.2.8 fast-glob: 3.3.3 @@ -5224,8 +5212,8 @@ snapshots: oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.4 - smol-toml: 1.6.1 + picomatch: 4.0.3 + smol-toml: 1.6.0 strip-json-comments: 5.0.3 unbash: 2.2.0 yaml: 2.8.3 @@ -5708,7 +5696,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.2 + picomatch: 2.3.1 min-indent@1.0.1: {} @@ -5736,8 +5724,8 @@ snapshots: dependencies: '@next/env': 16.2.1 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.10 - caniuse-lite: 1.0.30001781 + baseline-browser-mapping: 2.10.9 + caniuse-lite: 1.0.30001780 postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -5916,12 +5904,10 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} + picomatch@2.3.1: {} picomatch@4.0.3: {} - picomatch@4.0.4: {} - pify@2.3.0: {} pify@3.0.0: {} @@ -6240,7 +6226,7 @@ snapshots: smart-buffer@4.2.0: {} - smol-toml@1.6.1: {} + smol-toml@1.6.0: {} socks-proxy-agent@9.0.0: dependencies: @@ -6375,8 +6361,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 tinyrainbow@3.1.0: {} @@ -6487,8 +6473,8 @@ snapshots: vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.8 rollup: 4.60.0 tinyglobby: 0.2.15 diff --git a/src/instrumentation.ts b/src/instrumentation.ts index e5f85563..b46c9cf9 100644 --- a/src/instrumentation.ts +++ b/src/instrumentation.ts @@ -11,6 +11,14 @@ export async function register() { const { startScheduler } = await import("@/lib/scheduler") const { log } = await import("@/lib/logger") + const { shouldSecureCookies } = await import("@/lib/cookie-security") + if (!shouldSecureCookies()) { + log.warn( + "Session cookies are not marked Secure. If this instance is served over HTTPS, " + + "set BASE_URL=https://... or SECURE_COOKIES=true in your environment." + ) + } + try { const key = await loadSchedulerKey() if (!key) { diff --git a/src/lib/auth-session.test.ts b/src/lib/auth-session.test.ts index af566a9a..99f4dcde 100644 --- a/src/lib/auth-session.test.ts +++ b/src/lib/auth-session.test.ts @@ -76,10 +76,21 @@ vi.mock("jose", () => ({ jwtDecrypt: vi.fn(async (token: string) => ({ payload: JSON.parse(token).payload })), })) +vi.mock("@/lib/cookie-security", async () => { + return { + shouldSecureCookies: () => { + if (process.env.SECURE_COOKIES === "true") return true + if (process.env.BASE_URL?.toLowerCase().startsWith("https://")) return true + return false + }, + } +}) + describe("auth session cookies", () => { beforeEach(() => { resetCookieState() tokenCounter = 0 + vi.unstubAllEnvs() vi.stubEnv("SESSION_SECRET", "x".repeat(32)) vi.stubEnv("NODE_ENV", "test") }) @@ -113,8 +124,8 @@ describe("auth session cookies", () => { await expect(getSession()).resolves.toEqual({ encryptionKey: "a".repeat(64) }) }) - it("uses secure cookies in production and different tokens per session", async () => { - vi.stubEnv("NODE_ENV", "production") + it("uses secure cookies when SECURE_COOKIES=true and different tokens per session", async () => { + vi.stubEnv("SECURE_COOKIES", "true") const { createSession } = await loadAuthModule() const first = await createSession("b".repeat(64), null) @@ -127,6 +138,64 @@ describe("auth session cookies", () => { }) }) + it("enables secure cookies when BASE_URL uses https", async () => { + vi.stubEnv("BASE_URL", "https://trackertracker.example.com") + const { createSession } = await loadAuthModule() + + await createSession("f".repeat(64), null) + + expect(getCookieOptions("tt_session")).toMatchObject({ + secure: true, + }) + }) + + it("does not set secure cookies when NODE_ENV is production but no SECURE_COOKIES or HTTPS BASE_URL", async () => { + vi.stubEnv("NODE_ENV", "production") + const { createSession } = await loadAuthModule() + + await createSession("g".repeat(64), null) + + expect(getCookieOptions("tt_session")).toMatchObject({ + secure: false, + }) + }) + + it("does not set secure cookies when BASE_URL uses http://", async () => { + vi.stubEnv("BASE_URL", "http://trackertracker.local") + const { createSession } = await loadAuthModule() + + await createSession("h".repeat(64), null) + + expect(getCookieOptions("tt_session")).toMatchObject({ + secure: false, + }) + expect(getCookieOptions("tt_max_age")).toMatchObject({ + secure: false, + }) + }) + + it("does not set secure cookies when SECURE_COOKIES is the string 'false'", async () => { + vi.stubEnv("SECURE_COOKIES", "false") + const { createSession } = await loadAuthModule() + + await createSession("i".repeat(64), null) + + expect(getCookieOptions("tt_session")).toMatchObject({ + secure: false, + }) + }) + + it("sets secure cookies when BASE_URL uses HTTPS:// with uppercase scheme", async () => { + vi.stubEnv("BASE_URL", "HTTPS://trackertracker.example.com") + const { createSession } = await loadAuthModule() + + await createSession("j".repeat(64), null) + + expect(getCookieOptions("tt_session")).toMatchObject({ + secure: true, + }) + }) + it("clears both cookies on logout", async () => { const { clearSession, createSession } = await loadAuthModule() diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 7f8d2d8c..b35146c7 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -8,6 +8,7 @@ import { hkdfSync } from "node:crypto" import argon2 from "argon2" import { EncryptJWT, jwtDecrypt } from "jose" import { cookies } from "next/headers" +import { shouldSecureCookies } from "@/lib/cookie-security" const SESSION_COOKIE = "tt_session" const MAX_AGE_COOKIE = "tt_max_age" @@ -47,12 +48,12 @@ export async function createSession( .setExpirationTime(`${jweMaxAge}s`) .encrypt(getSessionKey()) - const isProduction = process.env.NODE_ENV === "production" + const secureCookies = shouldSecureCookies() const cookieStore = await cookies() cookieStore.set(SESSION_COOKIE, token, { httpOnly: true, - secure: isProduction, + secure: secureCookies, sameSite: "strict", maxAge: cookieMaxAge, path: "/", @@ -61,7 +62,7 @@ export async function createSession( // Companion cookie so middleware can refresh maxAge without decrypting the JWE cookieStore.set(MAX_AGE_COOKIE, String(cookieMaxAge), { httpOnly: true, - secure: isProduction, + secure: secureCookies, sameSite: "strict", maxAge: cookieMaxAge, path: "/", diff --git a/src/lib/cookie-security.test.ts b/src/lib/cookie-security.test.ts new file mode 100644 index 00000000..6775cbcc --- /dev/null +++ b/src/lib/cookie-security.test.ts @@ -0,0 +1,84 @@ +// src/lib/cookie-security.test.ts + +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" +import { shouldSecureCookies } from "./cookie-security" + +describe("shouldSecureCookies", () => { + beforeEach(() => { + vi.unstubAllEnvs() + }) + + afterEach(() => { + vi.unstubAllEnvs() + }) + + it("returns true when SECURE_COOKIES is the string 'true'", () => { + vi.stubEnv("SECURE_COOKIES", "true") + expect(shouldSecureCookies()).toBe(true) + }) + + it("returns false when SECURE_COOKIES is the string 'false'", () => { + vi.stubEnv("SECURE_COOKIES", "false") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when SECURE_COOKIES is '1' (non-canonical truthy string)", () => { + vi.stubEnv("SECURE_COOKIES", "1") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when SECURE_COOKIES is 'TRUE' (wrong case)", () => { + vi.stubEnv("SECURE_COOKIES", "TRUE") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns true when BASE_URL starts with https://", () => { + vi.stubEnv("BASE_URL", "https://trackertracker.example.com") + expect(shouldSecureCookies()).toBe(true) + }) + + it("returns true when BASE_URL starts with HTTPS:// (uppercase scheme)", () => { + vi.stubEnv("BASE_URL", "HTTPS://trackertracker.example.com") + expect(shouldSecureCookies()).toBe(true) + }) + + it("returns true when BASE_URL starts with Https:// (mixed case scheme)", () => { + vi.stubEnv("BASE_URL", "Https://trackertracker.example.com") + expect(shouldSecureCookies()).toBe(true) + }) + + it("returns false when BASE_URL starts with http://", () => { + vi.stubEnv("BASE_URL", "http://trackertracker.local") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when BASE_URL starts with HTTP:// (uppercase http)", () => { + vi.stubEnv("BASE_URL", "HTTP://trackertracker.local") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when neither SECURE_COOKIES nor BASE_URL is set", () => { + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when NODE_ENV is production but no SECURE_COOKIES or https BASE_URL", () => { + vi.stubEnv("NODE_ENV", "production") + expect(shouldSecureCookies()).toBe(false) + }) + + it("SECURE_COOKIES=true takes precedence regardless of BASE_URL scheme", () => { + vi.stubEnv("SECURE_COOKIES", "true") + vi.stubEnv("BASE_URL", "http://trackertracker.local") + expect(shouldSecureCookies()).toBe(true) + }) + + it("returns false when BASE_URL is an empty string", () => { + vi.stubEnv("BASE_URL", "") + expect(shouldSecureCookies()).toBe(false) + }) + + it("returns false when BASE_URL contains https in the path but not the scheme", () => { + vi.stubEnv("BASE_URL", "http://example.com/redirect?to=https://other.com") + expect(shouldSecureCookies()).toBe(false) + }) +}) diff --git a/src/lib/cookie-security.ts b/src/lib/cookie-security.ts new file mode 100644 index 00000000..7b053133 --- /dev/null +++ b/src/lib/cookie-security.ts @@ -0,0 +1,7 @@ +// src/lib/cookie-security.ts + +export function shouldSecureCookies(): boolean { + if (process.env.SECURE_COOKIES === "true") return true + if (process.env.BASE_URL?.toLowerCase().startsWith("https://")) return true + return false +} diff --git a/src/proxy.test.ts b/src/proxy.test.ts index 25eb485b..0dc2fe3f 100644 --- a/src/proxy.test.ts +++ b/src/proxy.test.ts @@ -1,9 +1,13 @@ // src/proxy.test.ts import { NextRequest } from "next/server" -import { describe, expect, it } from "vitest" +import { describe, expect, it, vi } from "vitest" import { proxy } from "./proxy" +vi.mock("@/lib/cookie-security", () => ({ + shouldSecureCookies: () => false, +})) + describe("auth middleware", () => { it("allows public auth routes without a session", () => { const response = proxy(new NextRequest("http://localhost/api/auth/status")) @@ -42,6 +46,8 @@ describe("auth middleware", () => { expect(setCookie).toContain("HttpOnly") expect(setCookie.toLowerCase()).toContain("samesite=strict") expect(setCookie).toContain("Max-Age=1800") + // shouldSecureCookies() is mocked to return false — verify Secure is absent + expect(setCookie).not.toContain("Secure") }) it("does not honor oversized tt_max_age cookie values", () => { diff --git a/src/proxy.ts b/src/proxy.ts index 7c2425d8..33a07b15 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1,5 +1,6 @@ // src/proxy.ts import { type NextRequest, NextResponse } from "next/server" +import { shouldSecureCookies } from "@/lib/cookie-security" const PUBLIC_EXACT = ["/login", "/setup", "/api/health"] const PUBLIC_PREFIX = ["/api/auth/", "/api/verify-report", "/_next/", "/img/", "/favicon"] @@ -31,17 +32,17 @@ export function proxy(request: NextRequest) { const maxAge = maxAgeStr ? parseInt(maxAgeStr, 10) : null if (maxAge && maxAge > 0 && maxAge <= MAX_COOKIE_AGE) { - const isProduction = process.env.NODE_ENV === "production" + const secureCookies = shouldSecureCookies() response.cookies.set(SESSION_COOKIE, session.value, { httpOnly: true, - secure: isProduction, + secure: secureCookies, sameSite: "strict", maxAge, path: "/", }) response.cookies.set(MAX_AGE_COOKIE, String(maxAge), { httpOnly: true, - secure: isProduction, + secure: secureCookies, sameSite: "strict", maxAge, path: "/", From d1cc79d9e732de6251117b81c25560d2467da75a Mon Sep 17 00:00:00 2001 From: Jordy Date: Wed, 25 Mar 2026 11:07:31 -0500 Subject: [PATCH 15/59] chore(release): 2.4.2 --- CHANGELOG.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bdcc602..5b2c0aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## [2.4.2](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.2) (2026-03-25) + + +### Features + +- add development image to docker hub ([8785094](https://github.com/jordanlambrecht/tracker-tracker/commit/87850942f118ccfd23c0a04d537928cd3db82976)) +- add fetchTrackerStats for future live transit paper data ([ed6662f](https://github.com/jordanlambrecht/tracker-tracker/commit/ed6662f0ac0c9990b6c96e8ae5616452385b6c26)) +- add GitHub Actions workflow for building and pushing development Docker image ([dca0af0](https://github.com/jordanlambrecht/tracker-tracker/commit/dca0af0a75a4ce21fbe14414d86d4ecbe4d459d3)) +- add per-tracker pause polling ([14a6c43](https://github.com/jordanlambrecht/tracker-tracker/commit/14a6c43cd6764924aba6e2fa592ef13d208b528a)) +- add system events viewer and log management ([b01ed22](https://github.com/jordanlambrecht/tracker-tracker/commit/b01ed22ec2ecfefe7f12bb448998d2726cb6b7f9)) +- remote image upload ([a480c34](https://github.com/jordanlambrecht/tracker-tracker/commit/a480c3470b7e97e44764c1d9c6d1bee356d22728)) +- **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) +- **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) + + +### Bug Fixes + +- **api:** orpheus was not matching seeding/leeching to response ([4569238](https://github.com/jordanlambrecht/tracker-tracker/commit/456923879b2970c46432bc9a0b604da2685bc31d)) +- **auth:** decouple cookie secure flag from node_env for self-hosted http deployments. Closes [#101](https://github.com/jordanlambrecht/tracker-tracker/issues/101) ([b2a7902](https://github.com/jordanlambrecht/tracker-tracker/commit/b2a790245ca76f1ec3ef8220c273a4ab9ca508fd)) +- better regex for splitting comparison values in timing safe check ([8c67a50](https://github.com/jordanlambrecht/tracker-tracker/commit/8c67a50cb64196831d7b021249eb76e84766e009)) +- convert bold numbered rules to markdown list items ([6e96454](https://github.com/jordanlambrecht/tracker-tracker/commit/6e964541330cca791818aca5d703e03ac2165694)) +- deploy issues ([cf45ea1](https://github.com/jordanlambrecht/tracker-tracker/commit/cf45ea19726b8f31db5080ebe93909dd0825e995)) +- **Dockerfile:** update package.json for drizzle-kit with esbuild overrides ([bce0854](https://github.com/jordanlambrecht/tracker-tracker/commit/bce0854d8ea18cf4a99488ed6d9243b25fc71658)) +- preload fleet dashboard tab ([5f08951](https://github.com/jordanlambrecht/tracker-tracker/commit/5f0895192f39a740927594ab6617f2c5c04b5708)) +- resolve biome lint warnings ([af8807d](https://github.com/jordanlambrecht/tracker-tracker/commit/af8807d72847e139be396778a096a7695fc49123)) +- **trackers:** markdown rendering ([a5fbdde](https://github.com/jordanlambrecht/tracker-tracker/commit/a5fbdde7056848bb58fdbe6f1e77a765a543842d)) +- update type imports for CollapsibleCard ([23979d1](https://github.com/jordanlambrecht/tracker-tracker/commit/23979d1f6323bd3fa209c9ed19172dbd7d05b6db)) +- update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) +- wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) + + +### Performance + +- add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) +- **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) + + +### Refactoring + +- **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) +- **charts:** migrate time-series charts to time axis with shared helpers and quality fixes ([596396e](https://github.com/jordanlambrecht/tracker-tracker/commit/596396e205fe387799986af7f1ff8386e8f77d13)) +- **charts:** reorganize chart support files into lib/ subfolder ([98a26d4](https://github.com/jordanlambrecht/tracker-tracker/commit/98a26d4fb4bf2fb7c3b02c4d38151a6b07fbb887)) +- **Dockerfile:** cleaned up build stages ([3b96ff9](https://github.com/jordanlambrecht/tracker-tracker/commit/3b96ff964058f33d3fe8fd65bf7a6fcde9dbcd3b)) +- **settings:** extract CollapsibleCard ([5487d19](https://github.com/jordanlambrecht/tracker-tracker/commit/5487d19d1ac2dd56c6bf2136f072edbcb7868fe5)) +- **settings:** extract SettingsSection wrapper ([ea0572c](https://github.com/jordanlambrecht/tracker-tracker/commit/ea0572c603ef191494a37cd9fe7ca64447bce1d4)) + ## [2.4.1](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.1) (2026-03-23) diff --git a/package.json b/package.json index 28f11a82..a24c5f15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "private-tracker-tracker", - "version": "2.4.1", + "version": "2.4.2", "description": "Self-hosted dashboard for monitoring private tracker stats over time", "license": "GPL-3.0", "repository": { From baeacd726872b76142b1a9b9a3dd51cc8256a457 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:09:04 +0000 Subject: [PATCH 16/59] chore(deps-dev): bump vitest from 4.1.0 to 4.1.1 Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.1/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 120 ++++++++++++++++++++++++++----------------------- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index a24c5f15..96b05d44 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,6 @@ "tailwindcss": "^4.2.2", "tsx": "^4.21.0", "typescript": "^6.0.2", - "vitest": "^4.1.0" + "vitest": "^4.1.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2256ecc5..7a098a71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,8 +166,8 @@ importers: specifier: ^6.0.2 version: 6.0.2 vitest: - specifier: ^4.1.0 - version: 4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.1 + version: 4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages: @@ -1429,34 +1429,34 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/expect@4.1.1': + resolution: {integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==} - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/mocker@4.1.1': + resolution: {integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/pretty-format@4.1.1': + resolution: {integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.1.1': + resolution: {integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/snapshot@4.1.1': + resolution: {integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==} - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/spy@4.1.1': + resolution: {integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==} - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@vitest/utils@4.1.1': + resolution: {integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==} JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} @@ -2791,14 +2791,18 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3332,21 +3336,21 @@ packages: yaml: optional: true - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vitest@4.1.1: + resolution: {integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.1.1 + '@vitest/browser-preview': 4.1.1 + '@vitest/browser-webdriverio': 4.1.1 + '@vitest/ui': 4.1.1 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -4380,44 +4384,44 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitest/expect@4.1.0': + '@vitest/expect@4.1.1': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.1.1 + '@vitest/utils': 4.1.1 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.1.0': + '@vitest/pretty-format@4.1.1': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.0': + '@vitest/runner@4.1.1': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.1.1 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.1.1': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.1.1 + '@vitest/utils': 4.1.1 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.0': {} + '@vitest/spy@4.1.1': {} - '@vitest/utils@4.1.0': + '@vitest/utils@4.1.1': dependencies: - '@vitest/pretty-format': 4.1.0 + '@vitest/pretty-format': 4.1.1 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4927,9 +4931,9 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 figures@3.2.0: dependencies: @@ -5696,7 +5700,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 min-indent@1.0.1: {} @@ -5904,10 +5908,12 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} picomatch@4.0.3: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -6361,8 +6367,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinyrainbow@3.1.0: {} @@ -6473,8 +6479,8 @@ snapshots: vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.27.4 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 postcss: 8.5.8 rollup: 4.60.0 tinyglobby: 0.2.15 @@ -6486,21 +6492,21 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.0(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/expect': 4.1.1 + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.1 + '@vitest/runner': 4.1.1 + '@vitest/snapshot': 4.1.1 + '@vitest/spy': 4.1.1 + '@vitest/utils': 4.1.1 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 From d2cd450af0b6cf55bc5fc1c94ea01452c55252fb Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 00:19:35 -0500 Subject: [PATCH 17/59] fix(Icons): update DownloadArrowIcon stroke width --- src/components/ui/Icons.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/ui/Icons.tsx b/src/components/ui/Icons.tsx index 2f694597..dff99bf3 100644 --- a/src/components/ui/Icons.tsx +++ b/src/components/ui/Icons.tsx @@ -259,15 +259,14 @@ function DownloadArrowIcon(props: IconProps) { viewBox="0 0 24 24" fill="none" stroke="currentColor" - strokeWidth={2} + strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" {...props} > - - - + + ) } From 636d22701cd43d8827f9afe49a5105820578f892 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 01:21:30 -0500 Subject: [PATCH 18/59] feat(schema): add daily checkpoint tables and TodayAtAGlance types --- src/lib/db/schema.ts | 62 +++++++++++++++++++++++++++++++++++++++++++- src/lib/qbt/types.ts | 2 +- src/types/api.ts | 49 +++++++++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 0d325ef4..d1b7e707 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -2,7 +2,8 @@ // // Tables: appSettings, trackers, trackerSnapshots, trackerRoles, downloadClients, // tagGroups, tagGroupMembers, clientSnapshots, backupHistory, dismissedAlerts, -// draftQuicklinks (column on appSettings), notificationTargets, notificationDeliveryState +// draftQuicklinks (column on appSettings), notificationTargets, notificationDeliveryState, +// trackerDailyCheckpoints, torrentDailyCheckpoints import { bigint, @@ -364,3 +365,62 @@ export const notificationDeliveryState = pgTable( index("idx_delivery_state_tracker_id").on(table.trackerId), ] ) + +// ─── Today At A Glance — daily checkpoint tables ────────────────────────────── +// +// These tables store end-of-day snapshots used by the "Today At A Glance" +// feature to compute daily deltas (e.g. how much was uploaded/downloaded today). +// +// trackerDailyCheckpoints: one row per tracker per calendar day, capturing the +// final metric values recorded that day. snapshotCount reflects how many raw +// snapshots were taken during the day, giving confidence in the reading quality. +// +// torrentDailyCheckpoints: one row per (client, torrent hash, calendar day), +// recording the starting uploaded/downloaded values for the day. Delta is +// computed at query time as (current − start). + +export const trackerDailyCheckpoints = pgTable( + "tracker_daily_checkpoints", + { + id: serial("id").primaryKey(), + trackerId: integer("tracker_id") + .references(() => trackers.id, { onDelete: "cascade" }) + .notNull(), + checkpointDate: date("checkpoint_date").notNull(), + uploadedBytesEnd: bigint("uploaded_bytes_end", { mode: "bigint" }).notNull(), + downloadedBytesEnd: bigint("downloaded_bytes_end", { mode: "bigint" }).notNull(), + bufferBytesEnd: bigint("buffer_bytes_end", { mode: "bigint" }), + ratioEnd: real("ratio_end"), + seedbonusEnd: real("seedbonus_end"), + snapshotCount: integer("snapshot_count").notNull().default(0), + }, + (table) => [ + uniqueIndex("uq_tracker_checkpoint_tracker_date").on( + table.trackerId, + table.checkpointDate + ), + ] +) + +export const torrentDailyCheckpoints = pgTable( + "torrent_daily_checkpoints", + { + id: serial("id").primaryKey(), + clientId: integer("client_id") + .references(() => downloadClients.id, { onDelete: "cascade" }) + .notNull(), + hash: varchar("hash", { length: 64 }).notNull(), + name: text("name").notNull(), + checkpointDate: date("checkpoint_date").notNull(), + uploadedStart: bigint("uploaded_start", { mode: "bigint" }).notNull(), + downloadedStart: bigint("downloaded_start", { mode: "bigint" }).notNull(), + }, + (table) => [ + uniqueIndex("uq_torrent_checkpoint_client_hash_date").on( + table.clientId, + table.hash, + table.checkpointDate + ), + index("idx_torrent_checkpoint_date").on(table.checkpointDate), + ] +) diff --git a/src/lib/qbt/types.ts b/src/lib/qbt/types.ts index e3485b9f..5306d694 100644 --- a/src/lib/qbt/types.ts +++ b/src/lib/qbt/types.ts @@ -29,7 +29,7 @@ export interface QbtTorrent { progress: number // float 0-1, download progress content_path: string // full path to content save_path: string // save directory - isPrivate: boolean // true if from a private tracker + isPrivate?: boolean // qBT returns this as is_private (snake_case) — may be undefined in raw API responses } // From GET /api/v2/transfer/info diff --git a/src/types/api.ts b/src/types/api.ts index 71e77c27..ced0615e 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -22,7 +22,7 @@ export interface TrackerLatestStats { requiredRatio: number | null warned: boolean | null freeleechTokens: number | null - bufferBytes: string | null // bigint serialized as decimal string + bufferBytes: string | null hitAndRuns: number | null seedbonus: number | null shareScore: number | null @@ -114,9 +114,56 @@ export interface QbitmanageTagConfig { export interface DashboardSettings { showHealthIndicators: boolean showLoginTimers: boolean + showTodayAtAGlance: boolean +} + +export interface TodayAtAGlance { + fleet: { + uploadDelta: string + downloadDelta: string + bufferDelta: string + ratioChange: number | null + seedbonusChange: number | null + uploadDeltaYesterday: string | null + downloadDeltaYesterday: string | null + bufferDeltaYesterday: string | null + } + trackers: Array<{ + id: number + name: string + color: string | null + uploadDelta: string + downloadDelta: string + bufferDelta: string + }> + activity: { + addedToday: number + completedToday: number + } + movers: { + topUploaders: Array<{ + hash: string + name: string + qbtTag: string | null + trackerColor: string | null + clientName: string | null + uploadedToday: string + }> + topDownloaders: Array<{ + hash: string + name: string + qbtTag: string | null + trackerColor: string | null + clientName: string | null + downloadedToday: string + }> + } + trackerLastUpdated: string | null + clientLastUpdated: string | null } export const DASHBOARD_SETTINGS_DEFAULTS: DashboardSettings = { showHealthIndicators: true, showLoginTimers: true, + showTodayAtAGlance: true, } From cf54c7fbd6c2d9171b5d11b621e9a1f68abc381a Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 01:26:54 -0500 Subject: [PATCH 19/59] fix(auth): return 401 on stale session instead of misleading credential errors --- src/app/api/clients/[id]/test/route.ts | 10 +- src/app/api/clients/[id]/torrents/route.ts | 10 +- src/app/api/clients/client-routes.test.ts | 5 +- src/app/api/fleet/torrents/route.ts | 7 + src/app/api/trackers/[id]/poll/route.ts | 8 + src/lib/__tests__/client-decrypt.test.ts | 2 +- src/lib/__tests__/error-utils.test.ts | 182 +++++++++++++++++++++ src/lib/client-decrypt.ts | 5 +- src/lib/error-utils.ts | 13 +- src/lib/qbt/fetch-merged.ts | 15 +- 10 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 src/lib/__tests__/error-utils.test.ts diff --git a/src/app/api/clients/[id]/test/route.ts b/src/app/api/clients/[id]/test/route.ts index 45d05939..46de3ee4 100644 --- a/src/app/api/clients/[id]/test/route.ts +++ b/src/app/api/clients/[id]/test/route.ts @@ -8,6 +8,7 @@ import { authenticate, decodeKey, parseRouteId } from "@/lib/api-helpers" import { decryptClientCredentials } from "@/lib/client-decrypt" import { db } from "@/lib/db" import { downloadClients } from "@/lib/db/schema" +import { isDecryptionError } from "@/lib/error-utils" import { log } from "@/lib/logger" import { buildBaseUrl, getTransferInfo, invalidateSession, login } from "@/lib/qbt" @@ -34,7 +35,14 @@ export async function POST(_request: Request, props: { params: Promise<{ id: str let password: string try { ;({ username, password } = decryptClientCredentials(client, key)) - } catch { + } catch (err) { + if (isDecryptionError(err)) { + log.warn( + { route: "POST /api/clients/[id]/test", clientId }, + "client test failed — stale session key" + ) + return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) + } log.error( { route: "POST /api/clients/[id]/test", clientId }, "client test failed — credential decrypt error" diff --git a/src/app/api/clients/[id]/torrents/route.ts b/src/app/api/clients/[id]/torrents/route.ts index 647245f7..c1c26b7b 100644 --- a/src/app/api/clients/[id]/torrents/route.ts +++ b/src/app/api/clients/[id]/torrents/route.ts @@ -8,6 +8,7 @@ import { authenticate, decodeKey, parseRouteId } from "@/lib/api-helpers" import { decryptClientCredentials } from "@/lib/client-decrypt" import { db } from "@/lib/db" import { downloadClients } from "@/lib/db/schema" +import { isDecryptionError } from "@/lib/error-utils" import { log } from "@/lib/logger" import { getTorrents, withSessionRetry } from "@/lib/qbt" @@ -40,7 +41,14 @@ export async function GET(request: Request, props: { params: Promise<{ id: strin let password: string try { ;({ username, password } = decryptClientCredentials(client, key)) - } catch { + } catch (err) { + if (isDecryptionError(err)) { + log.warn( + { route: "GET /api/clients/[id]/torrents", clientId }, + "torrent fetch failed — stale session key" + ) + return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) + } log.error( { route: "GET /api/clients/[id]/torrents", clientId }, "torrent fetch failed — credential decrypt error" diff --git a/src/app/api/clients/client-routes.test.ts b/src/app/api/clients/client-routes.test.ts index 431963e1..c4f84fe9 100644 --- a/src/app/api/clients/client-routes.test.ts +++ b/src/app/api/clients/client-routes.test.ts @@ -238,7 +238,7 @@ describe("GET /api/clients/[id]/torrents", () => { // Credential handling // ------------------------------------------------------------------------- - it("returns 422 when credential decryption fails", async () => { + it("returns 401 when credential decryption fails", async () => { mockDbSelectClient(MOCK_CLIENT) ;(decrypt as ReturnType).mockImplementation(() => { throw new Error("decryption error") @@ -249,7 +249,8 @@ describe("GET /api/clients/[id]/torrents", () => { const response = await GET(request, { params }) const data = await response.json() - expect(response.status).toBe(422) + expect(response.status).toBe(401) + expect(data.error).toMatch(/session expired/i) // Response body must not contain the encryption key or the encrypted credential strings const body = JSON.stringify(data) expect(body).not.toContain(VALID_KEY) diff --git a/src/app/api/fleet/torrents/route.ts b/src/app/api/fleet/torrents/route.ts index 167d0611..68d0eb8b 100644 --- a/src/app/api/fleet/torrents/route.ts +++ b/src/app/api/fleet/torrents/route.ts @@ -10,6 +10,7 @@ import { NextResponse } from "next/server" import { authenticate, decodeKey } from "@/lib/api-helpers" import { db } from "@/lib/db" import { downloadClients, trackers } from "@/lib/db/schema" +import { log } from "@/lib/logger" import { fetchAndMergeTorrents } from "@/lib/qbt/fetch-merged" export async function GET() { @@ -43,5 +44,11 @@ export async function GET() { ] const result = await fetchAndMergeTorrents(clients, tags, key) + + if (result.sessionExpired) { + log.warn({ route: "GET /api/fleet/torrents" }, "fleet fetch failed — stale session key") + return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) + } + return NextResponse.json(result) } diff --git a/src/app/api/trackers/[id]/poll/route.ts b/src/app/api/trackers/[id]/poll/route.ts index 4097a81c..fb1af665 100644 --- a/src/app/api/trackers/[id]/poll/route.ts +++ b/src/app/api/trackers/[id]/poll/route.ts @@ -4,6 +4,7 @@ import { NextResponse } from "next/server" import { authenticate, decodeKey, parseTrackerId } from "@/lib/api-helpers" import { db } from "@/lib/db" import { appSettings, trackers } from "@/lib/db/schema" +import { isDecryptionError } from "@/lib/error-utils" import { log } from "@/lib/logger" import { buildProxyAgentFromSettings } from "@/lib/proxy" import { pollTracker } from "@/lib/scheduler" @@ -57,6 +58,13 @@ export async function POST(_request: Request, props: { params: Promise<{ id: str await pollTracker(trackerId, key, privacyMode, proxyAgent) return NextResponse.json({ success: true }) } catch (error) { + if (isDecryptionError(error)) { + log.warn( + { route: "POST /api/trackers/[id]/poll", trackerId }, + "manual poll failed — stale session key" + ) + return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) + } const message = error instanceof Error ? error.message : "Poll failed" log.error( { route: "POST /api/trackers/[id]/poll", trackerId, error: message }, diff --git a/src/lib/__tests__/client-decrypt.test.ts b/src/lib/__tests__/client-decrypt.test.ts index 594f6f8d..270f649f 100644 --- a/src/lib/__tests__/client-decrypt.test.ts +++ b/src/lib/__tests__/client-decrypt.test.ts @@ -24,7 +24,7 @@ describe("decryptClientCredentials", () => { }) const client = { name: "MyClient", encryptedUsername: "x", encryptedPassword: "y" } expect(() => decryptClientCredentials(client, Buffer.alloc(32))).toThrow( - 'Credentials are missing or invalid for client "MyClient"' + 'Failed to decrypt credentials for client "MyClient"' ) }) }) diff --git a/src/lib/__tests__/error-utils.test.ts b/src/lib/__tests__/error-utils.test.ts new file mode 100644 index 00000000..41299eed --- /dev/null +++ b/src/lib/__tests__/error-utils.test.ts @@ -0,0 +1,182 @@ +// src/lib/__tests__/error-utils.test.ts + +import { describe, expect, it } from "vitest" +import { isDecryptionError, sanitizeNetworkError } from "@/lib/error-utils" + +// --------------------------------------------------------------------------- +// isDecryptionError +// --------------------------------------------------------------------------- + +describe("isDecryptionError", () => { + // Positive cases — messages that indicate AES-GCM authentication failure + + it("returns true for 'Unsupported state or unable to authenticate data'", () => { + expect(isDecryptionError(new Error("Unsupported state or unable to authenticate data"))).toBe( + true + ) + }) + + it("returns true for 'bad decrypt'", () => { + expect(isDecryptionError(new Error("bad decrypt"))).toBe(true) + }) + + it("returns true for 'bad decrypt' with mixed casing", () => { + expect(isDecryptionError(new Error("Bad Decrypt"))).toBe(true) + }) + + it("returns true for 'Invalid key length'", () => { + expect(isDecryptionError(new Error("Invalid key length"))).toBe(true) + }) + + it("returns true for messages containing 'EVP_' (OpenSSL error codes)", () => { + expect( + isDecryptionError( + new Error("error:1e000065:Cipher functions:OPENSSL_internal:EVP_DecryptFinal_ex") + ) + ).toBe(true) + }) + + it("returns true for messages containing 'decrypt' anywhere", () => { + expect(isDecryptionError(new Error("Failed to decrypt cipher text"))).toBe(true) + }) + + it("returns true for 'authenticate data' partial match", () => { + expect(isDecryptionError(new Error("unable to authenticate data"))).toBe(true) + }) + + it("is case-insensitive for 'DECRYPT'", () => { + expect(isDecryptionError(new Error("DECRYPT failed"))).toBe(true) + }) + + it("is case-insensitive for 'INVALID KEY'", () => { + expect(isDecryptionError(new Error("INVALID KEY supplied"))).toBe(true) + }) + + // Negative cases — errors unrelated to decryption + + it("returns false for 'Connection refused'", () => { + expect(isDecryptionError(new Error("Connection refused"))).toBe(false) + }) + + it("returns false for 'Timeout'", () => { + expect(isDecryptionError(new Error("Timeout"))).toBe(false) + }) + + it("returns false for 'ECONNREFUSED'", () => { + expect(isDecryptionError(new Error("ECONNREFUSED"))).toBe(false) + }) + + it("returns false for 'Not found'", () => { + expect(isDecryptionError(new Error("Not found"))).toBe(false) + }) + + it("returns false for a generic empty error message", () => { + expect(isDecryptionError(new Error(""))).toBe(false) + }) + + // Non-Error inputs + + it("returns false for a plain string (not an Error instance)", () => { + expect(isDecryptionError("bad decrypt")).toBe(false) + }) + + it("returns false for null", () => { + expect(isDecryptionError(null)).toBe(false) + }) + + it("returns false for undefined", () => { + expect(isDecryptionError(undefined)).toBe(false) + }) + + it("returns false for a number", () => { + expect(isDecryptionError(42)).toBe(false) + }) + + it("returns false for a plain object", () => { + expect(isDecryptionError({ message: "bad decrypt" })).toBe(false) + }) + + it("returns false for an array", () => { + expect(isDecryptionError(["bad decrypt"])).toBe(false) + }) +}) + +// --------------------------------------------------------------------------- +// sanitizeNetworkError +// --------------------------------------------------------------------------- + +describe("sanitizeNetworkError", () => { + it("maps 'timed out' to 'Request timed out'", () => { + expect(sanitizeNetworkError("Request timed out after 15s")).toBe("Request timed out") + }) + + it("maps 'timeout' variant to 'Request timed out'", () => { + expect(sanitizeNetworkError("Connection timeout")).toBe("Request timed out") + }) + + it("maps 'ECONNREFUSED' to 'Connection refused'", () => { + expect(sanitizeNetworkError("ECONNREFUSED 127.0.0.1:8080")).toBe("Connection refused") + }) + + it("maps 'ENOTFOUND' to 'Host not found'", () => { + expect(sanitizeNetworkError("getaddrinfo ENOTFOUND example.invalid")).toBe("Host not found") + }) + + it("maps 'EHOSTUNREACH' to 'Host unreachable'", () => { + expect(sanitizeNetworkError("EHOSTUNREACH")).toBe("Host unreachable") + }) + + it("maps 'ECONNRESET' to 'Connection reset'", () => { + expect(sanitizeNetworkError("ECONNRESET")).toBe("Connection reset") + }) + + it("maps 'ip ban' to IP ban message", () => { + expect(sanitizeNetworkError("ip ban detected")).toBe("IP temporarily banned by tracker") + }) + + it("maps 'rate-limit' to IP ban message", () => { + expect(sanitizeNetworkError("rate-limit exceeded")).toBe("IP temporarily banned by tracker") + }) + + it("maps '401' to 'Authentication failed'", () => { + expect(sanitizeNetworkError("HTTP 401 Unauthorized")).toBe("Authentication failed") + }) + + it("maps 'Unauthorized' to 'Authentication failed'", () => { + expect(sanitizeNetworkError("Unauthorized")).toBe("Authentication failed") + }) + + it("maps 'Forbidden' to 'Authentication failed'", () => { + expect(sanitizeNetworkError("403 Forbidden")).toBe("Authentication failed") + }) + + it("maps 'Session expired' to 'Session expired'", () => { + expect(sanitizeNetworkError("Session expired")).toBe("Session expired") + }) + + it("maps 'proxy' to 'Proxy connection failed'", () => { + expect(sanitizeNetworkError("Could not connect via proxy")).toBe("Proxy connection failed") + }) + + it("extracts status code from 'Tracker API error: 404'", () => { + expect(sanitizeNetworkError("Tracker API error: 404")).toBe("API returned 404") + }) + + it("extracts status code from 'Tracker API error: 500'", () => { + expect(sanitizeNetworkError("Tracker API error: 500")).toBe("API returned 500") + }) + + it("returns the default fallback for an unrecognized message", () => { + expect(sanitizeNetworkError("Something completely unexpected happened")).toBe( + "Connection failed" + ) + }) + + it("uses a custom fallback when provided", () => { + expect(sanitizeNetworkError("Unknown error", "Polling failed")).toBe("Polling failed") + }) + + it("returns default fallback for empty string", () => { + expect(sanitizeNetworkError("")).toBe("Connection failed") + }) +}) diff --git a/src/lib/client-decrypt.ts b/src/lib/client-decrypt.ts index 30d94af6..440fe1dc 100644 --- a/src/lib/client-decrypt.ts +++ b/src/lib/client-decrypt.ts @@ -15,7 +15,8 @@ export function decryptClientCredentials( username: decrypt(client.encryptedUsername, key), password: decrypt(client.encryptedPassword, key), } - } catch { - throw new Error(`Credentials are missing or invalid for client "${client.name}"`) + } catch (err) { + const cause = err instanceof Error ? err.message : String(err) + throw new Error(`Failed to decrypt credentials for client "${client.name}": ${cause}`) } } diff --git a/src/lib/error-utils.ts b/src/lib/error-utils.ts index 2bcd9476..86665a24 100644 --- a/src/lib/error-utils.ts +++ b/src/lib/error-utils.ts @@ -1,6 +1,17 @@ // src/lib/error-utils.ts // -// Functions: sanitizeNetworkError +// Functions: sanitizeNetworkError, isDecryptionError + +/** + * Returns true when an error originates from AES-256-GCM authentication failure + * or key/ciphertext mismatch — i.e. the session encryption key is stale. + * Used by route handlers to distinguish key-mismatch (→ 401) from genuinely + * missing or corrupt stored credentials (→ 422). + */ +export function isDecryptionError(error: unknown): boolean { + if (!(error instanceof Error)) return false + return /decrypt|authenticate\s*data|bad\s*decrypt|invalid\s*key|EVP_/i.test(error.message) +} /** * Maps raw network/auth error messages to safe user-facing messages. diff --git a/src/lib/qbt/fetch-merged.ts b/src/lib/qbt/fetch-merged.ts index 0745d4e0..754eb328 100644 --- a/src/lib/qbt/fetch-merged.ts +++ b/src/lib/qbt/fetch-merged.ts @@ -32,6 +32,8 @@ export interface MergedResult { crossSeedTags: string[] clientErrors: string[] clientCount: number + /** True when every client failure was a decryption error, indicating a stale session key. */ + sessionExpired: boolean } async function fetchClientTorrents( @@ -90,6 +92,7 @@ export async function fetchAndMergeTorrents( crossSeedTags: [], clientErrors: [], clientCount: 0, + sessionExpired: false, } if (clients.length === 0 || tags.length === 0) { @@ -113,6 +116,7 @@ export async function fetchAndMergeTorrents( const torrentLists: QbtTorrent[][] = [] const crossSeedClients: { crossSeedTags: string[] }[] = [] const clientErrors: string[] = [] + let decryptionFailureCount = 0 for (let i = 0; i < results.length; i++) { const result = results[i] @@ -122,13 +126,17 @@ export async function fetchAndMergeTorrents( } else { const clientName = clients[i].name const raw = result.reason instanceof Error ? result.reason.message : "Unknown error" - const message = /decrypt|crypt|EVP_/i.test(raw) - ? "Credential decryption failed" - : sanitizeNetworkError(raw) + const isDecrypt = /decrypt|crypt|EVP_/i.test(raw) + if (isDecrypt) decryptionFailureCount++ + const message = isDecrypt ? "Credential decryption failed" : sanitizeNetworkError(raw) clientErrors.push(`${clientName}: ${message}`) } } + // All clients failed with decryption errors → the session key is stale. + const sessionExpired = + clients.length > 0 && torrentLists.length === 0 && decryptionFailureCount === clients.length + // Build hash → client name(s) lookup before merge flattens provenance const hashClients = new Map() for (let i = 0; i < results.length; i++) { @@ -156,5 +164,6 @@ export async function fetchAndMergeTorrents( crossSeedTags, clientErrors, clientCount: clients.length, + sessionExpired, } } From d3826a181c017c6b2ac8bbc6fa9d77f6dfd0edc2 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 09:15:50 -0500 Subject: [PATCH 20/59] feat(dashboard): add Today At A Glance server logic, checkpoints, and deep poll fixes --- src/app/api/dashboard/today/route.ts | 32 + src/app/api/trackers/tracker-routes.test.ts | 25 +- src/lib/__tests__/client-scheduler.test.ts | 222 +++++- src/lib/__tests__/formatters-today.test.ts | 163 ++++ src/lib/__tests__/security.test.ts | 8 +- src/lib/__tests__/today.test.ts | 799 ++++++++++++++++++++ src/lib/client-scheduler.ts | 51 +- src/lib/formatters.ts | 48 +- src/lib/nuke.ts | 6 +- src/lib/qbt/aggregator.ts | 7 +- src/lib/qbt/client.ts | 42 +- src/lib/qbt/index.ts | 1 + src/lib/qbt/qbt.test.ts | 166 +++- src/lib/scheduler.ts | 105 ++- src/lib/server-data.ts | 42 +- src/lib/today.ts | 447 +++++++++++ 16 files changed, 2046 insertions(+), 118 deletions(-) create mode 100644 src/app/api/dashboard/today/route.ts create mode 100644 src/lib/__tests__/formatters-today.test.ts create mode 100644 src/lib/__tests__/today.test.ts create mode 100644 src/lib/today.ts diff --git a/src/app/api/dashboard/today/route.ts b/src/app/api/dashboard/today/route.ts new file mode 100644 index 00000000..1ad6b186 --- /dev/null +++ b/src/app/api/dashboard/today/route.ts @@ -0,0 +1,32 @@ +// src/app/api/dashboard/today/route.ts + +import { NextResponse } from "next/server" +import { authenticate } from "@/lib/api-helpers" +import { log } from "@/lib/logger" +import { backfillTrackerCheckpoints, computeTodayAtAGlance } from "@/lib/today" + +const g = globalThis as typeof globalThis & { __backfillDone?: boolean } + +export async function GET() { + const auth = await authenticate() + if (auth instanceof NextResponse) return auth + + try { + // One-time backfill on first request that populates checkpoint table from existing snapshots + if (!g.__backfillDone) { + g.__backfillDone = true + try { + const filled = await backfillTrackerCheckpoints() + if (filled > 0) log.info(`Backfilled ${filled} tracker daily checkpoints`) + } catch (err) { + log.error(err, "Checkpoint backfill failed") + } + } + + const data = await computeTodayAtAGlance() + return NextResponse.json(data) + } catch (error) { + log.error(error, "Failed to compute today at a glance") + return NextResponse.json({ error: "Failed to compute daily stats" }, { status: 500 }) + } +} diff --git a/src/app/api/trackers/tracker-routes.test.ts b/src/app/api/trackers/tracker-routes.test.ts index 5859c7bd..f346736a 100644 --- a/src/app/api/trackers/tracker-routes.test.ts +++ b/src/app/api/trackers/tracker-routes.test.ts @@ -24,6 +24,7 @@ vi.mock("@/lib/api-helpers", async (importOriginal) => { vi.mock("@/lib/db", () => ({ db: { select: vi.fn(), + selectDistinctOn: vi.fn(), insert: vi.fn(), update: vi.fn(), delete: vi.fn(), @@ -114,8 +115,12 @@ describe("GET /api/trackers", () => { ;(db.select as ReturnType) .mockReturnValueOnce({ from: mockFromTrackers }) .mockReturnValueOnce({ from: mockSettingsFrom }) - // DISTINCT ON query via db.execute - ;(db.execute as ReturnType).mockResolvedValueOnce([snapshot]) + // DISTINCT ON query via db.selectDistinctOn + ;(db.selectDistinctOn as ReturnType).mockReturnValueOnce({ + from: vi.fn().mockReturnValue({ + orderBy: vi.fn().mockResolvedValue([snapshot]), + }), + }) const response = await GET() const data = await response.json() @@ -139,8 +144,12 @@ describe("GET /api/trackers", () => { ;(db.select as ReturnType) .mockReturnValueOnce({ from: mockFrom }) .mockReturnValueOnce({ from: mockSettingsFrom }) - // DISTINCT ON query via db.execute - ;(db.execute as ReturnType).mockResolvedValueOnce([]) + // DISTINCT ON query via db.selectDistinctOn + ;(db.selectDistinctOn as ReturnType).mockReturnValueOnce({ + from: vi.fn().mockReturnValue({ + orderBy: vi.fn().mockResolvedValue([]), + }), + }) const response = await GET() const data = await response.json() @@ -181,8 +190,12 @@ describe("GET /api/trackers", () => { ;(db.select as ReturnType) .mockReturnValueOnce({ from: mockFromTrackers }) .mockReturnValueOnce({ from: mockSettingsFrom }) - // DISTINCT ON query via db.execute - ;(db.execute as ReturnType).mockResolvedValueOnce([]) + // DISTINCT ON query via db.selectDistinctOn + ;(db.selectDistinctOn as ReturnType).mockReturnValueOnce({ + from: vi.fn().mockReturnValue({ + orderBy: vi.fn().mockResolvedValue([]), + }), + }) const response = await GET() const data = await response.json() diff --git a/src/lib/__tests__/client-scheduler.test.ts b/src/lib/__tests__/client-scheduler.test.ts index 4eef762b..991a7fd7 100644 --- a/src/lib/__tests__/client-scheduler.test.ts +++ b/src/lib/__tests__/client-scheduler.test.ts @@ -259,35 +259,15 @@ describe("deepPollClient per-tag optimization", () => { // ------------------------------------------------------------------------- it("passes deduped per-tag results to aggregateByTag", async () => { + // isPrivate omitted — real qBT API returns is_private (snake_case), not isPrivate const aitherTorrents = [ - { hash: "a1", state: "uploading", tags: "aither", upspeed: 100, dlspeed: 0, isPrivate: true }, - { hash: "a2", state: "uploading", tags: "aither", upspeed: 100, dlspeed: 0, isPrivate: true }, + { hash: "a1", state: "uploading", tags: "aither", upspeed: 100, dlspeed: 0 }, + { hash: "a2", state: "uploading", tags: "aither", upspeed: 100, dlspeed: 0 }, ] const crossTorrents = [ - { - hash: "c1", - state: "uploading", - tags: "cross-seed", - upspeed: 100, - dlspeed: 0, - isPrivate: true, - }, - { - hash: "c2", - state: "uploading", - tags: "cross-seed", - upspeed: 100, - dlspeed: 0, - isPrivate: true, - }, - { - hash: "c3", - state: "uploading", - tags: "cross-seed", - upspeed: 100, - dlspeed: 0, - isPrivate: true, - }, + { hash: "c1", state: "uploading", tags: "cross-seed", upspeed: 100, dlspeed: 0 }, + { hash: "c2", state: "uploading", tags: "cross-seed", upspeed: 100, dlspeed: 0 }, + { hash: "c3", state: "uploading", tags: "cross-seed", upspeed: 100, dlspeed: 0 }, ] mockDbSelectSequence(MOCK_CLIENT, ["aither"]) @@ -423,6 +403,7 @@ describe("deepPollClient per-tag optimization", () => { // ------------------------------------------------------------------------- it("caches filtered torrents to downloadClients on successful poll", async () => { + // isPrivate omitted — real qBT API returns is_private (snake_case), not isPrivate const filteredTorrents = [ { hash: "a1", @@ -431,7 +412,6 @@ describe("deepPollClient per-tag optimization", () => { tags: "aither", upspeed: 100, dlspeed: 0, - isPrivate: true, }, { hash: "a2", @@ -440,7 +420,6 @@ describe("deepPollClient per-tag optimization", () => { tags: "aither", upspeed: 200, dlspeed: 0, - isPrivate: true, }, ] @@ -473,6 +452,7 @@ describe("deepPollClient per-tag optimization", () => { }) it("strips tracker, content_path, and save_path from cached torrents", async () => { + // isPrivate omitted — real qBT API returns is_private (snake_case), not isPrivate const torrentsWithSensitiveFields = [ { hash: "a1", @@ -481,7 +461,6 @@ describe("deepPollClient per-tag optimization", () => { tags: "aither", upspeed: 100, dlspeed: 0, - isPrivate: true, tracker: "https://aither.cc/announce?passkey=SECRET123", content_path: "/data/torrents/Movie.mkv", save_path: "/data/torrents", @@ -550,3 +529,188 @@ describe("deepPollClient per-tag optimization", () => { expect(retryCalls[0][4]).toBe("decrypted-pass") }) }) + +// --------------------------------------------------------------------------- +// Regression: isPrivate field mismatch — dedup must not rely on t.isPrivate +// --------------------------------------------------------------------------- + +describe("deepPollClient dedup without isPrivate", () => { + beforeEach(() => { + vi.resetAllMocks() + }) + + // Regression: prevents dedup from silently dropping all torrents when t.isPrivate + // is undefined. The old guard `if (!t.isPrivate || seen.has(t.hash)) continue` would + // skip every torrent because `t.isPrivate` is always undefined (the real qBT API + // returns `is_private` in snake_case, not camelCase). The fix removed the isPrivate + // guard entirely, keeping only the hash-based dedup. + it("dedup includes torrents that do not have an isPrivate field", async () => { + // Torrents constructed WITHOUT isPrivate — matching real qBT API response shape + const torrentsWithoutIsPrivate = [ + { hash: "h1", state: "uploading", tags: "aither", upspeed: 100, dlspeed: 0 }, + { hash: "h2", state: "uploading", tags: "aither", upspeed: 200, dlspeed: 0 }, + { hash: "h3", state: "uploading", tags: "aither", upspeed: 300, dlspeed: 0 }, + ] + + // Sanity-check the test data itself: none of these objects have isPrivate defined + for (const t of torrentsWithoutIsPrivate) { + expect(Object.hasOwn(t, "isPrivate")).toBe(false) + } + + mockDbSelectSequence(MOCK_CLIENT, ["aither"]) + ;(decrypt as ReturnType) + .mockReturnValueOnce("admin") + .mockReturnValueOnce("secret") + ;(getTorrents as ReturnType).mockResolvedValue(torrentsWithoutIsPrivate) + ;(getTransferInfo as ReturnType).mockResolvedValue(MOCK_TRANSFER_INFO) + ;(aggregateByTag as ReturnType).mockReturnValue(MOCK_STATS) + mockDbInsertSnapshot() + mockDbUpdateClient() + + await deepPollClient(1, makeEncryptionKey()) + + // aggregateByTag must have been called with all 3 torrents — none were dropped + const aggregateCalls = (aggregateByTag as ReturnType).mock.calls + expect(aggregateCalls).toHaveLength(1) + const passedTorrents = aggregateCalls[0][0] + expect(passedTorrents).toHaveLength(3) + }) + + // Regression: verify that dedup still correctly handles duplicate hashes across tag + // fetches even without isPrivate present. The only dedup criterion should be the hash. + it("dedup removes duplicate hashes across tag results when isPrivate is absent", async () => { + // Same hash "shared" appears in both aither and cross-seed tag responses + const aitherResult = [ + { hash: "shared", state: "uploading", tags: "aither, cross-seed", upspeed: 100, dlspeed: 0 }, + { hash: "aither-only", state: "uploading", tags: "aither", upspeed: 50, dlspeed: 0 }, + ] + const crossResult = [ + // "shared" seen again from the cross-seed tag query — should be deduped + { hash: "shared", state: "uploading", tags: "aither, cross-seed", upspeed: 100, dlspeed: 0 }, + { hash: "cross-only", state: "uploading", tags: "cross-seed", upspeed: 75, dlspeed: 0 }, + ] + + mockDbSelectSequence(MOCK_CLIENT, ["aither"]) + ;(decrypt as ReturnType) + .mockReturnValueOnce("admin") + .mockReturnValueOnce("secret") + // First call (aither tag) returns aitherResult; second (cross-seed tag) returns crossResult + ;(getTorrents as ReturnType) + .mockResolvedValueOnce(aitherResult) + .mockResolvedValueOnce(crossResult) + ;(getTransferInfo as ReturnType).mockResolvedValue(MOCK_TRANSFER_INFO) + ;(aggregateByTag as ReturnType).mockReturnValue(MOCK_STATS) + mockDbInsertSnapshot() + mockDbUpdateClient() + + await deepPollClient(1, makeEncryptionKey()) + + const aggregateCalls = (aggregateByTag as ReturnType).mock.calls + expect(aggregateCalls).toHaveLength(1) + const passedTorrents = aggregateCalls[0][0] as Array<{ hash: string }> + + // 3 unique hashes: "shared", "aither-only", "cross-only" + expect(passedTorrents).toHaveLength(3) + const hashes = passedTorrents.map((t) => t.hash) + expect(hashes).toContain("shared") + expect(hashes).toContain("aither-only") + expect(hashes).toContain("cross-only") + // "shared" must appear exactly once + expect(hashes.filter((h) => h === "shared")).toHaveLength(1) + }) +}) + +// --------------------------------------------------------------------------- +// Regression: heartbeat must not overwrite lastPolledAt +// --------------------------------------------------------------------------- + +describe("deepPollClient lastPolledAt written; heartbeat update does not include it", () => { + beforeEach(() => { + vi.resetAllMocks() + }) + + // Regression: prevents heartbeat from starving the deep poll scheduler. + // deepPollAllClients decides whether to run by comparing now - lastPolledAt >= intervalMs. + // If heartbeat (running every 5s) also wrote lastPolledAt, the overdue threshold would + // never be reached after the first heartbeat, and deep polls would stop firing. + // The fix: heartbeat updates only lastError/errorSince/updatedAt, never lastPolledAt. + it("deep poll success writes lastPolledAt to the DB", async () => { + setupFullHappyPathMocks(["aither"]) + const { mockSet } = mockDbUpdateClient() + + await deepPollClient(1, makeEncryptionKey()) + + // At least one update call must include lastPolledAt — this is the status update + const allSetCalls = mockSet.mock.calls.map((c: unknown[]) => c[0] as Record) + const statusUpdate = allSetCalls.find((c) => "lastPolledAt" in c) + expect(statusUpdate).toBeDefined() + expect(statusUpdate?.lastPolledAt).toBeInstanceOf(Date) + }) + + // Regression: verifies the scheduling invariant — a client with a recent lastPolledAt + // (set during a prior deep poll) should be considered NOT overdue, while a client + // with lastPolledAt = null (never polled) should always be considered overdue. + // This mirrors the logic in deepPollAllClients without needing to call it directly. + it("overdue check: client with null lastPolledAt is always overdue", () => { + const pollIntervalSeconds = 30 + const now = Date.now() + + // Mirrors: const lastPoll = client.lastPolledAt?.getTime() ?? 0 + // Mirrors: return now - lastPoll >= intervalMs + const lastPolledAt = null + const intervalMs = pollIntervalSeconds * 1000 + const lastPoll = lastPolledAt ?? 0 + const isOverdue = now - lastPoll >= intervalMs + + expect(isOverdue).toBe(true) + }) + + it("overdue check: client deep-polled within interval is not overdue", () => { + const pollIntervalSeconds = 30 + const now = Date.now() + + // Simulate: heartbeat ran 5s ago but deep poll ran 10s ago (within 30s interval) + const lastPolledAt = new Date(now - 10_000) // 10 seconds ago + const intervalMs = pollIntervalSeconds * 1000 + const lastPoll = lastPolledAt.getTime() + const isOverdue = now - lastPoll >= intervalMs + + expect(isOverdue).toBe(false) + }) + + it("overdue check: client whose interval has elapsed is overdue", () => { + const pollIntervalSeconds = 30 + const now = Date.now() + + // Simulate: last deep poll was 35s ago (past the 30s interval) + const lastPolledAt = new Date(now - 35_000) + const intervalMs = pollIntervalSeconds * 1000 + const lastPoll = lastPolledAt.getTime() + const isOverdue = now - lastPoll >= intervalMs + + expect(isOverdue).toBe(true) + }) + + // Regression: if heartbeat wrote lastPolledAt (the bug), a client polled 5s ago + // via heartbeat would NOT be considered overdue even after the deep poll interval elapsed. + // This test documents that scenario: if lastPolledAt reflects a heartbeat timestamp + // (5s ago) and the poll interval is 30s, the client should still be overdue — + // but only if deep poll hadn't run. We verify the boundary is the deep poll timestamp, + // not the heartbeat timestamp. + it("overdue check: client whose only recent update was a heartbeat 5s ago is still overdue after 30s interval", () => { + const pollIntervalSeconds = 30 + const now = Date.now() + + // Scenario demonstrating the bug: if heartbeat wrote lastPolledAt 5s ago, + // the client would appear not-overdue even though deep poll hasn't run in 60s. + // With the fix, heartbeat does NOT write lastPolledAt, so the last deep poll + // timestamp (60s ago) is what drives the overdue check. + const lastDeepPollAt = new Date(now - 60_000) // deep poll ran 60s ago + const intervalMs = pollIntervalSeconds * 1000 + const lastPoll = lastDeepPollAt.getTime() + const isOverdue = now - lastPoll >= intervalMs + + // 60s ago > 30s interval → overdue + expect(isOverdue).toBe(true) + }) +}) diff --git a/src/lib/__tests__/formatters-today.test.ts b/src/lib/__tests__/formatters-today.test.ts new file mode 100644 index 00000000..075a10ba --- /dev/null +++ b/src/lib/__tests__/formatters-today.test.ts @@ -0,0 +1,163 @@ +// src/lib/__tests__/formatters-today.test.ts + +import { describe, expect, it } from "vitest" +import { formatBytesFromString, localDateStr } from "@/lib/formatters" + +// --------------------------------------------------------------------------- +// localDateStr +// --------------------------------------------------------------------------- + +describe("localDateStr", () => { + it("returns a YYYY-MM-DD formatted string", () => { + const result = localDateStr(new Date("2024-06-15")) + expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/) + }) + + it("returns the correct date for a given Date object", () => { + // Construct a date at local midnight to avoid any timezone edge case confusion + const d = new Date(2024, 5, 15) // June 15 2024 in local time + const result = localDateStr(d) + expect(result).toBe("2024-06-15") + }) + + it("returns the correct date for a unix milliseconds number", () => { + // 2024-01-20 at noon UTC — choosing noon avoids UTC±local edge + const ms = new Date("2024-01-20T12:00:00").getTime() + const result = localDateStr(ms) + // The year, month, and day must match a local interpretation of that timestamp + expect(result).toMatch(/^2024-01-\d{2}$/) + }) + + it("with no args, returns today's date in YYYY-MM-DD format", () => { + const today = new Date().toLocaleDateString("en-CA") + const result = localDateStr() + expect(result).toBe(today) + }) + + it("uses en-CA locale formatting (YYYY-MM-DD, not MM/DD/YYYY)", () => { + // en-CA produces ISO-style dates; en-US would produce "6/15/2024" + const d = new Date(2024, 5, 15) + const result = localDateStr(d) + // Must not contain slashes + expect(result).not.toContain("/") + // Must match the ISO date pattern + expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/) + }) + + it("handles the first day of the month without off-by-one", () => { + const d = new Date(2024, 0, 1) // January 1 local + expect(localDateStr(d)).toBe("2024-01-01") + }) + + it("handles the last day of the month correctly", () => { + const d = new Date(2024, 0, 31) // January 31 local + expect(localDateStr(d)).toBe("2024-01-31") + }) + + it("handles leap day", () => { + const d = new Date(2024, 1, 29) // Feb 29 2024 (leap year) local + expect(localDateStr(d)).toBe("2024-02-29") + }) + + it("is consistent between Date object and equivalent unix ms input", () => { + const d = new Date(2025, 2, 26) // March 26 2025 local + const ms = d.getTime() + expect(localDateStr(d)).toBe(localDateStr(ms)) + }) +}) + +// --------------------------------------------------------------------------- +// formatBytesFromString — MiB threshold +// --------------------------------------------------------------------------- + +describe("formatBytesFromString MiB/GiB threshold", () => { + const MiB = 1024 * 1024 + const GiB = 1024 * 1024 * 1024 + const TiB = 1024 * 1024 * 1024 * 1024 + + it("value of exactly 0 bytes returns MiB scale", () => { + // "0" bigint → 0 bytes → 0 MiB + const result = formatBytesFromString("0") + expect(result).toContain("MiB") + }) + + it("returns '0 MiB' (zero padded rounds to 0) for 0 bytes", () => { + expect(formatBytesFromString("0")).toBe("0 MiB") + }) + + it("500 MiB (under 1 GiB) displays as MiB, not GiB", () => { + const bytes = BigInt(500 * MiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("MiB") + expect(result).not.toContain("GiB") + expect(result).not.toContain("TiB") + }) + + it("value just under 1 GiB displays as MiB", () => { + const bytes = BigInt(GiB - 1) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("MiB") + expect(result).not.toContain("GiB") + }) + + it("exactly 1 GiB displays as GiB", () => { + const bytes = BigInt(GiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("GiB") + expect(result).not.toContain("MiB") + expect(result).toBe("1.00 GiB") + }) + + it("value over 1 GiB displays as GiB", () => { + const bytes = BigInt(1.5 * GiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("GiB") + expect(result).not.toContain("MiB") + expect(result).toBe("1.50 GiB") + }) + + it("value over 1 TiB displays as TiB", () => { + const bytes = BigInt(2 * TiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("TiB") + expect(result).not.toContain("GiB") + expect(result).toBe("2.00 TiB") + }) + + it("value just over 1 TiB displays as TiB not GiB", () => { + const bytes = BigInt(TiB + 1) + const result = formatBytesFromString(bytes.toString()) + expect(result).toContain("TiB") + }) + + it("null input returns the em dash fallback", () => { + expect(formatBytesFromString(null)).toBe("—") + }) + + it("undefined input returns the em dash fallback", () => { + expect(formatBytesFromString(undefined)).toBe("—") + }) + + it("empty string input returns the em dash fallback", () => { + expect(formatBytesFromString("")).toBe("—") + }) + + it("MiB value rounds to whole number (no decimal)", () => { + const bytes = BigInt(512 * MiB) + const result = formatBytesFromString(bytes.toString()) + // Math.round(512) → "512 MiB" + expect(result).toBe("512 MiB") + }) + + it("100 MiB rounds and displays correctly", () => { + const bytes = BigInt(100 * MiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toBe("100 MiB") + }) + + it("1 MiB displays as 1 MiB", () => { + const bytes = BigInt(MiB) + const result = formatBytesFromString(bytes.toString()) + expect(result).toBe("1 MiB") + }) +}) diff --git a/src/lib/__tests__/security.test.ts b/src/lib/__tests__/security.test.ts index f256544c..1d54d0a3 100644 --- a/src/lib/__tests__/security.test.ts +++ b/src/lib/__tests__/security.test.ts @@ -24,6 +24,7 @@ vi.mock("@/lib/api-helpers", async (importOriginal) => { vi.mock("@/lib/db", () => ({ db: { select: vi.fn(), + selectDistinctOn: vi.fn(), insert: vi.fn(), update: vi.fn(), delete: vi.fn(), @@ -845,7 +846,12 @@ describe("Token leakage prevention", () => { ;(db.select as ReturnType) .mockReturnValueOnce({ from: mockFrom }) .mockReturnValueOnce({ from: mockSettingsFrom }) - // DISTINCT ON query via db.execute — default mock resolves [] + // DISTINCT ON query via db.selectDistinctOn + ;(db.selectDistinctOn as ReturnType).mockReturnValueOnce({ + from: vi.fn().mockReturnValue({ + orderBy: vi.fn().mockResolvedValue([]), + }), + }) const res = await GET() const body = await res.json() diff --git a/src/lib/__tests__/today.test.ts b/src/lib/__tests__/today.test.ts new file mode 100644 index 00000000..d36cc27d --- /dev/null +++ b/src/lib/__tests__/today.test.ts @@ -0,0 +1,799 @@ +// src/lib/__tests__/today.test.ts +// +// Tests for computeTodayAtAGlance() in src/lib/today.ts. +// The DB layer is mocked via vi.mock. Because vi.mock factories are hoisted to +// the top of the file before any variable declarations, the mock factories use +// inline string sentinels that match the schema mock values exactly. + +import { beforeEach, describe, expect, it, vi } from "vitest" + +// --------------------------------------------------------------------------- +// Per-test data store — populated by seedStore() in each test +// --------------------------------------------------------------------------- + +// These string values must match the values returned by the schema mock below. +// "T_" prefix is just a local naming convention; the actual values are what matter. +const SENT_TRACKERS = "SENT_trackers" +const SENT_SNAPSHOTS = "SENT_snapshots" +const SENT_TRACKER_CP = "SENT_tracker_cp" // single merged query for both checkpoint dates +const SENT_TORRENT_CP = "SENT_torrent_cp" +const SENT_CLIENTS = "SENT_clients" + +// The store is read by the mock's .from() handler +const store: Record = { + [SENT_TRACKERS]: [], + [SENT_SNAPSHOTS]: [], + [SENT_TRACKER_CP]: [], + [SENT_TORRENT_CP]: [], + [SENT_CLIENTS]: [], +} + +function seedStore( + trackers: unknown[] = [], + snapshots: unknown[] = [], + yesterdayCps: unknown[] = [], + dayBeforeCps: unknown[] = [], + torrentCps: unknown[] = [], + clients: unknown[] = [] +) { + store[SENT_TRACKERS] = trackers + store[SENT_SNAPSHOTS] = snapshots + // Both date ranges are now fetched in one inArray query; combine them here + store[SENT_TRACKER_CP] = [...yesterdayCps, ...dayBeforeCps] + store[SENT_TORRENT_CP] = torrentCps + store[SENT_CLIENTS] = clients +} + +// --------------------------------------------------------------------------- +// DB mock — vi.mock factory is hoisted; use ONLY inline literals here +// --------------------------------------------------------------------------- + +vi.mock("@/lib/db", () => { + // NOTE: Cannot reference outer variables here — this factory is hoisted. + // The store/storeDayBefore variables are module-level so they ARE accessible + // after hoisting as long as we reference them by closure (not by value at + // declaration time). Vitest hoists vi.mock but the factory closure still + // has access to module-level mutable state. + return { + db: { + select: vi.fn(() => ({ + from: vi.fn((table: string) => ({ + where: vi.fn((_cond: unknown) => { + // todaySnapshots has .orderBy() after .where() + if (table === "SENT_snapshots") { + return { + orderBy: vi.fn(() => Promise.resolve(store.SENT_snapshots ?? [])), + } + } + // trackerDailyCheckpoints is fetched in a single inArray query + if (table === "SENT_tracker_cp") { + return Promise.resolve(store.SENT_tracker_cp ?? []) + } + if (table === "SENT_torrent_cp") { + return Promise.resolve(store.SENT_torrent_cp ?? []) + } + if (table === "SENT_clients") { + return Promise.resolve(store.SENT_clients ?? []) + } + if (table === "SENT_trackers") { + return Promise.resolve(store.SENT_trackers ?? []) + } + return Promise.resolve([]) + }), + })), + })), + }, + } +}) + +// Schema mock — each table is a string sentinel matching the from() dispatch above +vi.mock("@/lib/db/schema", () => ({ + trackers: "SENT_trackers", + trackerSnapshots: "SENT_snapshots", + trackerDailyCheckpoints: "SENT_tracker_cp", + torrentDailyCheckpoints: "SENT_torrent_cp", + downloadClients: "SENT_clients", +})) + +vi.mock("drizzle-orm", () => ({ + eq: vi.fn((_col: unknown, _val: unknown) => ({ type: "eq" })), + gte: vi.fn((_col: unknown, _val: unknown) => ({ type: "gte" })), + inArray: vi.fn((_col: unknown, _vals: unknown) => ({ type: "inArray" })), + sql: vi.fn(() => ({ type: "sql" })), +})) + +// --------------------------------------------------------------------------- +// Import under test (after mocks) +// --------------------------------------------------------------------------- + +import { localDateStr } from "@/lib/formatters" +import { computeTodayAtAGlance } from "@/lib/today" + +// --------------------------------------------------------------------------- +// Factories +// --------------------------------------------------------------------------- + +function makeTracker( + id: number, + overrides: { qbtTag?: string | null; color?: string | null } = {} +) { + return { + id, + name: `Tracker ${id}`, + color: overrides.color ?? null, + qbtTag: overrides.qbtTag ?? null, + } +} + +function makeSnapshot( + trackerId: number, + polledAt: Date, + uploadedBytes: string, + downloadedBytes: string, + bufferBytes: string | null = null, + ratio: number | null = null, + seedbonus: number | null = null +) { + return { + id: Math.random(), + trackerId, + polledAt, + uploadedBytes, + downloadedBytes, + bufferBytes, + ratio, + seedbonus, + seedingCount: null, + leechingCount: null, + hitAndRuns: null, + warned: null, + freeleechTokens: null, + shareScore: null, + username: null, + group: null, + } +} + +function makeTrackerCheckpoint( + trackerId: number, + checkpointDate: string, + uploadedBytesEnd: string, + downloadedBytesEnd: string, + bufferBytesEnd: string | null = null +) { + return { + id: Math.random(), + trackerId, + checkpointDate, + uploadedBytesEnd, + downloadedBytesEnd, + bufferBytesEnd, + ratioEnd: null, + seedbonusEnd: null, + snapshotCount: 1, + } +} + +function makeTorrent( + hash: string, + name: string, + tags: string, + uploaded: number, + downloaded: number, + addedOn = 0, + completionOn = -1 +) { + return { + hash, + name, + state: "uploading", + tags, + category: "", + upspeed: 0, + dlspeed: 0, + uploaded, + downloaded, + ratio: 1.0, + size: downloaded || 1, + num_seeds: 5, + num_leechs: 0, + num_complete: 5, + num_incomplete: 0, + tracker: "", + added_on: addedOn, + completion_on: completionOn, + last_activity: 0, + seeding_time: 0, + time_active: 0, + seen_complete: 0, + availability: 1, + amount_left: 0, + progress: 1, + content_path: "", + save_path: "", + } +} + +function makeTorrentCheckpoint( + clientId: number, + hash: string, + uploadedStart: string, + downloadedStart: string +) { + return { + id: Math.random(), + clientId, + hash, + checkpointDate: localDateStr(), + uploadedStart, + downloadedStart, + } +} + +// --------------------------------------------------------------------------- +// beforeEach resets store to empty state +// --------------------------------------------------------------------------- + +beforeEach(() => { + seedStore() +}) + +// --------------------------------------------------------------------------- +// Empty state +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — empty state", () => { + it("returns zero fleet upload delta when there are no trackers", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.uploadDelta).toBe("0") + }) + + it("returns zero fleet download delta when there are no trackers", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.downloadDelta).toBe("0") + }) + + it("returns zero fleet buffer delta when there are no trackers", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.bufferDelta).toBe("0") + }) + + it("returns null ratioChange when there is no upload activity", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.ratioChange).toBeNull() + }) + + it("returns null yesterday delta values when no checkpoints exist", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.uploadDeltaYesterday).toBeNull() + expect(result.fleet.downloadDeltaYesterday).toBeNull() + expect(result.fleet.bufferDeltaYesterday).toBeNull() + }) + + it("returns empty trackers array", async () => { + const result = await computeTodayAtAGlance() + expect(result.trackers).toEqual([]) + }) + + it("returns zero activity counts", async () => { + const result = await computeTodayAtAGlance() + expect(result.activity.addedToday).toBe(0) + expect(result.activity.completedToday).toBe(0) + }) + + it("returns empty movers arrays", async () => { + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders).toHaveLength(0) + expect(result.movers.topDownloaders).toHaveLength(0) + }) +}) + +// --------------------------------------------------------------------------- +// Single tracker, two snapshots today +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — single tracker two snapshots today", () => { + const GiB = 1024 * 1024 * 1024 + + beforeEach(() => { + const now = new Date() + const tracker = makeTracker(1) + const earliest = makeSnapshot( + 1, + new Date(now.getTime() - 3600000), + String(10n * BigInt(GiB)), + String(5n * BigInt(GiB)) + ) + const latest = makeSnapshot( + 1, + now, + String(11n * BigInt(GiB)), + String(5n * BigInt(GiB) + 512n * 1024n * 1024n) + ) + seedStore([tracker], [earliest, latest]) + }) + + it("computes upload delta as last minus first snapshot value", async () => { + const result = await computeTodayAtAGlance() + expect(BigInt(result.fleet.uploadDelta)).toBe(BigInt(GiB)) + }) + + it("computes download delta correctly", async () => { + const result = await computeTodayAtAGlance() + expect(BigInt(result.fleet.downloadDelta)).toBe(512n * 1024n * 1024n) + }) + + it("exposes the tracker entry with the correct upload delta", async () => { + const result = await computeTodayAtAGlance() + expect(result.trackers).toHaveLength(1) + expect(result.trackers[0].id).toBe(1) + expect(BigInt(result.trackers[0].uploadDelta)).toBe(BigInt(GiB)) + }) +}) + +// --------------------------------------------------------------------------- +// Fewer than 2 snapshots +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — tracker with only one snapshot today", () => { + beforeEach(() => { + const tracker = makeTracker(1) + const snap = makeSnapshot(1, new Date(), "1000000000", "500000000") + seedStore([tracker], [snap]) + }) + + it("returns zero deltas when tracker has fewer than 2 snapshots today", async () => { + const result = await computeTodayAtAGlance() + expect(result.trackers[0].uploadDelta).toBe("0") + expect(result.trackers[0].downloadDelta).toBe("0") + }) +}) + +// --------------------------------------------------------------------------- +// Null bufferBytes handling +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — null bufferBytes on both snapshots", () => { + beforeEach(() => { + const tracker = makeTracker(1) + const earliest = makeSnapshot( + 1, + new Date(Date.now() - 3600000), + "1000000000", + "500000000", + null + ) + const latest = makeSnapshot(1, new Date(), "2000000000", "600000000", null) + seedStore([tracker], [earliest, latest]) + }) + + it("does not crash when bufferBytes is null and returns 0 buffer delta", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet.bufferDelta).toBe("0") + expect(result.trackers[0].bufferDelta).toBe("0") + }) +}) + +describe("computeTodayAtAGlance — null bufferBytes on earliest, value on latest", () => { + beforeEach(() => { + const tracker = makeTracker(1) + const earliest = makeSnapshot( + 1, + new Date(Date.now() - 3600000), + "1000000000", + "500000000", + null + ) + const latest = makeSnapshot(1, new Date(), "2000000000", "600000000", "1073741824") + seedStore([tracker], [earliest, latest]) + }) + + it("treats null bufferBytes as 0n so delta equals the latest buffer value", async () => { + const result = await computeTodayAtAGlance() + expect(BigInt(result.trackers[0].bufferDelta)).toBe(1073741824n) + }) +}) + +// --------------------------------------------------------------------------- +// Yesterday comparison via checkpoint pairs +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — yesterday comparison", () => { + const yesterdayStr = localDateStr(Date.now() - 86400000) + const dayBeforeStr = localDateStr(Date.now() - 172800000) + + it("computes fleet upload and download deltas for yesterday", async () => { + const tracker = makeTracker(1) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "2000000000", "1000000000") + const snap2 = makeSnapshot(1, new Date(), "3000000000", "1500000000") + // Yesterday ended at 2G upload, day-before ended at 1G → delta = 1G + const yestCp = makeTrackerCheckpoint(1, yesterdayStr, "2000000000", "1000000000") + const dayBeforeCp = makeTrackerCheckpoint(1, dayBeforeStr, "1000000000", "500000000") + seedStore([tracker], [snap1, snap2], [yestCp], [dayBeforeCp]) + + const result = await computeTodayAtAGlance() + expect(result.fleet.uploadDeltaYesterday).toBe("1000000000") + expect(result.fleet.downloadDeltaYesterday).toBe("500000000") + }) + + it("returns null when yesterday checkpoint exists but no day-before checkpoint", async () => { + const tracker = makeTracker(1) + const snap = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const yestCp = makeTrackerCheckpoint(1, yesterdayStr, "2000000000", "1000000000") + seedStore([tracker], [snap], [yestCp], []) + + const result = await computeTodayAtAGlance() + expect(result.fleet.uploadDeltaYesterday).toBeNull() + expect(result.fleet.downloadDeltaYesterday).toBeNull() + }) + + it("returns null when checkpoints exist for a different tracker id", async () => { + const tracker = makeTracker(1) + const snap = makeSnapshot(1, new Date(), "2000000000", "1000000000") + // trackerId 99 does not match tracker 1 + const yestCp = makeTrackerCheckpoint(99, yesterdayStr, "2000000000", "1000000000") + const dayBeforeCp = makeTrackerCheckpoint(99, dayBeforeStr, "1000000000", "500000000") + seedStore([tracker], [snap], [yestCp], [dayBeforeCp]) + + const result = await computeTodayAtAGlance() + expect(result.fleet.uploadDeltaYesterday).toBeNull() + }) + + it("treats null bufferBytesEnd as 0n in yesterday buffer delta", async () => { + const tracker = makeTracker(1) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const yestCp = makeTrackerCheckpoint(1, yesterdayStr, "2000000000", "1000000000", null) + const dayBeforeCp = makeTrackerCheckpoint(1, dayBeforeStr, "1000000000", "500000000", null) + seedStore([tracker], [snap1, snap2], [yestCp], [dayBeforeCp]) + + const result = await computeTodayAtAGlance() + expect(result.fleet.bufferDeltaYesterday).toBe("0") + }) +}) + +// --------------------------------------------------------------------------- +// Torrent movers tag filtering +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — torrent movers tag filtering", () => { + it("excludes torrents whose tags do not match any tracker qbtTag", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("hash1", "Some Movie", "other-tracker", 5000000000, 3000000000) + const cp = makeTorrentCheckpoint(1, "hash1", "0", "0") + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [cp], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders).toHaveLength(0) + expect(result.movers.topDownloaders).toHaveLength(0) + }) + + it("includes torrents whose tags match a tracker's qbtTag", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("hash1", "Some Movie", "aither", 5000000000, 3000000000) + const cp = makeTorrentCheckpoint(1, "hash1", "0", "0") + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [cp], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders).toHaveLength(1) + expect(result.movers.topUploaders[0].hash).toBe("hash1") + expect(result.movers.topUploaders[0].qbtTag).toBe("aither") + }) + + it("excludes torrents with zero upload delta from topUploaders", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + // uploaded matches checkpoint start → upload delta = 0 + const torrent = makeTorrent("hash1", "Stalled Movie", "aither", 1000000000, 3000000000) + const cp = makeTorrentCheckpoint(1, "hash1", "1000000000", "0") + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [cp], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders).toHaveLength(0) + }) + + it("excludes torrents with zero download delta from topDownloaders", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + // downloaded matches checkpoint start → download delta = 0 + const torrent = makeTorrent("hash1", "Completed Movie", "aither", 5000000000, 3000000000) + const cp = makeTorrentCheckpoint(1, "hash1", "0", "3000000000") + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [cp], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topDownloaders).toHaveLength(0) + }) + + it("caps topUploaders and topDownloaders at 5 entries each", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrents = Array.from({ length: 8 }, (_, i) => + makeTorrent(`hash${i}`, `Movie ${i}`, "aither", (i + 1) * 1000000000, (i + 1) * 500000000) + ) + const cps = torrents.map((t) => makeTorrentCheckpoint(1, t.hash, "0", "0")) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify(torrents) } + seedStore([tracker], [snap1, snap2], [], [], cps, [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders.length).toBeLessThanOrEqual(5) + expect(result.movers.topDownloaders.length).toBeLessThanOrEqual(5) + }) + + it("excludes torrents that have no matching torrent checkpoint entry", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent( + "hash-no-cp", + "No Checkpoint Movie", + "aither", + 5000000000, + 3000000000 + ) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + // No checkpoint in torrent CP list + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders).toHaveLength(0) + }) + + it("attaches tracker color to matched mover entries", async () => { + const tracker = makeTracker(1, { qbtTag: "aither", color: "#00d4ff" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("hash1", "Colored Movie", "aither", 5000000000, 3000000000) + const cp = makeTorrentCheckpoint(1, "hash1", "0", "0") + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [cp], [client]) + + const result = await computeTodayAtAGlance() + expect(result.movers.topUploaders[0].trackerColor).toBe("#00d4ff") + }) +}) + +// --------------------------------------------------------------------------- +// Fleet aggregation — multiple trackers +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — fleet aggregation with multiple trackers", () => { + it("sums upload deltas across all trackers", async () => { + const tracker1 = makeTracker(1) + const tracker2 = makeTracker(2) + const now = Date.now() + // tracker1: 3G - 1G = 2G; tracker2: 4G - 2G = 2G; total = 4G + const snaps = [ + makeSnapshot(1, new Date(now - 3600000), "1000000000", "500000000"), + makeSnapshot(2, new Date(now - 3600000), "2000000000", "1000000000"), + makeSnapshot(1, new Date(now), "3000000000", "600000000"), + makeSnapshot(2, new Date(now), "4000000000", "1200000000"), + ] + seedStore([tracker1, tracker2], snaps) + + const result = await computeTodayAtAGlance() + expect(BigInt(result.fleet.uploadDelta)).toBe(4000000000n) + }) + + it("sums download deltas across all trackers", async () => { + const tracker1 = makeTracker(1) + const tracker2 = makeTracker(2) + const now = Date.now() + // tracker1 dl: 700M - 500M = 200M; tracker2 dl: 500M - 200M = 300M; total = 500M + const snaps = [ + makeSnapshot(1, new Date(now - 3600000), "1000000000", "500000000"), + makeSnapshot(2, new Date(now - 3600000), "1000000000", "200000000"), + makeSnapshot(1, new Date(now), "2000000000", "700000000"), + makeSnapshot(2, new Date(now), "2000000000", "500000000"), + ] + seedStore([tracker1, tracker2], snaps) + + const result = await computeTodayAtAGlance() + expect(BigInt(result.fleet.downloadDelta)).toBe(500000000n) + }) + + it("tracker with no snapshots contributes zero and still appears in trackers array", async () => { + const tracker1 = makeTracker(1) + const tracker2 = makeTracker(2) // no snapshots + const now = Date.now() + const snaps = [ + makeSnapshot(1, new Date(now - 3600000), "1000000000", "500000000"), + makeSnapshot(1, new Date(now), "2000000000", "600000000"), + ] + seedStore([tracker1, tracker2], snaps) + + const result = await computeTodayAtAGlance() + expect(BigInt(result.fleet.uploadDelta)).toBe(1000000000n) + expect(result.trackers).toHaveLength(2) + expect(result.trackers.find((t) => t.id === 2)?.uploadDelta).toBe("0") + }) +}) + +// --------------------------------------------------------------------------- +// Ratio weighted average +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — ratio weighted average", () => { + it("weights ratio change by upload volume — more upload means more weight", async () => { + const tracker1 = makeTracker(1) + const tracker2 = makeTracker(2) + const now = Date.now() + // tracker1: +2G upload, ratio +0.1; tracker2: +0.5G upload, ratio +1.0 + // weighted avg = (0.1 * 2G + 1.0 * 0.5G) / (2G + 0.5G) + // = (0.2G + 0.5G) / 2.5G = 0.7 / 2.5 = 0.28 + const snaps = [ + makeSnapshot(1, new Date(now - 3600000), "2000000000", "2000000000", null, 1.0), + makeSnapshot(2, new Date(now - 3600000), "500000000", "500000000", null, 1.0), + makeSnapshot(1, new Date(now), "4000000000", "2000000000", null, 1.1), + makeSnapshot(2, new Date(now), "1000000000", "500000000", null, 2.0), + ] + seedStore([tracker1, tracker2], snaps) + + const result = await computeTodayAtAGlance() + expect(result.fleet.ratioChange).not.toBeNull() + expect(result.fleet.ratioChange as number).toBeCloseTo(0.28, 5) + }) + + it("returns null ratioChange when all upload deltas are zero (no progress this session)", async () => { + const tracker = makeTracker(1) + // Both snapshots have same uploaded bytes — delta = 0, weight = 0 + const snap1 = makeSnapshot( + 1, + new Date(Date.now() - 3600000), + "1000000000", + "500000000", + null, + 1.0 + ) + const snap2 = makeSnapshot(1, new Date(), "1000000000", "500000000", null, 1.5) + seedStore([tracker], [snap1, snap2]) + + const result = await computeTodayAtAGlance() + expect(result.fleet.ratioChange).toBeNull() + }) +}) + +// --------------------------------------------------------------------------- +// Activity counts +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — activity counts", () => { + it("counts torrents whose added_on unix timestamp falls on today", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + + const todayNoon = new Date() + todayNoon.setHours(12, 0, 0, 0) + const torrent = makeTorrent( + "h1", + "New Movie", + "aither", + 0, + 0, + Math.floor(todayNoon.getTime() / 1000), + -1 + ) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.activity.addedToday).toBe(1) + }) + + it("does not count torrents with added_on of 0 (unix epoch, not today)", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("h1", "Old Movie", "aither", 0, 0, 0, -1) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.activity.addedToday).toBe(0) + }) + + it("counts torrents whose completion_on unix timestamp falls on today", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + + const todayNoon = new Date() + todayNoon.setHours(12, 0, 0, 0) + const torrent = makeTorrent( + "h1", + "Done Movie", + "aither", + 0, + 5000000000, + 0, + Math.floor(todayNoon.getTime() / 1000) + ) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.activity.completedToday).toBe(1) + }) + + it("ignores completion_on of -1 (torrent is still downloading)", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("h1", "Seeding Movie", "aither", 5000000000, 5000000000, 0, -1) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.activity.completedToday).toBe(0) + }) + + it("ignores completion_on of 0 (qBittorrent sentinel for 'not completed')", async () => { + const tracker = makeTracker(1, { qbtTag: "aither" }) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "1000000000") + const torrent = makeTorrent("h1", "Seeding Movie", "aither", 5000000000, 5000000000, 0, 0) + const client = { id: 1, name: "qBit", cachedTorrents: JSON.stringify([torrent]) } + seedStore([tracker], [snap1, snap2], [], [], [], [client]) + + const result = await computeTodayAtAGlance() + expect(result.activity.completedToday).toBe(0) + }) +}) + +// --------------------------------------------------------------------------- +// Output shape invariants +// --------------------------------------------------------------------------- + +describe("computeTodayAtAGlance — output shape", () => { + it("fleet object has all required keys", async () => { + const result = await computeTodayAtAGlance() + expect(result.fleet).toHaveProperty("uploadDelta") + expect(result.fleet).toHaveProperty("downloadDelta") + expect(result.fleet).toHaveProperty("bufferDelta") + expect(result.fleet).toHaveProperty("ratioChange") + expect(result.fleet).toHaveProperty("seedbonusChange") + expect(result.fleet).toHaveProperty("uploadDeltaYesterday") + expect(result.fleet).toHaveProperty("downloadDeltaYesterday") + expect(result.fleet).toHaveProperty("bufferDeltaYesterday") + }) + + it("fleet delta values are strings (bigints serialized for JSON safety)", async () => { + const result = await computeTodayAtAGlance() + expect(typeof result.fleet.uploadDelta).toBe("string") + expect(typeof result.fleet.downloadDelta).toBe("string") + expect(typeof result.fleet.bufferDelta).toBe("string") + }) + + it("movers has topUploaders and topDownloaders as arrays", async () => { + const result = await computeTodayAtAGlance() + expect(Array.isArray(result.movers.topUploaders)).toBe(true) + expect(Array.isArray(result.movers.topDownloaders)).toBe(true) + }) + + it("each tracker entry has id, name, color, uploadDelta, downloadDelta, bufferDelta", async () => { + const tracker = makeTracker(1) + const snap1 = makeSnapshot(1, new Date(Date.now() - 3600000), "1000000000", "500000000") + const snap2 = makeSnapshot(1, new Date(), "2000000000", "600000000") + seedStore([tracker], [snap1, snap2]) + + const result = await computeTodayAtAGlance() + const t = result.trackers[0] + expect(t).toHaveProperty("id") + expect(t).toHaveProperty("name") + expect(t).toHaveProperty("color") + expect(t).toHaveProperty("uploadDelta") + expect(t).toHaveProperty("downloadDelta") + expect(t).toHaveProperty("bufferDelta") + }) +}) diff --git a/src/lib/client-scheduler.ts b/src/lib/client-scheduler.ts index 43074256..31f2ce0d 100644 --- a/src/lib/client-scheduler.ts +++ b/src/lib/client-scheduler.ts @@ -10,6 +10,10 @@ // // Functions: heartbeatClient, heartbeatAllClients, deepPollClient, deepPollAllClients, // startClientScheduler, stopClientScheduler, ensureClientSchedulerRunning +// +// Side effects of deepPollClient: +// - Writes torrent daily checkpoints (torrentDailyCheckpoints) for "Movers & Shakers". +// Uses onConflictDoNothing so the first poll of the day wins; subsequent polls skip. import { eq, isNotNull, lt, sql } from "drizzle-orm" import cron, { type ScheduledTask } from "node-cron" @@ -20,9 +24,11 @@ import { clientSnapshots, clientUptimeBuckets, downloadClients, + torrentDailyCheckpoints, trackers, } from "@/lib/db/schema" import { sanitizeNetworkError } from "@/lib/error-utils" +import { localDateStr } from "@/lib/formatters" import { log } from "@/lib/logger" import type { QbtTorrent } from "@/lib/qbt" import { @@ -38,7 +44,7 @@ import { } from "@/lib/qbt" import { clearUptimeAccumulator, flushCompletedBuckets, recordHeartbeat } from "@/lib/uptime" -/** Columns needed by heartbeatClient — excludes large blobs like cachedTorrents */ +/** Columns needed by heartbeatClient. Excludes large blobs like cachedTorrents */ export const HEARTBEAT_COLUMNS = { id: downloadClients.id, enabled: downloadClients.enabled, @@ -50,7 +56,7 @@ export const HEARTBEAT_COLUMNS = { encryptedPassword: downloadClients.encryptedPassword, } as const -/** Columns needed by deepPollClient — heartbeat fields + poll config + tags */ +/** Columns needed by deepPollClient. Heartbeat fields + poll config + tags */ export const DEEP_POLL_COLUMNS = { ...HEARTBEAT_COLUMNS, crossSeedTags: downloadClients.crossSeedTags, @@ -82,7 +88,7 @@ function setDeepPollTask(task: ScheduledTask | null) { } // --------------------------------------------------------------------------- -// Heartbeat — lightweight speed + connection check +// Heartbeat // --------------------------------------------------------------------------- async function heartbeatClient(clientId: number, encryptionKey: Buffer): Promise { @@ -112,7 +118,7 @@ async function heartbeatClient(clientId: number, encryptionKey: Buffer): Promise await db .update(downloadClients) - .set({ lastPolledAt: new Date(), lastError: null, errorSince: null, updatedAt: new Date() }) + .set({ lastError: null, errorSince: null, updatedAt: new Date() }) .where(eq(downloadClients.id, clientId)) } catch (error) { recordHeartbeat(clientId, false) @@ -142,7 +148,7 @@ async function heartbeatAllClients(encryptionKey: Buffer): Promise { } // --------------------------------------------------------------------------- -// Deep poll — full torrent list + per-tag aggregation +// Deep poll // --------------------------------------------------------------------------- export async function deepPollClient(clientId: number, encryptionKey: Buffer): Promise { @@ -187,7 +193,7 @@ export async function deepPollClient(clientId: number, encryptionKey: Buffer): P for (const result of tagSettled) { if (result.status !== "fulfilled") continue for (const t of result.value) { - if (!t.isPrivate || seen.has(t.hash)) continue + if (seen.has(t.hash)) continue seen.add(t.hash) deduped.push(t) } @@ -200,6 +206,33 @@ export async function deepPollClient(clientId: number, encryptionKey: Buffer): P `[deep-poll] client=${clientId} → ${torrents.length} relevant torrents (${allTags.length} tags)` ) + // Write daily torrent checkpoints for "Movers & Shakers" — first-seen-today wins + const checkpointDate = localDateStr() + const checkpointable = torrents.filter( + (t) => t.uploaded != null && t.downloaded != null && t.hash && t.name + ) + if (checkpointable.length > 0) { + try { + await db + .insert(torrentDailyCheckpoints) + .values( + checkpointable.map((t) => ({ + clientId, + hash: t.hash, + name: t.name, + checkpointDate, + uploadedStart: BigInt(t.uploaded), + downloadedStart: BigInt(t.downloaded), + })) + ) + .onConflictDoNothing() + } catch (err) { + log.warn( + `Torrent checkpoint insert failed for client ${clientId}: ${err instanceof Error ? err.message : "Unknown"}` + ) + } + } + const stats = aggregateByTag(torrents, trackerTags, crossSeedTags) // Cache the filtered torrent list for fallback when client is offline. @@ -280,7 +313,7 @@ export function startClientScheduler(encryptionKey: Buffer): void { log.error(error, "Initial deep poll error") }) - // Heartbeat: every 5 seconds — lightweight speed + connection check + // Heartbeat: every 5 seconds const hbTask = cron.schedule("*/5 * * * * *", async () => { if (heartbeatInFlight) return heartbeatInFlight = true @@ -295,7 +328,7 @@ export function startClientScheduler(encryptionKey: Buffer): void { }) setHeartbeatTask(hbTask) - // Deep poll: every 5 minutes — full torrent list + tag aggregation + // Deep poll const dpTask = cron.schedule("*/5 * * * *", async () => { if (deepPollInFlight) return deepPollInFlight = true @@ -334,7 +367,7 @@ export function stopClientScheduler(): void { } clearAllSessions() clearSpeedCache() - // Best-effort flush of any completed uptime buckets before clearing + flushCompletedBuckets().catch(() => {}) clearUptimeAccumulator() } diff --git a/src/lib/formatters.ts b/src/lib/formatters.ts index 58d0f5c5..bfdc2c9a 100644 --- a/src/lib/formatters.ts +++ b/src/lib/formatters.ts @@ -1,6 +1,10 @@ // src/lib/formatters.ts // -// Functions: formatBytesFromString, bytesToGiB, formatBytesNum, formatRatio, formatAccountAge, formatJoinedDate, hexToRgba, hexToHsl, hslToHex, generatePalette, getComplementaryColor, formatStatValue, computeDelta, formatDuration, formatTimeAgo, splitValueUnit +// Functions: formatBytesFromString, bytesToGiB, formatBytesNum, +// formatRatio, formatAccountAge, formatJoinedDate, hexToRgba, hexToHsl, +// hslToHex, generatePalette, getComplementaryColor, formatStatValue, +// computeDelta, formatDuration, formatTimeAgo, splitValueUnit, compareBigIntDesc, +// computePctChange, localDateStr, isUnixTimestampOnDate import type { Snapshot, TrackerLatestStats } from "@/types/api" @@ -14,7 +18,9 @@ export function formatBytesFromString(bytesStr: string | null | undefined): stri const tib = bytes / 1024 ** 4 if (tib >= 1) return `${tib.toFixed(2)} TiB` const gib = bytes / 1024 ** 3 - return `${gib.toFixed(2)} GiB` + if (gib >= 1) return `${gib.toFixed(2)} GiB` + const mib = bytes / 1024 ** 2 + return `${Math.round(mib)} MiB` } /** @@ -291,3 +297,41 @@ export function splitValueUnit(formatted: string): { num: string; unit: string } if (idx === -1) return { num: formatted, unit: "" } return { num: formatted.slice(0, idx), unit: formatted.slice(idx + 1) } } + +/** Comparator for sorting bigint values in descending order. */ +export function compareBigIntDesc(a: bigint, b: bigint): number { + if (b > a) return 1 + if (b < a) return -1 + return 0 +} + +/** Compute percentage change between two bigint-encoded decimal strings. */ +export function computePctChange(today: string, yesterday: string | null): number | null { + if (yesterday === null) return null + try { + const y = Number(BigInt(yesterday)) + if (y === 0) return null + const t = Number(BigInt(today)) + return ((t - y) / y) * 100 + } catch { + return null + } +} + +/** + * Returns a YYYY-MM-DD date string in the server's local timezone (respects TZ env). + * Use this instead of `.toISOString().slice(0, 10)` which always returns UTC. + */ +export function localDateStr(date?: Date | number): string { + const d = date instanceof Date ? date : date !== undefined ? new Date(date) : new Date() + return d.toLocaleDateString("en-CA") +} + +/** + * Returns true if a unix timestamp (seconds) falls on the given date string (YYYY-MM-DD) + * in the server's local timezone. Returns false for timestamps <= 0. + */ +export function isUnixTimestampOnDate(unixSeconds: number, dateStr: string): boolean { + if (unixSeconds <= 0) return false + return localDateStr(new Date(unixSeconds * 1000)) === dateStr +} diff --git a/src/lib/nuke.ts b/src/lib/nuke.ts index e55b1bc5..c4ed8866 100644 --- a/src/lib/nuke.ts +++ b/src/lib/nuke.ts @@ -1,6 +1,4 @@ // src/lib/nuke.ts -// -// Functions: scrubAndDeleteAll import { randomBytes } from "node:crypto" import { db } from "@/lib/db" @@ -14,6 +12,8 @@ import { notificationTargets, tagGroupMembers, tagGroups, + torrentDailyCheckpoints, + trackerDailyCheckpoints, trackerRoles, trackerSnapshots, trackers, @@ -73,6 +73,8 @@ export async function scrubAndDeleteAll(): Promise { await tx.delete(dismissedAlerts) await tx.delete(clientUptimeBuckets) + await tx.delete(torrentDailyCheckpoints) + await tx.delete(trackerDailyCheckpoints) await tx.delete(clientSnapshots) await tx.delete(trackerSnapshots) await tx.delete(trackerRoles) diff --git a/src/lib/qbt/aggregator.ts b/src/lib/qbt/aggregator.ts index 4f47738d..9033c32a 100644 --- a/src/lib/qbt/aggregator.ts +++ b/src/lib/qbt/aggregator.ts @@ -1,6 +1,4 @@ // src/lib/qbt/aggregator.ts -// -// Functions: aggregateByTag import { LEECHING_STATES, parseTorrentTags, SEEDING_STATES } from "@/lib/fleet" import type { ClientStats, QbtTorrent, TagStats } from "./types" @@ -12,11 +10,10 @@ export function aggregateByTag( ): ClientStats { const knownTags = [...trackerTags, ...crossSeedTags] - // Build a map of tag → accumulator const tagMap = new Map() for (const tag of knownTags) { - tagMap.set(tag, { - tag, + tagMap.set(tag.toLowerCase(), { + tag: tag.toLowerCase(), seedingCount: 0, leechingCount: 0, uploadSpeed: 0, diff --git a/src/lib/qbt/client.ts b/src/lib/qbt/client.ts index 02f2c3c0..aa3df638 100644 --- a/src/lib/qbt/client.ts +++ b/src/lib/qbt/client.ts @@ -1,15 +1,16 @@ // src/lib/qbt/client.ts // // Available functions: -// buildBaseUrl - Construct base URL from host/port/ssl -// login - Authenticate with qBittorrent Web API, returns SID cookie -// getSession - Return cached SID or perform a fresh login -// invalidateSession - Remove a cached SID (i.e after 403) -// clearAllSessions - Remove all cached SIDs (called on logout) -// withSessionRetry - Run an operation with automatic session retry on expiry -// qbtFetch - Shared fetch + error handler for authenticated qBT requests -// getTorrents - Fetch torrent info from qBittorrent (optionally filtered by tag) -// getTransferInfo - Fetch global transfer stats from qBittorrent +// buildBaseUrl - Construct base URL from host/port/ssl +// login - Authenticate with qBittorrent Web API, returns SID cookie +// getSession - Return cached SID or perform a fresh login +// invalidateSession - Remove a cached SID (i.e after 403) +// clearAllSessions - Remove all cached SIDs (called on logout) +// withSessionRetry - Run an operation with automatic session retry on expiry +// qbtFetch - Shared fetch + error handler for authenticated qBT requests +// parseCachedTorrents - Safely parse JSONB cachedTorrents column (string or object) +// getTorrents - Fetch torrent info from qBittorrent (optionally filtered by tag) +// getTransferInfo - Fetch global transfer stats from qBittorrent import type { QbtTorrent, QbtTransferInfo } from "./types" @@ -18,14 +19,13 @@ import type { QbtTorrent, QbtTransferInfo } from "./types" * TypeError("fetch failed") { cause: Error("ECONNREFUSED ...") } */ function describeFetchError(err: unknown): string { - // Unwrap Node fetch's cause chain for the real error const cause = err !== null && typeof err === "object" && "cause" in (err as object) ? (err as { cause: unknown }).cause : undefined if (cause instanceof Error) { const code = "code" in cause ? (cause as NodeJS.ErrnoException).code : undefined - if (code) return code // i.e ECONNREFUSED, ENOTFOUND, ETIMEDOUT + if (code) return code if (cause.message) return cause.message } @@ -45,8 +45,7 @@ export function buildBaseUrl(host: string, port: number, ssl: boolean): string { } // --------------------------------------------------------------------------- -// SID session cache — avoids re-authenticating on every poll cycle. -// SIDs are cached per baseUrl and reused until a 403 invalidates them. +// SID session cache to avoid re-authenticating on every poll cycle. // --------------------------------------------------------------------------- const gSid = globalThis as typeof globalThis & { @@ -195,6 +194,23 @@ async function qbtFetch( return response } +/** + * Parses the cachedTorrents JSONB column + */ +export function parseCachedTorrents(raw: unknown): QbtTorrent[] { + if (!raw) return [] + if (typeof raw === "string") { + try { + const parsed = JSON.parse(raw) + return Array.isArray(parsed) ? (parsed as QbtTorrent[]) : [] + } catch { + return [] + } + } + if (Array.isArray(raw)) return raw as QbtTorrent[] + return [] +} + export async function getTorrents( baseUrl: string, sid: string, diff --git a/src/lib/qbt/index.ts b/src/lib/qbt/index.ts index b61018c0..5efcf6f8 100644 --- a/src/lib/qbt/index.ts +++ b/src/lib/qbt/index.ts @@ -9,6 +9,7 @@ export { getTransferInfo, invalidateSession, login, + parseCachedTorrents, withSessionRetry, } from "./client" export type { SpeedSnapshot } from "./speed-cache" diff --git a/src/lib/qbt/qbt.test.ts b/src/lib/qbt/qbt.test.ts index fa353ec6..53850c82 100644 --- a/src/lib/qbt/qbt.test.ts +++ b/src/lib/qbt/qbt.test.ts @@ -334,6 +334,10 @@ describe("getTransferInfo", () => { // --------------------------------------------------------------------------- describe("aggregateByTag", () => { + // Regression: prevents isPrivate camelCase/snake_case mismatch from masking real API shape. + // The real qBT API returns `is_private` (snake_case), not `isPrivate`. Tests that set + // `isPrivate: true` would hide bugs that relied on that field being defined. This factory + // deliberately omits it to match the real API response shape. function makeTorrent(overrides: Partial): QbtTorrent { return { hash: "deadbeef", @@ -363,7 +367,8 @@ describe("aggregateByTag", () => { progress: 1, content_path: "/downloads/Test Torrent", save_path: "/downloads", - isPrivate: true, + // isPrivate intentionally omitted — real qBT API returns `is_private` (snake_case). + // Only override explicitly in tests that specifically need to test isPrivate handling. ...overrides, } } @@ -517,3 +522,162 @@ describe("parseCrossSeedTags", () => { expect(parseCrossSeedTags(null)).toEqual([]) }) }) + +// --------------------------------------------------------------------------- +// Mock factory realism +// --------------------------------------------------------------------------- + +describe("makeTorrent factory shape", () => { + // Regression: prevents isPrivate camelCase mismatch from masking deep-poll dedup bug. + // The factory previously included `isPrivate: true`, which caused any code that + // checked `t.isPrivate` to behave differently from the real API (which returns + // `is_private` in snake_case). Tests built on the old factory would never catch + // bugs that depended on `t.isPrivate` being undefined. + it("produces API-realistic shape without isPrivate by default", () => { + // Re-declare the factory inline to confirm the standalone shape — this is the + // canonical check that should fail immediately if someone adds isPrivate back. + function makeTorrentForShapeCheck(overrides: Partial = {}): QbtTorrent { + return { + hash: "deadbeef", + name: "Test Torrent", + state: "uploading", + tags: "", + category: "", + upspeed: 0, + dlspeed: 0, + uploaded: 0, + downloaded: 0, + ratio: 0, + size: 0, + num_seeds: 0, + num_leechs: 0, + num_complete: 0, + num_incomplete: 0, + tracker: "", + added_on: 0, + completion_on: -1, + last_activity: 0, + seeding_time: 0, + time_active: 0, + seen_complete: 0, + availability: -1, + amount_left: 0, + progress: 1, + content_path: "/downloads/Test Torrent", + save_path: "/downloads", + ...overrides, + } + } + + const torrent = makeTorrentForShapeCheck() + + // The real qBT API does NOT return `isPrivate` (camelCase). Ensure the factory + // does not include it so tests built on this shape match real API responses. + expect(Object.hasOwn(torrent, "isPrivate")).toBe(false) + + // Core fields that the API does return must be present + expect(torrent).toHaveProperty("hash") + expect(torrent).toHaveProperty("state") + expect(torrent).toHaveProperty("tags") + }) +}) + +// --------------------------------------------------------------------------- +// aggregateByTag — case-insensitive tag matching +// --------------------------------------------------------------------------- + +describe("aggregateByTag case-insensitive tag matching", () => { + // Local helper that does NOT include isPrivate, matching real API shape + function makeRealTorrent(overrides: Partial): QbtTorrent { + return { + hash: "deadbeef", + name: "Test Torrent", + state: "uploading", + tags: "", + category: "", + upspeed: 0, + dlspeed: 0, + uploaded: 0, + downloaded: 0, + ratio: 0, + size: 0, + num_seeds: 0, + num_leechs: 0, + num_complete: 0, + num_incomplete: 0, + tracker: "", + added_on: 0, + completion_on: -1, + last_activity: 0, + seeding_time: 0, + time_active: 0, + seen_complete: 0, + availability: -1, + amount_left: 0, + progress: 1, + content_path: "/downloads/Test Torrent", + save_path: "/downloads", + ...overrides, + } + } + + // Regression: prevents tag case mismatch between DB-stored tags and parseTorrentTags output. + // aggregateByTag builds its internal map with lowercase keys. parseTorrentTags lowercases + // torrent tags by default. If the map were built with original-case keys (e.g. "Blutopia"), + // a torrent tagged "blutopia" (lowercased by parseTorrentTags) would never match and would + // fall into the untagged bucket instead. + it("matches torrent tags case-insensitively when DB tag has title case", () => { + // Torrent tags as parseTorrentTags returns them: lowercase + const torrents = [makeRealTorrent({ state: "uploading", tags: "blutopia", upspeed: 512 })] + // DB stores the tag with title case + const result = aggregateByTag(torrents, ["Blutopia"], []) + + const blutopiaStats = result.tagStats.find((t) => t.tag === "blutopia") + expect(blutopiaStats).toBeDefined() + expect(blutopiaStats?.seedingCount).toBe(1) + expect(blutopiaStats?.uploadSpeed).toBe(512) + + // Must NOT fall into the untagged bucket + const untagged = result.tagStats.find((t) => t.tag === "untagged") + expect(untagged).toBeUndefined() + }) + + // Regression: verifies the fix handles all common tracker tag casing patterns from DB. + it("handles mixed case tags from DB — ALL_CAPS, lowercase, TitleCase", () => { + const torrents = [ + makeRealTorrent({ state: "uploading", tags: "red", upspeed: 100 }), + makeRealTorrent({ state: "uploading", tags: "ops", upspeed: 200 }), + makeRealTorrent({ state: "uploading", tags: "nebulance", upspeed: 300 }), + ] + // DB may store these as "RED", "ops", "Nebulance" + const result = aggregateByTag(torrents, ["RED", "ops", "Nebulance"], []) + + const redStats = result.tagStats.find((t) => t.tag === "red") + const opsStats = result.tagStats.find((t) => t.tag === "ops") + const nebStats = result.tagStats.find((t) => t.tag === "nebulance") + + expect(redStats?.seedingCount).toBe(1) + expect(opsStats?.seedingCount).toBe(1) + expect(nebStats?.seedingCount).toBe(1) + + // No torrent should end up untagged + const untagged = result.tagStats.find((t) => t.tag === "untagged") + expect(untagged).toBeUndefined() + + expect(result.totalSeedingCount).toBe(3) + expect(result.uploadSpeedBytes).toBe(600) + }) + + it("cross-seed tags from DB are also lowercased for matching", () => { + const torrents = [makeRealTorrent({ state: "uploading", tags: "cross-seed", upspeed: 50 })] + // DB cross-seed tag stored with mixed case + const result = aggregateByTag(torrents, [], ["Cross-Seed"]) + + const csStats = result.tagStats.find((t) => t.tag === "cross-seed") + expect(csStats).toBeDefined() + expect(csStats?.seedingCount).toBe(1) + + const untagged = result.tagStats.find((t) => t.tag === "untagged") + expect(untagged).toBeUndefined() + }) +}) diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index d1684b54..bb302f35 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -1,6 +1,6 @@ // src/lib/scheduler.ts // -// Functions: pollTracker, pollAllTrackers, pruneOldSnapshots, startScheduler, stopScheduler, ensureSchedulerRunning, POLL_FAILURE_THRESHOLD +// Functions: pollTracker, pollAllTrackers, pruneOldSnapshots, pruneOldCheckpoints, startScheduler, stopScheduler, ensureSchedulerRunning, fetchTrackerStats, POLL_FAILURE_THRESHOLD import type { Agent as HttpAgent } from "node:http" import { and, desc, eq, isNotNull, lt, notInArray, sql } from "drizzle-orm" @@ -17,8 +17,16 @@ import { } from "@/lib/client-scheduler" import { decrypt } from "@/lib/crypto" import { db } from "@/lib/db" -import { appSettings, notificationTargets, trackerSnapshots, trackers } from "@/lib/db/schema" +import { + appSettings, + notificationTargets, + torrentDailyCheckpoints, + trackerDailyCheckpoints, + trackerSnapshots, + trackers, +} from "@/lib/db/schema" import { sanitizeNetworkError } from "@/lib/error-utils" +import { localDateStr } from "@/lib/formatters" import { log } from "@/lib/logger" import { dispatchNotifications } from "@/lib/notifications/dispatch" import { maskUsername } from "@/lib/privacy" @@ -27,7 +35,7 @@ import { getPauseState } from "@/lib/tracker-status" // Store on globalThis to survive HMR in development. // Without this, each hot-reload orphans the old cron job (it keeps firing) -// while creating a new one — causing duplicate polls that hammer tracker APIs. +// while creating a new one which causes duplicate polls that hammer tracker APIs. const g = globalThis as typeof globalThis & { __schedulerTask?: ScheduledTask | null __schedulerKey?: Buffer | null @@ -73,13 +81,14 @@ export async function fetchTrackerStats( let apiToken: string try { apiToken = decrypt(tracker.encryptedApiToken, encryptionKey) - } catch { - throw new Error(`API key is missing or invalid for tracker "${tracker.name}"`) + } catch (err) { + const cause = err instanceof Error ? err.message : String(err) + throw new Error(`API key is missing or invalid for tracker "${tracker.name}": ${cause}`) } const adapter = getAdapter(tracker.platformType) if (tracker.useProxy && !proxyAgent) { - throw new Error("Proxy required but not available — refusing to leak IP via direct connection") + throw new Error("Proxy required but not available, refusing to leak IP via direct connection") } const fetchOptions = buildFetchOptions(tracker.baseUrl, { @@ -131,13 +140,14 @@ export async function pollTracker( let apiToken: string try { apiToken = decrypt(tracker.encryptedApiToken, encryptionKey) - } catch (_err) { - throw new Error(`API key is missing or invalid for tracker "${tracker.name}"`) + } catch (err) { + const cause = err instanceof Error ? err.message : String(err) + throw new Error(`API key is missing or invalid for tracker "${tracker.name}": ${cause}`) } const adapter = getAdapter(tracker.platformType) if (tracker.useProxy && !proxyAgent) { throw new Error( - "Proxy required but not available — refusing to leak IP via direct connection" + "Proxy required but not available, refusing to leak IP via direct connection" ) } const fetchOptions = buildFetchOptions(tracker.baseUrl, { @@ -173,7 +183,7 @@ export async function pollTracker( await db.update(trackers).set(metaUpdates).where(eq(trackers.id, tracker.id)) } - // Fetch previous snapshot BEFORE inserting the new one — used for change detection in notifications + // Fetch previous snapshot before inserting the new one (used for change detection in notifications) const [previousSnapshot] = await db .select({ ratio: trackerSnapshots.ratio, @@ -207,6 +217,38 @@ export async function pollTracker( group: privacyMode ? maskUsername(stats.group) : stats.group, }) + try { + // Upsert daily checkpoint for "Today At A Glance" comparisons + const checkpointDate = localDateStr(timestamp) + await db + .insert(trackerDailyCheckpoints) + .values({ + trackerId: tracker.id, + checkpointDate, + uploadedBytesEnd: stats.uploadedBytes !== null ? BigInt(stats.uploadedBytes) : 0n, + downloadedBytesEnd: stats.downloadedBytes !== null ? BigInt(stats.downloadedBytes) : 0n, + bufferBytesEnd: stats.bufferBytes !== null ? BigInt(stats.bufferBytes) : null, + ratioEnd: stats.ratio, + seedbonusEnd: stats.seedbonus, + snapshotCount: 1, + }) + .onConflictDoUpdate({ + target: [trackerDailyCheckpoints.trackerId, trackerDailyCheckpoints.checkpointDate], + set: { + uploadedBytesEnd: stats.uploadedBytes !== null ? BigInt(stats.uploadedBytes) : 0n, + downloadedBytesEnd: stats.downloadedBytes !== null ? BigInt(stats.downloadedBytes) : 0n, + bufferBytesEnd: stats.bufferBytes !== null ? BigInt(stats.bufferBytes) : null, + ratioEnd: stats.ratio, + seedbonusEnd: stats.seedbonus, + snapshotCount: sql`${trackerDailyCheckpoints.snapshotCount} + 1`, + }, + }) + } catch (checkpointErr) { + log.warn( + `Daily checkpoint upsert failed for tracker ${tracker.id}: ${checkpointErr instanceof Error ? checkpointErr.message : "Unknown"}` + ) + } + try { await dispatchNotifications( { @@ -308,7 +350,7 @@ export async function pollTracker( enabledTargets ) } catch { - // security-audit-ignore: notification dispatch is non-critical — errors logged inside dispatchNotifications + // security-audit-ignore: notification dispatch is non-critical, errors logged inside dispatchNotifications } } } @@ -330,8 +372,26 @@ export async function pruneOldSnapshots(retentionDays: number): Promise return deleted.length } +export async function pruneOldCheckpoints(retentionDays: number): Promise { + const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000) + .toISOString() + .slice(0, 10) + + const deletedTracker = await db + .delete(trackerDailyCheckpoints) + .where(lt(trackerDailyCheckpoints.checkpointDate, cutoffDate)) + .returning({ id: trackerDailyCheckpoints.id }) + + const deletedTorrent = await db + .delete(torrentDailyCheckpoints) + .where(lt(torrentDailyCheckpoints.checkpointDate, cutoffDate)) + .returning({ id: torrentDailyCheckpoints.id }) + + return deletedTracker.length + deletedTorrent.length +} + export async function pollAllTrackers(encryptionKey: Buffer): Promise { - // Query settings first — global interval is needed for overdue filtering + // Query settings first; global interval is needed for overdue filtering const [settings] = await db .select({ storeUsernames: appSettings.storeUsernames, @@ -370,7 +430,7 @@ export async function pollAllTrackers(encryptionKey: Buffer): Promise { // Build proxy agent once for all trackers that need it const proxyAgent = settings ? buildProxyAgentFromSettings(settings, encryptionKey) : undefined - // Fetch notification targets once for the entire poll cycle — avoids N identical queries + // Fetch notification targets once for the entire poll cycle (avoids N identical queries) let enabledNotificationTargets: (typeof notificationTargets.$inferSelect)[] = [] try { enabledNotificationTargets = await db @@ -384,10 +444,10 @@ export async function pollAllTrackers(encryptionKey: Buffer): Promise { } // Capture a single timestamp for the whole batch so all snapshots in one - // cycle share the same polledAt value — simplifies time-series queries + // cycle share the same polledAt value, simplifying time-series queries const batchTimestamp = new Date() - // Poll all overdue trackers in parallel — one slow tracker won't block the rest + // Poll all overdue trackers in parallel so one slow tracker won't block the rest await Promise.allSettled( overdue.map((tracker) => pollTracker( @@ -411,6 +471,17 @@ export async function pollAllTrackers(encryptionKey: Buffer): Promise { } catch (error) { log.error(error, "Snapshot pruning failed") } + + try { + const prunedCheckpoints = await pruneOldCheckpoints(settings.snapshotRetentionDays) + if (prunedCheckpoints > 0) { + log.info( + `Pruned ${prunedCheckpoints} checkpoint rows older than ${settings.snapshotRetentionDays} days` + ) + } + } catch (error) { + log.error(error, "Checkpoint pruning failed") + } } // Prune expired dismissed alerts (stale-data and zero-seeding types expire after 24h) @@ -426,7 +497,7 @@ export function startScheduler(encryptionKey: Buffer): void { setSchedulerKey(encryptionKey) - // Poll immediately on start — don't wait for first 5-minute tick + // Poll immediately on start, don't wait for first 5-minute tick pollAllTrackers(encryptionKey).catch((error) => { log.error(error, "Initial poll error") }) @@ -468,7 +539,7 @@ export function stopScheduler(): void { stopBackupScheduler() } -/** Exposed for testing — returns the raw schedulerKey buffer reference. */ +/** Exposed for testing. Returns the raw schedulerKey buffer reference. */ export function _getSchedulerKeyForTest(): Buffer | null { return getSchedulerKey() } diff --git a/src/lib/server-data.ts b/src/lib/server-data.ts index f3da1f28..049dce53 100644 --- a/src/lib/server-data.ts +++ b/src/lib/server-data.ts @@ -44,7 +44,7 @@ import type { Snapshot, TagGroup, TagGroupChartType } from "@/types/api" * * Note: `hasProxyPassword` and `hasBackupPassword` select the encrypted column * references so the serializer can coerce them to booleans. The raw ciphertext - * is never returned to clients — serializeSettingsResponse() converts these to + * is never returned to clients and serializeSettingsResponse() converts these to * `!!value` before the data leaves the server. */ export const settingsColumns = { @@ -116,7 +116,7 @@ export function serializeSettingsResponse(row: SettingsRow) { } /** - * Convenience wrapper: fetches settings and serializes for the client. + * fetches settings and serializes for the client. * Returns null if no settings row exists (app not yet configured). */ export async function getSettingsForClient() { @@ -175,40 +175,16 @@ export async function getTrackerListForDashboard(): Promise { db.select({ storeUsernames: appSettings.storeUsernames }).from(appSettings).limit(1), ]) - // Enforce masking at response time -- even if DB has plaintext from before - // privacy was enabled, the API never leaks it when privacy mode is on. + // Enforce masking at response time // Fallback true = "store usernames" = no masking. Matches createPrivacyMask() // behavior when no settings row exists. Do NOT change to false. const mask = createPrivacyMaskSync(privacySettings?.storeUsernames ?? true) - // Batch-fetch the latest snapshot per tracker using DISTINCT ON. - // PG18's enable_distinct_reordering planner flag optimises exactly this pattern. - // Drizzle has no native DISTINCT ON support, so we use db.execute with a raw sql tag. I know, I know. - // security-audit-ignore: static SQL string with zero user input -- no injection risk - const latestSnapshots = (await db.execute(sql` - SELECT DISTINCT ON (tracker_id) - id, - tracker_id AS "trackerId", - polled_at AS "polledAt", - uploaded_bytes AS "uploadedBytes", - downloaded_bytes AS "downloadedBytes", - ratio, - buffer_bytes AS "bufferBytes", - seeding_count AS "seedingCount", - leeching_count AS "leechingCount", - seedbonus, - hit_and_runs AS "hitAndRuns", - required_ratio AS "requiredRatio", - warned, - freeleech_tokens AS "freeleechTokens", - share_score AS "shareScore", - username, - group_name AS "group" - FROM tracker_snapshots - ORDER BY tracker_id, polled_at DESC - `)) as unknown as (typeof trackerSnapshots.$inferSelect)[] - - // Build a lookup map for O(1) access + const latestSnapshots = await db + .selectDistinctOn([trackerSnapshots.trackerId]) + .from(trackerSnapshots) + .orderBy(trackerSnapshots.trackerId, desc(trackerSnapshots.polledAt)) + const snapshotByTracker = new Map(latestSnapshots.map((s) => [s.trackerId, s])) // SECURITY: Never include encryptedApiToken in response @@ -341,7 +317,7 @@ export async function getTagGroupsWithMembers(): Promise { /** * Fetches only id, name, and color for trackers that have useProxy enabled. - * Used by the settings page — avoids the heavy dashboard query for this narrow need. + * Used by the settings page */ export async function getProxyTrackers(): Promise<{ id: number; name: string; color: string }[]> { const rows = await db diff --git a/src/lib/today.ts b/src/lib/today.ts new file mode 100644 index 00000000..284dde01 --- /dev/null +++ b/src/lib/today.ts @@ -0,0 +1,447 @@ +// src/lib/today.ts +// +// Functions: computeTodayAtAGlance, backfillTrackerCheckpoints + +import "server-only" + +import { eq, gte, inArray, sql } from "drizzle-orm" +import { db } from "@/lib/db" +import { + downloadClients, + torrentDailyCheckpoints, + trackerDailyCheckpoints, + trackerSnapshots, + trackers, +} from "@/lib/db/schema" +import { parseTorrentTags } from "@/lib/fleet" +import { compareBigIntDesc, isUnixTimestampOnDate, localDateStr } from "@/lib/formatters" +import { log } from "@/lib/logger" +import { parseCachedTorrents } from "@/lib/qbt/client" +import type { TodayAtAGlance } from "@/types/api" + +interface TrackerDelta { + id: number + name: string + color: string | null + uploadDelta: bigint + downloadDelta: bigint + bufferDelta: bigint + ratioChange: number + seedbonusChange: number +} + +function zeroDelta(tracker: { id: number; name: string; color: string | null }): TrackerDelta { + return { + id: tracker.id, + name: tracker.name, + color: tracker.color ?? null, + uploadDelta: 0n, + downloadDelta: 0n, + bufferDelta: 0n, + ratioChange: 0, + seedbonusChange: 0, + } +} + +interface TorrentMover { + hash: string + name: string + qbtTag: string | null + trackerColor: string | null + clientName: string | null + uploadedToday: bigint + downloadedToday: bigint +} + +/** + * Computes the entire TodayAtAGlance payload in a single call. + * Fetches all required data in parallel where possible, then aggregates + * fleet-level deltas, yesterday comparisons, torrent movers, and activity counts. + */ +export async function computeTodayAtAGlance(): Promise { + // ── Date boundaries (local timezone) ────────────────────────────────────── + const now = Date.now() + const todayStr = localDateStr() + // Start of today in local timezone for snapshot filtering + const todayStart = new Date(`${todayStr}T00:00:00`) + const yesterdayStr = localDateStr(now - 86400000) + const dayBeforeStr = localDateStr(now - 172800000) + + // ── Parallel data fetches ───────────────────────────────────────────────── + const [allTrackers, todaySnapshots, checkpointRows, torrentCps, clients] = await Promise.all([ + db + .select({ + id: trackers.id, + name: trackers.name, + color: trackers.color, + qbtTag: trackers.qbtTag, + }) + .from(trackers) + .where(eq(trackers.isActive, true)), + + db + .select({ + trackerId: trackerSnapshots.trackerId, + polledAt: trackerSnapshots.polledAt, + uploadedBytes: trackerSnapshots.uploadedBytes, + downloadedBytes: trackerSnapshots.downloadedBytes, + bufferBytes: trackerSnapshots.bufferBytes, + ratio: trackerSnapshots.ratio, + seedbonus: trackerSnapshots.seedbonus, + }) + .from(trackerSnapshots) + .where(gte(trackerSnapshots.polledAt, todayStart)) + .orderBy(trackerSnapshots.polledAt), + + db + .select({ + trackerId: trackerDailyCheckpoints.trackerId, + checkpointDate: trackerDailyCheckpoints.checkpointDate, + uploadedBytesEnd: trackerDailyCheckpoints.uploadedBytesEnd, + downloadedBytesEnd: trackerDailyCheckpoints.downloadedBytesEnd, + bufferBytesEnd: trackerDailyCheckpoints.bufferBytesEnd, + }) + .from(trackerDailyCheckpoints) + .where(inArray(trackerDailyCheckpoints.checkpointDate, [yesterdayStr, dayBeforeStr])), + + db + .select({ + clientId: torrentDailyCheckpoints.clientId, + hash: torrentDailyCheckpoints.hash, + uploadedStart: torrentDailyCheckpoints.uploadedStart, + downloadedStart: torrentDailyCheckpoints.downloadedStart, + }) + .from(torrentDailyCheckpoints) + .where(eq(torrentDailyCheckpoints.checkpointDate, todayStr)), + + db + .select({ + id: downloadClients.id, + name: downloadClients.name, + cachedTorrents: downloadClients.cachedTorrents, + cachedTorrentsAt: downloadClients.cachedTorrentsAt, + }) + .from(downloadClients) + .where(eq(downloadClients.enabled, true)), + ]) + + const yesterdayCheckpoints = checkpointRows.filter((r) => r.checkpointDate === yesterdayStr) + const dayBeforeCheckpoints = checkpointRows.filter((r) => r.checkpointDate === dayBeforeStr) + + // ── Per-tracker deltas from today's snapshots ───────────────────────────── + + // Group snapshots by trackerId + const snapshotsByTracker = new Map() + for (const snap of todaySnapshots) { + const bucket = snapshotsByTracker.get(snap.trackerId) ?? [] + bucket.push(snap) + snapshotsByTracker.set(snap.trackerId, bucket) + } + + const trackerDeltas: TrackerDelta[] = [] + + for (const tracker of allTrackers) { + const snaps = snapshotsByTracker.get(tracker.id) + + if (!snaps || snaps.length < 2) { + trackerDeltas.push(zeroDelta(tracker)) + continue + } + + const earliest = snaps[0] + const latest = snaps[snaps.length - 1] + + try { + const uploadDelta = BigInt(latest.uploadedBytes) - BigInt(earliest.uploadedBytes) + const downloadDelta = BigInt(latest.downloadedBytes) - BigInt(earliest.downloadedBytes) + + const latestBuffer = latest.bufferBytes ?? 0n + const earliestBuffer = earliest.bufferBytes ?? 0n + const bufferDelta = BigInt(latestBuffer) - BigInt(earliestBuffer) + + const ratioChange = (latest.ratio ?? 0) - (earliest.ratio ?? 0) + const seedbonusChange = (latest.seedbonus ?? 0) - (earliest.seedbonus ?? 0) + + trackerDeltas.push({ + id: tracker.id, + name: tracker.name, + color: tracker.color ?? null, + uploadDelta, + downloadDelta, + bufferDelta, + ratioChange, + seedbonusChange, + }) + } catch { + log.debug("BigInt conversion failed for tracker %d", tracker.id) + trackerDeltas.push(zeroDelta(tracker)) + } + } + + // ── Fleet aggregation ────────────────────────────────────────────────────── + + let fleetUploadDelta = 0n + let fleetDownloadDelta = 0n + let fleetBufferDelta = 0n + let fleetSeedbonusChange = 0 + + // Weighted average of ratio change: sum(ratioChange * uploadDelta) / sum(uploadDelta) + let weightedRatioSum = 0 + let totalUploadWeight = 0n + + for (const delta of trackerDeltas) { + fleetUploadDelta += delta.uploadDelta + fleetDownloadDelta += delta.downloadDelta + fleetBufferDelta += delta.bufferDelta + fleetSeedbonusChange += delta.seedbonusChange + + if (delta.uploadDelta > 0n) { + // Convert upload delta to a Number for the weight + const weight = Number(delta.uploadDelta) + weightedRatioSum += delta.ratioChange * weight + totalUploadWeight += delta.uploadDelta + } + } + + const fleetRatioChange = + totalUploadWeight > 0n ? weightedRatioSum / Number(totalUploadWeight) : null + + // ── Yesterday comparison from checkpoint tables ──────────────────────────── + + const yesterdayByTracker = new Map(yesterdayCheckpoints.map((cp) => [cp.trackerId, cp])) + const dayBeforeByTracker = new Map(dayBeforeCheckpoints.map((cp) => [cp.trackerId, cp])) + + let yesterdayFleetUpload: bigint | null = null + let yesterdayFleetDownload: bigint | null = null + let yesterdayFleetBuffer: bigint | null = null + + for (const tracker of allTrackers) { + const yesterday = yesterdayByTracker.get(tracker.id) + const dayBefore = dayBeforeByTracker.get(tracker.id) + + if (!yesterday || !dayBefore) continue + + try { + const trackerUploadYesterday = + BigInt(yesterday.uploadedBytesEnd) - BigInt(dayBefore.uploadedBytesEnd) + const trackerDownloadYesterday = + BigInt(yesterday.downloadedBytesEnd) - BigInt(dayBefore.downloadedBytesEnd) + + const yesterdayBuffer = yesterday.bufferBytesEnd ?? 0n + const dayBeforeBuffer = dayBefore.bufferBytesEnd ?? 0n + const trackerBufferYesterday = BigInt(yesterdayBuffer) - BigInt(dayBeforeBuffer) + + yesterdayFleetUpload = (yesterdayFleetUpload ?? 0n) + trackerUploadYesterday + yesterdayFleetDownload = (yesterdayFleetDownload ?? 0n) + trackerDownloadYesterday + yesterdayFleetBuffer = (yesterdayFleetBuffer ?? 0n) + trackerBufferYesterday + } catch { + log.debug("BigInt conversion failed for yesterday comparison, tracker %d", tracker.id) + } + } + + // ── Torrent movers ───────────────────────────────────────────────────────── + + // Build checkpoint lookup + const cpByKey = new Map(torrentCps.map((cp) => [`${cp.clientId}:${cp.hash}`, cp])) + + // Build tracker tag → color lookup for matching torrent tags + const trackerTagToColor = new Map() + for (const tracker of allTrackers) { + if (tracker.qbtTag) { + trackerTagToColor.set(tracker.qbtTag, tracker.color ?? null) + } + } + + const movers: TorrentMover[] = [] + let addedToday = 0 + let completedToday = 0 + + for (const client of clients) { + const torrents = parseCachedTorrents(client.cachedTorrents) + + for (const torrent of torrents) { + // Activity counts. added_on and completion_on are unix timestamps (seconds) + if (isUnixTimestampOnDate(torrent.added_on, todayStr)) { + addedToday++ + } + if (torrent.completion_on !== -1 && isUnixTimestampOnDate(torrent.completion_on, todayStr)) { + completedToday++ + } + + // Compare current uploaded/downloaded to today's checkpoint + const key = `${client.id}:${torrent.hash}` + const checkpoint = cpByKey.get(key) + if (!checkpoint) continue + + let uploadedToday: bigint + let downloadedToday: bigint + try { + uploadedToday = BigInt(torrent.uploaded) - BigInt(checkpoint.uploadedStart) + downloadedToday = BigInt(torrent.downloaded) - BigInt(checkpoint.downloadedStart) + } catch { + log.debug("BigInt conversion failed for torrent %s", torrent.hash) + continue + } + + // Match first qbtTag found in the torrent's comma-separated tags field + let matchedTag: string | null = null + let matchedColor: string | null = null + if (torrent.tags) { + const torrentTags = parseTorrentTags(torrent.tags, false) + for (const tag of torrentTags) { + if (trackerTagToColor.has(tag)) { + matchedTag = tag + matchedColor = trackerTagToColor.get(tag) ?? null + break + } + } + } + + // Only include torrents that match a tracked tracker + if (!matchedTag) continue + + movers.push({ + hash: torrent.hash, + name: torrent.name, + qbtTag: matchedTag, + trackerColor: matchedColor, + clientName: client.name, + uploadedToday, + downloadedToday, + }) + } + } + + // Sort and take top 5 uploaders and downloaders + const topUploaders = movers + .filter((m) => m.uploadedToday > 0n) + .sort((a, b) => compareBigIntDesc(a.uploadedToday, b.uploadedToday)) + .slice(0, 5) + + const topDownloaders = movers + .filter((m) => m.downloadedToday > 0n) + .sort((a, b) => compareBigIntDesc(a.downloadedToday, b.downloadedToday)) + .slice(0, 5) + + // ── Assemble and return ─────────────────────────────────────────────────── + + const latestClientPoll = clients.reduce((latest, c) => { + if (c.cachedTorrentsAt && (!latest || c.cachedTorrentsAt > latest)) return c.cachedTorrentsAt + return latest + }, null) + + return { + fleet: { + uploadDelta: fleetUploadDelta.toString(), + downloadDelta: fleetDownloadDelta.toString(), + bufferDelta: fleetBufferDelta.toString(), + ratioChange: fleetRatioChange, + seedbonusChange: fleetSeedbonusChange, + uploadDeltaYesterday: yesterdayFleetUpload !== null ? yesterdayFleetUpload.toString() : null, + downloadDeltaYesterday: + yesterdayFleetDownload !== null ? yesterdayFleetDownload.toString() : null, + bufferDeltaYesterday: yesterdayFleetBuffer !== null ? yesterdayFleetBuffer.toString() : null, + }, + trackers: trackerDeltas.map((d) => ({ + id: d.id, + name: d.name, + color: d.color, + uploadDelta: d.uploadDelta.toString(), + downloadDelta: d.downloadDelta.toString(), + bufferDelta: d.bufferDelta.toString(), + })), + activity: { + addedToday, + completedToday, + }, + movers: { + topUploaders: topUploaders.map((t) => ({ + hash: t.hash, + name: t.name, + qbtTag: t.qbtTag, + trackerColor: t.trackerColor, + clientName: t.clientName, + uploadedToday: t.uploadedToday.toString(), + })), + topDownloaders: topDownloaders.map((t) => ({ + hash: t.hash, + name: t.name, + qbtTag: t.qbtTag, + trackerColor: t.trackerColor, + clientName: t.clientName, + downloadedToday: t.downloadedToday.toString(), + })), + }, + trackerLastUpdated: + todaySnapshots.length > 0 + ? todaySnapshots[todaySnapshots.length - 1].polledAt.toISOString() + : null, + clientLastUpdated: latestClientPoll?.toISOString() ?? null, + } +} + +// ── Backfill ─────────────────────────────────────────────────── + +/** + * One-time backfill: populates trackerDailyCheckpoints from existing trackerSnapshots. + * Should be called once when the checkpoint table is empty but snapshot data exists. + */ +export async function backfillTrackerCheckpoints(): Promise { + // Check if backfill is needed + const [existing] = await db + .select({ id: trackerDailyCheckpoints.id }) + .from(trackerDailyCheckpoints) + .limit(1) + + if (existing) return 0 // Already has data, skip + + // Check if there are snapshots to backfill from + const [hasSnapshots] = await db + .select({ id: trackerSnapshots.id }) + .from(trackerSnapshots) + .limit(1) + + if (!hasSnapshots) return 0 // No snapshots, nothing to backfill + + const polledDate = sql`DATE(${trackerSnapshots.polledAt})` + + const rows = await db + .selectDistinctOn([trackerSnapshots.trackerId, polledDate], { + trackerId: trackerSnapshots.trackerId, + checkpointDate: polledDate.as("checkpoint_date"), + uploadedBytesEnd: trackerSnapshots.uploadedBytes, + downloadedBytesEnd: trackerSnapshots.downloadedBytes, + bufferBytesEnd: trackerSnapshots.bufferBytes, + ratioEnd: trackerSnapshots.ratio, + seedbonusEnd: trackerSnapshots.seedbonus, + }) + .from(trackerSnapshots) + .orderBy(trackerSnapshots.trackerId, polledDate, sql`${trackerSnapshots.polledAt} DESC`) + + if (rows.length === 0) return 0 + + const CHUNK_SIZE = 500 + let inserted = 0 + + for (let i = 0; i < rows.length; i += CHUNK_SIZE) { + const chunk = rows.slice(i, i + CHUNK_SIZE) + await db + .insert(trackerDailyCheckpoints) + .values( + chunk.map((row) => ({ + trackerId: row.trackerId, + checkpointDate: row.checkpointDate, + uploadedBytesEnd: row.uploadedBytesEnd, + downloadedBytesEnd: row.downloadedBytesEnd, + bufferBytesEnd: row.bufferBytesEnd, + ratioEnd: row.ratioEnd != null ? Number(row.ratioEnd) : null, + seedbonusEnd: row.seedbonusEnd != null ? Number(row.seedbonusEnd) : null, + snapshotCount: 1, + })) + ) + .onConflictDoNothing() + inserted += chunk.length + } + + return inserted +} From 84c522750f517c95653b73e1298aa72bd204e27f Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 09:43:21 -0500 Subject: [PATCH 21/59] feat(dashboard): add Today At A Glance UI --- src/app/(auth)/DashboardClient.tsx | 18 +- .../dashboard/DashboardSettingsSheet.tsx | 6 + src/components/dashboard/FleetActivity.tsx | 31 +++ src/components/dashboard/FleetHeadline.tsx | 107 ++++++++++ src/components/dashboard/MoversAndShakers.tsx | 96 +++++++++ src/components/dashboard/TodayAtAGlance.tsx | 87 ++++++++ .../dashboard/TodayAtAGlanceSkeleton.tsx | 43 ++++ .../dashboard/TrackerBreakdownBars.tsx | 89 ++++++++ .../dashboard/TrackerBreakdownTicker.tsx | 39 ++++ src/components/ui/ProgressBar.tsx | 77 +++++++ .../ui/__tests__/ProgressBar.test.tsx | 196 ++++++++++++++++++ src/hooks/useDashboardData.ts | 20 +- 12 files changed, 804 insertions(+), 5 deletions(-) create mode 100644 src/components/dashboard/FleetActivity.tsx create mode 100644 src/components/dashboard/FleetHeadline.tsx create mode 100644 src/components/dashboard/MoversAndShakers.tsx create mode 100644 src/components/dashboard/TodayAtAGlance.tsx create mode 100644 src/components/dashboard/TodayAtAGlanceSkeleton.tsx create mode 100644 src/components/dashboard/TrackerBreakdownBars.tsx create mode 100644 src/components/dashboard/TrackerBreakdownTicker.tsx create mode 100644 src/components/ui/ProgressBar.tsx create mode 100644 src/components/ui/__tests__/ProgressBar.test.tsx diff --git a/src/app/(auth)/DashboardClient.tsx b/src/app/(auth)/DashboardClient.tsx index a3e70c16..06dafdd6 100644 --- a/src/app/(auth)/DashboardClient.tsx +++ b/src/app/(auth)/DashboardClient.tsx @@ -15,6 +15,8 @@ import { EcosystemStatsSection } from "@/components/dashboard/EcosystemStatsSect import { FleetDashboard } from "@/components/dashboard/FleetDashboard" import { LoginTimers } from "@/components/dashboard/LoginTimers" import { PollAllButton } from "@/components/dashboard/PollAllButton" +import { TodayAtAGlance } from "@/components/dashboard/TodayAtAGlance" +import { TodayAtAGlanceSkeleton } from "@/components/dashboard/TodayAtAGlanceSkeleton" import { TrackerLeaderboard } from "@/components/dashboard/TrackerLeaderboard" import { TrackerOverviewGrid } from "@/components/dashboard/TrackerOverviewGrid" import { useDashboardSettings } from "@/components/dashboard/useDashboardSettings" @@ -96,7 +98,19 @@ export function DashboardClient({ initialTrackers }: DashboardClientProps) { - {/* Section 1: Tracker Overview */} + {/* Today At A Glance */} + {dashSettings.settings.showTodayAtAGlance && ( +
+

Today At A Glance

+ {data.todayData ? ( + + ) : data.todayLoading ? ( + + ) : null} +
+ )} + + {/* Tracker Overview */}

Trackers

- {/* Section 2: Alerts */} + {/* Alerts */} {data.alerts.length > 0 && (

Trackers

+ dashSettings.update("showTodayAtAGlance", checked)} + /> No fleet activity today

+ } + + return ( +
+ + {addedToday} + {" added today"} + + · + + {completedToday} + {" completed today"} + +
+ ) +} diff --git a/src/components/dashboard/FleetHeadline.tsx b/src/components/dashboard/FleetHeadline.tsx new file mode 100644 index 00000000..3152741a --- /dev/null +++ b/src/components/dashboard/FleetHeadline.tsx @@ -0,0 +1,107 @@ +// src/components/dashboard/FleetHeadline.tsx + +"use client" + +import { CHART_THEME } from "@/components/charts/lib/theme" +import { + DownloadArrowIcon, + RatioIcon, + ShieldIcon, + StarIcon, + UploadArrowIcon, +} from "@/components/ui/Icons" +import { StatCard } from "@/components/ui/StatCard" +import { computePctChange, formatBytesFromString, splitValueUnit } from "@/lib/formatters" +import type { TodayAtAGlance } from "@/types/api" + +interface FleetHeadlineProps { + fleet: TodayAtAGlance["fleet"] +} + +function pctLabel(pct: number | null): string | undefined { + if (pct === null) return undefined + const rounded = Math.round(pct) + return rounded >= 0 ? `+${rounded}% vs yesterday` : `${rounded}% vs yesterday` +} + +export function FleetHeadline({ fleet }: FleetHeadlineProps) { + const uploadParts = splitValueUnit(formatBytesFromString(fleet.uploadDelta)) + const downloadParts = splitValueUnit(formatBytesFromString(fleet.downloadDelta)) + const bufferParts = splitValueUnit(formatBytesFromString(fleet.bufferDelta)) + + const ratioDisplay = + fleet.ratioChange !== null + ? fleet.ratioChange >= 0 + ? `+${fleet.ratioChange.toFixed(4)}` + : fleet.ratioChange.toFixed(4) + : "+0.0000" + + const seedbonusDisplay = (() => { + if (fleet.seedbonusChange === null) return "+0" + const floored = Math.floor(Math.abs(fleet.seedbonusChange)) + const sign = fleet.seedbonusChange >= 0 ? "+" : "-" + return sign + floored.toLocaleString() + })() + + const uploadPct = computePctChange(fleet.uploadDelta, fleet.uploadDeltaYesterday) + const downloadPct = computePctChange(fleet.downloadDelta, fleet.downloadDeltaYesterday) + const bufferPct = computePctChange(fleet.bufferDelta, fleet.bufferDeltaYesterday) + + const ratioTrend = + fleet.ratioChange === null || fleet.ratioChange === 0 + ? undefined + : fleet.ratioChange > 0 + ? ("up" as const) + : ("down" as const) + + return ( +
+ } + /> + + } + /> + + } + /> + + } + /> + + } + /> +
+ ) +} diff --git a/src/components/dashboard/MoversAndShakers.tsx b/src/components/dashboard/MoversAndShakers.tsx new file mode 100644 index 00000000..bba1a7f4 --- /dev/null +++ b/src/components/dashboard/MoversAndShakers.tsx @@ -0,0 +1,96 @@ +// src/components/dashboard/MoversAndShakers.tsx + +"use client" + +import type { ReactNode } from "react" +import { MarqueeText } from "@/components/ui/MarqueeText" +import { Tooltip } from "@/components/ui/Tooltip" +import { formatBytesFromString } from "@/lib/formatters" +import type { TodayAtAGlance } from "@/types/api" + +interface MoversAndShakersProps { + movers: TodayAtAGlance["movers"] +} + +interface TorrentRankListEntry { + hash: string + name: string + qbtTag: string | null + trackerColor: string | null + clientName: string | null + bytes: string +} + +function buildTooltip(entry: TorrentRankListEntry): ReactNode { + const lines: string[] = [] + if (entry.qbtTag) lines.push(`Tracker: ${entry.qbtTag}`) + if (entry.clientName) lines.push(`Client: ${entry.clientName}`) + if (entry.qbtTag?.toLowerCase().includes("cross-seed")) lines.push("Cross-seeded") + if (lines.length === 0) return "Unknown tracker" + return ( +
+ {lines.map((line) => ( + {line} + ))} +
+ ) +} + +function TorrentRankList({ label, entries }: { label: string; entries: TorrentRankListEntry[] }) { + return ( +
+

+ {label} +

+ {entries.length === 0 ? ( +

No activity

+ ) : ( +
    + {entries.map((entry, index) => ( + +
  • +
    + + {entry.name} +
    + + {formatBytesFromString(entry.bytes)} + +
  • +
    + ))} +
+ )} +
+ ) +} + +export function MoversAndShakers({ movers }: MoversAndShakersProps) { + const { topUploaders, topDownloaders } = movers + + if (topUploaders.length === 0 && topDownloaders.length === 0) { + return ( +

+ Connect a download client to see torrent activity +

+ ) + } + + return ( +
+ ({ ...e, bytes: e.uploadedToday }))} + /> + ({ ...e, bytes: e.downloadedToday }))} + /> +
+ ) +} diff --git a/src/components/dashboard/TodayAtAGlance.tsx b/src/components/dashboard/TodayAtAGlance.tsx new file mode 100644 index 00000000..92e83069 --- /dev/null +++ b/src/components/dashboard/TodayAtAGlance.tsx @@ -0,0 +1,87 @@ +// src/components/dashboard/TodayAtAGlance.tsx + +"use client" + +import { FleetActivity } from "@/components/dashboard/FleetActivity" +import { FleetHeadline } from "@/components/dashboard/FleetHeadline" +import { MoversAndShakers } from "@/components/dashboard/MoversAndShakers" +import { TrackerBreakdownBars } from "@/components/dashboard/TrackerBreakdownBars" +import { TrackerBreakdownTicker } from "@/components/dashboard/TrackerBreakdownTicker" +import { Card } from "@/components/ui/Card" +import { formatTimeAgo } from "@/lib/formatters" +import type { TodayAtAGlance as TodayAtAGlanceData } from "@/types/api" + +interface TodayAtAGlanceProps { + data: TodayAtAGlanceData + variant?: "bars" | "ticker" +} + +function UpdatedAt({ iso }: { iso: string | null }) { + if (!iso) return null + const ago = formatTimeAgo(new Date(iso)) + return ( + + Updated {ago} + + ) +} + +export function TodayAtAGlance({ data, variant = "bars" }: TodayAtAGlanceProps) { + const hasActivity = + data.activity.addedToday > 0 || + data.activity.completedToday > 0 || + data.movers.topUploaders.length > 0 || + data.movers.topDownloaders.length > 0 + + return ( + +
+ + +
+ + {/* Tracker breakdowns — upload and download side by side on lg, stacked on mobile */} +
+ + By Tracker + + +
+
+
+

+ Upload +

+ {variant === "bars" ? ( + + ) : ( + + )} +
+ +
+

+ Download +

+ {variant === "bars" ? ( + + ) : ( + + )} +
+
+ + {hasActivity && ( + <> +
+
+ + +
+ + + )} +
+ + ) +} diff --git a/src/components/dashboard/TodayAtAGlanceSkeleton.tsx b/src/components/dashboard/TodayAtAGlanceSkeleton.tsx new file mode 100644 index 00000000..dd1ebf3f --- /dev/null +++ b/src/components/dashboard/TodayAtAGlanceSkeleton.tsx @@ -0,0 +1,43 @@ +// src/components/dashboard/TodayAtAGlanceSkeleton.tsx + +"use client" + +import { Card } from "@/components/ui/Card" + +function Shimmer({ className }: { className?: string }) { + return
+} + +const STAT_KEYS = ["upload", "download", "buffer", "ratio", "bonus"] as const +const PANEL_KEYS = ["upload-panel", "download-panel"] as const + +export function TodayAtAGlanceSkeleton() { + return ( + +
+ {/* Fleet headline — 5 stat card placeholders */} +
+ {STAT_KEYS.map((key) => ( +
+ + +
+ ))} +
+ +
+ + {/* Tracker breakdowns — two panels */} +
+ {PANEL_KEYS.map((key) => ( +
+ + + +
+ ))} +
+
+ + ) +} diff --git a/src/components/dashboard/TrackerBreakdownBars.tsx b/src/components/dashboard/TrackerBreakdownBars.tsx new file mode 100644 index 00000000..db662e44 --- /dev/null +++ b/src/components/dashboard/TrackerBreakdownBars.tsx @@ -0,0 +1,89 @@ +// src/components/dashboard/TrackerBreakdownBars.tsx + +"use client" + +import { ProgressBar } from "@/components/ui/ProgressBar" +import { compareBigIntDesc, formatBytesFromString } from "@/lib/formatters" +import type { TodayAtAGlance } from "@/types/api" + +interface TrackerBreakdownProps { + trackers: TodayAtAGlance["trackers"] + metric?: "upload" | "download" +} + +const TOP_N = 5 + +export function TrackerBreakdownBars({ trackers, metric = "upload" }: TrackerBreakdownProps) { + if (trackers.length === 0) return null + + const getDelta = (t: TodayAtAGlance["trackers"][number]) => + metric === "upload" ? t.uploadDelta : t.downloadDelta + + const sorted = [...trackers].sort((a, b) => + compareBigIntDesc(BigInt(getDelta(a)), BigInt(getDelta(b))) + ) + + const top = sorted.slice(0, TOP_N) + const rest = sorted.slice(TOP_N) + + const othersDelta = rest.reduce((acc, t) => acc + BigInt(getDelta(t)), 0n) + const totalDelta = sorted.reduce((acc, t) => acc + BigInt(getDelta(t)), 0n) + + const allZero = totalDelta === 0n + + function getPercentage(delta: bigint): number { + if (totalDelta === 0n) return 0 + return Number((delta * 100n) / totalDelta) + } + + if (allZero) { + return ( +

+ No {metric} activity yet today +

+ ) + } + + const activeTop = top.filter((t) => BigInt(getDelta(t)) > 0n) + + return ( +
+ {activeTop.map((tracker) => { + const delta = BigInt(getDelta(tracker)) + const pct = getPercentage(delta) + const color = tracker.color ?? undefined + + return ( +
+ + {tracker.name} + + + + + + {formatBytesFromString(getDelta(tracker))} + +
+ ) + })} + + {othersDelta > 0n && ( +
+ Others + + + + + {formatBytesFromString(othersDelta.toString())} + +
+ )} +
+ ) +} diff --git a/src/components/dashboard/TrackerBreakdownTicker.tsx b/src/components/dashboard/TrackerBreakdownTicker.tsx new file mode 100644 index 00000000..94afe3e2 --- /dev/null +++ b/src/components/dashboard/TrackerBreakdownTicker.tsx @@ -0,0 +1,39 @@ +// src/components/dashboard/TrackerBreakdownTicker.tsx + +"use client" + +import { compareBigIntDesc, formatBytesFromString } from "@/lib/formatters" +import type { TodayAtAGlance } from "@/types/api" + +interface TrackerBreakdownProps { + trackers: TodayAtAGlance["trackers"] +} + +export function TrackerBreakdownTicker({ trackers }: TrackerBreakdownProps) { + const active = [...trackers] + .filter((t) => BigInt(t.uploadDelta) > 0n) + .sort((a, b) => compareBigIntDesc(BigInt(a.uploadDelta), BigInt(b.uploadDelta))) + + if (active.length === 0) return null + + return ( +
+ {active.map((tracker) => { + const dotColor = tracker.color ?? "var(--color-accent)" + + return ( +
+ + {tracker.name} + + {formatBytesFromString(tracker.uploadDelta)} + +
+ ) + })} +
+ ) +} diff --git a/src/components/ui/ProgressBar.tsx b/src/components/ui/ProgressBar.tsx new file mode 100644 index 00000000..be690396 --- /dev/null +++ b/src/components/ui/ProgressBar.tsx @@ -0,0 +1,77 @@ +// src/components/ui/ProgressBar.tsx +// +// Functions: ProgressBar +// +// Neumorphic progress bar matching the Toggle track/knob depth pattern. +// Track: nm-inset-sm (recessed channel). Fill: nm-raised-sm (floating bar). + +"use client" + +import clsx from "clsx" + +type ProgressBarSize = "sm" | "md" | "lg" + +interface ProgressBarProps { + /** Percentage filled (0–100). Clamped internally. */ + percent: number + /** Fill color — accepts any CSS color value or CSS variable. Defaults to accent. */ + color?: string + /** Track height. sm=4px, md=8px (default), lg=12px */ + size?: ProgressBarSize + /** Show the percentage as text inside the bar (only visible on lg size) */ + showLabel?: boolean + /** Animate width transitions */ + animated?: boolean + /** Additional classes on the outer track */ + className?: string +} + +const sizeMap: Record = { + sm: { track: "p-[1px]", fill: "h-1.5" }, + md: { track: "p-[3px]", fill: "h-3" }, + lg: { track: "p-[2px]", fill: "h-3" }, +} + +function ProgressBar({ + percent, + color, + size = "md", + showLabel = false, + animated = true, + className, +}: ProgressBarProps) { + const clamped = Math.max(0, Math.min(100, percent)) + const { track, fill } = sizeMap[size] + const resolvedColor = color ?? "var(--color-accent)" + + return ( +
+
0 ? `0 0 12px ${resolvedColor}60` : undefined, + }} + role="progressbar" + aria-valuenow={clamped} + aria-valuemin={0} + aria-valuemax={100} + > + {showLabel && size === "lg" && clamped > 10 && ( + + {Math.round(clamped)}% + + )} +
+
+ ) +} + +export type { ProgressBarProps, ProgressBarSize } +export { ProgressBar } diff --git a/src/components/ui/__tests__/ProgressBar.test.tsx b/src/components/ui/__tests__/ProgressBar.test.tsx new file mode 100644 index 00000000..395415af --- /dev/null +++ b/src/components/ui/__tests__/ProgressBar.test.tsx @@ -0,0 +1,196 @@ +// src/components/ui/__tests__/ProgressBar.test.tsx + +import { render } from "@testing-library/react" +import { describe, expect, it } from "vitest" +import { ProgressBar } from "@/components/ui/ProgressBar" + +// --------------------------------------------------------------------------- +// aria attributes +// --------------------------------------------------------------------------- + +describe("ProgressBar aria attributes", () => { + it("renders an element with role='progressbar'", () => { + const { getByRole } = render() + expect(getByRole("progressbar")).toBeDefined() + }) + + it("sets aria-valuenow to the clamped percent value", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.getAttribute("aria-valuenow")).toBe("75") + }) + + it("sets aria-valuemin to 0", () => { + const { getByRole } = render() + expect(getByRole("progressbar").getAttribute("aria-valuemin")).toBe("0") + }) + + it("sets aria-valuemax to 100", () => { + const { getByRole } = render() + expect(getByRole("progressbar").getAttribute("aria-valuemax")).toBe("100") + }) +}) + +// --------------------------------------------------------------------------- +// width computation +// --------------------------------------------------------------------------- + +describe("ProgressBar fill width", () => { + it("at 50% the fill has width 50%", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.width).toBe("50%") + }) + + it("at 100% the fill has width 100%", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.width).toBe("100%") + }) + + it("at 0% the fill has width 0% and aria-valuenow is 0", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.width).toBe("0%") + expect(bar.getAttribute("aria-valuenow")).toBe("0") + }) + + it("at 0% the fill element has the 'invisible' class applied", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.className).toContain("invisible") + }) + + it("at 50% the fill element does NOT have the 'invisible' class", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.className).not.toContain("invisible") + }) +}) + +// --------------------------------------------------------------------------- +// clamping +// --------------------------------------------------------------------------- + +describe("ProgressBar clamping", () => { + it("negative values are clamped to 0", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.getAttribute("aria-valuenow")).toBe("0") + expect(bar.style.width).toBe("0%") + }) + + it("values over 100 are clamped to 100", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.getAttribute("aria-valuenow")).toBe("100") + expect(bar.style.width).toBe("100%") + }) + + it("aria-valuenow reflects the clamped value, not the raw input", () => { + const { getByRole } = render() + expect(getByRole("progressbar").getAttribute("aria-valuenow")).toBe("0") + }) + + it("exactly 0 is not clamped further", () => { + const { getByRole } = render() + expect(getByRole("progressbar").getAttribute("aria-valuenow")).toBe("0") + }) + + it("exactly 100 is not clamped further", () => { + const { getByRole } = render() + expect(getByRole("progressbar").getAttribute("aria-valuenow")).toBe("100") + }) +}) + +// --------------------------------------------------------------------------- +// color prop +// --------------------------------------------------------------------------- + +describe("ProgressBar color prop", () => { + it("applies custom color to the fill's backgroundColor", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.backgroundColor).toBe("rgb(255, 0, 0)") + }) + + it("uses var(--color-accent) when no color prop is provided", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + // jsdom renders CSS variable references as-is in the style attribute + expect(bar.style.backgroundColor).toBe("var(--color-accent)") + }) + + it("applies a CSS variable as color", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.backgroundColor).toBe("var(--color-warn)") + }) +}) + +// --------------------------------------------------------------------------- +// glow / boxShadow +// --------------------------------------------------------------------------- + +describe("ProgressBar box shadow", () => { + it("applies glow boxShadow when percent > 0", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.boxShadow).toContain("12px") + }) + + it("does not apply boxShadow when percent is 0 (fill invisible)", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.style.boxShadow).toBe("") + }) +}) + +// --------------------------------------------------------------------------- +// showLabel +// --------------------------------------------------------------------------- + +describe("ProgressBar showLabel prop", () => { + it("does not render label by default", () => { + const { queryByText } = render() + expect(queryByText("50%")).toBeNull() + }) + + it("does not render label on lg size when percent <= 10", () => { + const { queryByText } = render() + expect(queryByText("10%")).toBeNull() + }) + + it("renders label text on lg size when percent > 10 and showLabel is true", () => { + const { getByText } = render() + expect(getByText("50%")).toBeDefined() + }) + + it("does not render label on md size even when showLabel is true", () => { + const { queryByText } = render() + expect(queryByText("50%")).toBeNull() + }) + + it("rounds the percent label to the nearest integer", () => { + const { getByText } = render() + expect(getByText("51%")).toBeDefined() + }) +}) + +// --------------------------------------------------------------------------- +// animated prop +// --------------------------------------------------------------------------- + +describe("ProgressBar animated prop", () => { + it("includes transition class by default", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.className).toContain("transition-all") + }) + + it("omits transition class when animated=false", () => { + const { getByRole } = render() + const bar = getByRole("progressbar") + expect(bar.className).not.toContain("transition-all") + }) +}) diff --git a/src/hooks/useDashboardData.ts b/src/hooks/useDashboardData.ts index 724b5c26..9c3bc4d0 100644 --- a/src/hooks/useDashboardData.ts +++ b/src/hooks/useDashboardData.ts @@ -1,6 +1,4 @@ // src/hooks/useDashboardData.ts -// -// Functions: useDashboardData "use client" @@ -16,7 +14,7 @@ import { fetchDismissedKeys, postDismissAlert as persistDismiss, } from "@/lib/dashboard" -import type { Snapshot, TrackerSummary } from "@/types/api" +import type { Snapshot, TodayAtAGlance, TrackerSummary } from "@/types/api" interface DashboardData { trackers: TrackerSummary[] @@ -25,6 +23,8 @@ interface DashboardData { alerts: DashboardAlert[] dayRange: DayRange setDayRange: (range: DayRange) => void + todayData: TodayAtAGlance | null + todayLoading: boolean dismissAlert: (key: string, type: string) => void dismissAllAlerts: () => void refresh: () => Promise @@ -88,6 +88,17 @@ function useDashboardData(options?: UseDashboardDataOptions): DashboardData { })), }) + const todayQuery = useQuery({ + queryKey: ["dashboard-today"], + queryFn: async ({ signal }): Promise => { + const res = await fetch("/api/dashboard/today", { signal }) + if (!res.ok) throw new Error("Failed to fetch today data") + return res.json() + }, + staleTime: 60_000, + refetchInterval: 60_000, + }) + // Secondary queries for alert computation (less frequent) const clientsQuery = useQuery({ queryKey: ["clients-for-alerts"], @@ -169,6 +180,8 @@ function useDashboardData(options?: UseDashboardDataOptions): DashboardData { return { trackers, snapshotMap, + todayData: todayQuery.data ?? null, + todayLoading: todayQuery.isLoading, loading: trackersQuery.isLoading, alerts: visibleAlerts, dayRange, @@ -178,6 +191,7 @@ function useDashboardData(options?: UseDashboardDataOptions): DashboardData { refresh: async () => { await trackersQuery.refetch() await queryClient.invalidateQueries({ queryKey: ["snapshots"] }) + queryClient.invalidateQueries({ queryKey: ["dashboard-today"] }) }, } } From 2d0b22aa51b1badeb8916ace9505fdf32f526dc9 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 09:46:54 -0500 Subject: [PATCH 22/59] fix(ui): prevent StatCard DOM prop leak --- src/components/ui/StatCard.tsx | 41 ++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/components/ui/StatCard.tsx b/src/components/ui/StatCard.tsx index e1d7cae8..99ded844 100644 --- a/src/components/ui/StatCard.tsx +++ b/src/components/ui/StatCard.tsx @@ -1,8 +1,6 @@ // src/components/ui/StatCard.tsx -// -// Functions: StatCard -// -// Unified stat card with three variants: + +// three variants: // "basic" — single hero value (default) // "stacked" — multiple label/value rows with optional total // "ring" — countdown ring (login deadline) @@ -14,7 +12,7 @@ import { Tooltip } from "@/components/ui/Tooltip" import { hexToRgba } from "@/lib/formatters" // --------------------------------------------------------------------------- -// Shared types +// types // --------------------------------------------------------------------------- type TrendDirection = "up" | "down" | "flat" @@ -228,7 +226,7 @@ function BasicContent({ } // --------------------------------------------------------------------------- -// Stacked variant — multiple label/value rows +// Stacked variant // --------------------------------------------------------------------------- function StackedContent({ @@ -298,7 +296,7 @@ function StackedContent({ } // --------------------------------------------------------------------------- -// Ring variant — countdown progress +// Ring variant // --------------------------------------------------------------------------- function getDeadlineColor(progress: number, accent: string): string { @@ -404,7 +402,26 @@ function RingContent({ // --------------------------------------------------------------------------- function StatCard(props: StatCardProps) { - const { accentColor, icon, alert, alertReason, className, style, ...rest } = props + const { + accentColor, + icon, + alert, + alertReason, + className, + style, + // Destructure component-only props so they don't leak into Shell's DOM spread + ...rest + } = props + // Strip non-DOM props from rest before Shell spread + const { + label: _label, + value: _value, + unit: _unit, + subValue: _subValue, + subtitle: _subtitle, + trend: _trend, + ...shellRest + } = rest as Record if (props.type === "ring") { const la = new Date(props.lastAccessAt).getTime() @@ -456,7 +473,13 @@ function StatCard(props: StatCardProps) { // Default: basic return ( - +
Date: Thu, 26 Mar 2026 09:48:41 -0500 Subject: [PATCH 23/59] style(sidebar): add image priority attr and inline logout/settings buttons --- src/components/layout/Sidebar.tsx | 50 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index e78266e0..d25f0751 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -766,6 +766,8 @@ function Sidebar({ collapsed: collapsedProp, onToggle, isMobile = false }: Sideb width={140} height={40} className="shrink-0" + style={{ width: 140, height: "auto" }} + priority /> - +
+ + +
{/* Version + GitHub + Docs */}
From 5a95cd039daf5aebb128f547acf70ae749132221 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 09:51:03 -0500 Subject: [PATCH 24/59] fix(api): improve session expiration error message --- CHANGELOG.md | 12 - README.md | 2 +- .../kb/docs/contributing/trackers/seedpool.md | 16 +- docs/kb/docs/getting-started/docker-config.md | 26 +- pnpm-lock.yaml | 6567 +++++++++++------ src/app/api/clients/[id]/torrents/route.ts | 2 +- .../dashboard/TodayAtAGlanceSkeleton.tsx | 4 +- src/components/ui/ProgressBar.tsx | 7 +- src/lib/db/schema.ts | 5 +- 9 files changed, 4413 insertions(+), 2228 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b2c0aac..17514b4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## [2.4.2](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.2) (2026-03-25) - ### Features - add development image to docker hub ([8785094](https://github.com/jordanlambrecht/tracker-tracker/commit/87850942f118ccfd23c0a04d537928cd3db82976)) @@ -14,7 +13,6 @@ - **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) - **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) - ### Bug Fixes - **api:** orpheus was not matching seeding/leeching to response ([4569238](https://github.com/jordanlambrecht/tracker-tracker/commit/456923879b2970c46432bc9a0b604da2685bc31d)) @@ -30,13 +28,11 @@ - update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) - wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) - ### Performance - add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) - **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) - ### Refactoring - **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) @@ -48,7 +44,6 @@ ## [2.4.1](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.1) (2026-03-23) - ### Features - add development image to docker hub ([8785094](https://github.com/jordanlambrecht/tracker-tracker/commit/87850942f118ccfd23c0a04d537928cd3db82976)) @@ -60,7 +55,6 @@ - **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) - **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) - ### Bug Fixes - **api:** orpheus was not matching seeding/leeching to response ([4569238](https://github.com/jordanlambrecht/tracker-tracker/commit/456923879b2970c46432bc9a0b604da2685bc31d)) @@ -74,13 +68,11 @@ - update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) - wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) - ### Performance - add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) - **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) - ### Refactoring - **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) @@ -91,7 +83,6 @@ ## [2.4.0](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.0) (2026-03-23) - ### Features - add fetchTrackerStats for future live transit paper data ([ed6662f](https://github.com/jordanlambrecht/tracker-tracker/commit/ed6662f0ac0c9990b6c96e8ae5616452385b6c26)) @@ -102,7 +93,6 @@ - **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) - **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) - ### Bug Fixes - **api:** orpheus was not matching seeding/leeching to response ([4569238](https://github.com/jordanlambrecht/tracker-tracker/commit/456923879b2970c46432bc9a0b604da2685bc31d)) @@ -115,13 +105,11 @@ - update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) - wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) - ### Performance - add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) - **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) - ### Refactoring - **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) diff --git a/README.md b/README.md index 40dd2272..4dddab7c 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ pnpm dev | `POSTGRES_USER` | No | `postgres` | Database user | | `POSTGRES_DB` | No | `tracker_tracker` | Database name | | `DATABASE_URL` | No\* | _(auto-built)_ | Override to use an external Postgres instance | -| `SECURE_COOKIES` | No | _(auto)_ | Set `true` for HTTPS. Auto-enabled by `BASE_URL`. | +| `SECURE_COOKIES` | No | _(auto)_ | Set `true` for HTTPS. Auto-enabled by `BASE_URL`. | \* Set either `POSTGRES_PASSWORD` (bundled DB) or `DATABASE_URL` (external DB). diff --git a/docs/kb/docs/contributing/trackers/seedpool.md b/docs/kb/docs/contributing/trackers/seedpool.md index ccc48a7e..42029d5d 100644 --- a/docs/kb/docs/contributing/trackers/seedpool.md +++ b/docs/kb/docs/contributing/trackers/seedpool.md @@ -1,13 +1,13 @@ # Seed Pool (SP) -| Field | Value | -| ------------ | -------------------------------------- | -| Platform | UNIT3D | -| Base URL | `https://seedpool.org` | -| API Endpoint | `https://seedpool.org/api/user` | -| Auth Method | Query parameter: `?api_token=TOKEN` | -| Enrichment | N/A | -| Auth Style | N/A | +| Field | Value | +| ------------ | ----------------------------------- | +| Platform | UNIT3D | +| Base URL | `https://seedpool.org` | +| API Endpoint | `https://seedpool.org/api/user` | +| Auth Method | Query parameter: `?api_token=TOKEN` | +| Enrichment | N/A | +| Auth Style | N/A | ## Notes diff --git a/docs/kb/docs/getting-started/docker-config.md b/docs/kb/docs/getting-started/docker-config.md index e8d440de..69ae554f 100644 --- a/docs/kb/docs/getting-started/docker-config.md +++ b/docs/kb/docs/getting-started/docker-config.md @@ -20,19 +20,19 @@ Everything you need to customize how Tracker Tracker runs: environment variables ### Optional -| Variable | Default | Description | -| --------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `POSTGRES_USER` | `postgres` | PostgreSQL username. Must match in both the app and db services. | -| `POSTGRES_DB` | `tracker_tracker` | Database name. | -| `POSTGRES_HOST` | `tracker-tracker-db` | Hostname of the PostgreSQL server. Only change this if you're using an external database without `DATABASE_URL`. | -| `POSTGRES_PORT` | `5432` | PostgreSQL port. If you change this, uncomment the matching lines in `docker-compose.yml`. | -| `DATABASE_URL` | _(auto-built)_ | Full connection string. Set this to use an external Postgres instance instead of the `POSTGRES_*` variables. Format: `postgresql://user:password@host:5432/dbname` | -| `PORT` | `3000` | Port the app listens on inside the container. The host-side port mapping in `docker-compose.yml` follows this value. | -| `BASE_URL` | _(empty)_ | The public URL where your app is reachable, e.g. `https://trackertracker.example.com`. Used in backup file metadata and notification links. | -| `SECURE_COOKIES` | _(auto)_ | Set to `true` to mark session cookies as `Secure`. Auto-enabled when `BASE_URL` starts with `https://`. Only needed if you serve over HTTPS without setting `BASE_URL`. | -| `TZ` | `UTC` | Timezone for scheduled tasks and log timestamps. Uses standard tz database names, e.g. `America/Chicago`, `Europe/London`. | -| `LOG_LEVEL` | `info` | Log verbosity. Options: `error`, `warn`, `info`, `debug`. | -| `LOG_FILE` | _(none)_ | Absolute path inside the container to write logs to disk, e.g. `/data/logs/tracker-tracker.log`. | +| Variable | Default | Description | +| ---------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `POSTGRES_USER` | `postgres` | PostgreSQL username. Must match in both the app and db services. | +| `POSTGRES_DB` | `tracker_tracker` | Database name. | +| `POSTGRES_HOST` | `tracker-tracker-db` | Hostname of the PostgreSQL server. Only change this if you're using an external database without `DATABASE_URL`. | +| `POSTGRES_PORT` | `5432` | PostgreSQL port. If you change this, uncomment the matching lines in `docker-compose.yml`. | +| `DATABASE_URL` | _(auto-built)_ | Full connection string. Set this to use an external Postgres instance instead of the `POSTGRES_*` variables. Format: `postgresql://user:password@host:5432/dbname` | +| `PORT` | `3000` | Port the app listens on inside the container. The host-side port mapping in `docker-compose.yml` follows this value. | +| `BASE_URL` | _(empty)_ | The public URL where your app is reachable, e.g. `https://trackertracker.example.com`. Used in backup file metadata and notification links. | +| `SECURE_COOKIES` | _(auto)_ | Set to `true` to mark session cookies as `Secure`. Auto-enabled when `BASE_URL` starts with `https://`. Only needed if you serve over HTTPS without setting `BASE_URL`. | +| `TZ` | `UTC` | Timezone for scheduled tasks and log timestamps. Uses standard tz database names, e.g. `America/Chicago`, `Europe/London`. | +| `LOG_LEVEL` | `info` | Log verbosity. Options: `error`, `warn`, `info`, `debug`. | +| `LOG_FILE` | _(none)_ | Absolute path inside the container to write logs to disk, e.g. `/data/logs/tracker-tracker.log`. | !!! info "Settings vs environment variables" Most day-to-day settings — polling interval, privacy mode, proxy config, backup schedule, lockout policy — live inside the app under **Settings**, not in environment variables. Environment variables are just for infrastructure stuff like database connections and ports. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a098a71..0ad4dc29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,32 +1,31 @@ -lockfileVersion: '9.0' +lockfileVersion: "9.0" settings: autoInstallPeers: true excludeLinksFromLockfile: false overrides: - esbuild: '>=0.25.0' + esbuild: ">=0.25.0" importers: - .: dependencies: - '@dnd-kit/core': + "@dnd-kit/core": specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@dnd-kit/sortable': + "@dnd-kit/sortable": specifier: ^10.0.0 version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) - '@dnd-kit/utilities': + "@dnd-kit/utilities": specifier: ^3.2.2 version: 3.2.2(react@19.2.4) - '@formkit/auto-animate': + "@formkit/auto-animate": specifier: ^0.9.0 version: 0.9.0 - '@tailwindcss/typography': + "@tailwindcss/typography": specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.2.2) - '@tanstack/react-query': + "@tanstack/react-query": specifier: ^5.95.2 version: 5.95.2(react@19.2.4) argon2: @@ -102,37 +101,37 @@ importers: specifier: ^9.0.0 version: 9.0.0 devDependencies: - '@biomejs/biome': + "@biomejs/biome": specifier: ^2.4.8 version: 2.4.8 - '@commitlint/cli': + "@commitlint/cli": specifier: ^20.5.0 version: 20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2) - '@commitlint/config-conventional': + "@commitlint/config-conventional": specifier: ^20.5.0 version: 20.5.0 - '@tailwindcss/postcss': + "@tailwindcss/postcss": specifier: ^4.2.2 version: 4.2.2 - '@testing-library/jest-dom': + "@testing-library/jest-dom": specifier: ^6.9.1 version: 6.9.1 - '@testing-library/react': + "@testing-library/react": specifier: ^16.3.2 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@testing-library/user-event': + "@testing-library/user-event": specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) - '@types/node': + "@types/node": specifier: ^25.5.0 version: 25.5.0 - '@types/node-cron': + "@types/node-cron": specifier: ^3.0.11 version: 3.0.11 - '@types/react': + "@types/react": specifier: ^19.2.14 version: 19.2.14 - '@types/react-dom': + "@types/react-dom": specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) commit-and-tag-version: @@ -170,170 +169,277 @@ importers: version: 4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages: - - '@adobe/css-tools@4.4.4': - resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@asamuzakjp/css-color@5.0.1': - resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@asamuzakjp/dom-selector@7.0.4': - resolution: {integrity: sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - - '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - - '@biomejs/biome@2.4.8': - resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} - engines: {node: '>=14.21.3'} + "@adobe/css-tools@4.4.4": + resolution: + { + integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==, + } + + "@alloc/quick-lru@5.2.0": + resolution: + { + integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, + } + engines: { node: ">=10" } + + "@asamuzakjp/css-color@5.0.1": + resolution: + { + integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + + "@asamuzakjp/dom-selector@7.0.4": + resolution: + { + integrity: sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + + "@asamuzakjp/nwsapi@2.3.9": + resolution: + { + integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, + } + + "@babel/code-frame@7.29.0": + resolution: + { + integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.28.5": + resolution: + { + integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, + } + engines: { node: ">=6.9.0" } + + "@babel/runtime@7.29.2": + resolution: + { + integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==, + } + engines: { node: ">=6.9.0" } + + "@biomejs/biome@2.4.8": + resolution: + { + integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==, + } + engines: { node: ">=14.21.3" } hasBin: true - '@biomejs/cli-darwin-arm64@2.4.8': - resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-darwin-arm64@2.4.8": + resolution: + { + integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==, + } + engines: { node: ">=14.21.3" } cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.8': - resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-darwin-x64@2.4.8": + resolution: + { + integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==, + } + engines: { node: ">=14.21.3" } cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.8': - resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-linux-arm64-musl@2.4.8": + resolution: + { + integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==, + } + engines: { node: ">=14.21.3" } cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.8': - resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-linux-arm64@2.4.8": + resolution: + { + integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==, + } + engines: { node: ">=14.21.3" } cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.8': - resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-linux-x64-musl@2.4.8": + resolution: + { + integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==, + } + engines: { node: ">=14.21.3" } cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.8': - resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-linux-x64@2.4.8": + resolution: + { + integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==, + } + engines: { node: ">=14.21.3" } cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.8': - resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-win32-arm64@2.4.8": + resolution: + { + integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==, + } + engines: { node: ">=14.21.3" } cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.8': - resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} - engines: {node: '>=14.21.3'} + "@biomejs/cli-win32-x64@2.4.8": + resolution: + { + integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==, + } + engines: { node: ">=14.21.3" } cpu: [x64] os: [win32] - '@bramus/specificity@2.4.2': - resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + "@bramus/specificity@2.4.2": + resolution: + { + integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==, + } hasBin: true - '@commitlint/cli@20.5.0': - resolution: {integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==} - engines: {node: '>=v18'} + "@commitlint/cli@20.5.0": + resolution: + { + integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==, + } + engines: { node: ">=v18" } hasBin: true - '@commitlint/config-conventional@20.5.0': - resolution: {integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==} - engines: {node: '>=v18'} - - '@commitlint/config-validator@20.5.0': - resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==} - engines: {node: '>=v18'} - - '@commitlint/ensure@20.5.0': - resolution: {integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==} - engines: {node: '>=v18'} - - '@commitlint/execute-rule@20.0.0': - resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} - engines: {node: '>=v18'} - - '@commitlint/format@20.5.0': - resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==} - engines: {node: '>=v18'} - - '@commitlint/is-ignored@20.5.0': - resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==} - engines: {node: '>=v18'} - - '@commitlint/lint@20.5.0': - resolution: {integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==} - engines: {node: '>=v18'} - - '@commitlint/load@20.5.0': - resolution: {integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==} - engines: {node: '>=v18'} - - '@commitlint/message@20.4.3': - resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} - engines: {node: '>=v18'} - - '@commitlint/parse@20.5.0': - resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==} - engines: {node: '>=v18'} - - '@commitlint/read@20.5.0': - resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==} - engines: {node: '>=v18'} - - '@commitlint/resolve-extends@20.5.0': - resolution: {integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==} - engines: {node: '>=v18'} - - '@commitlint/rules@20.5.0': - resolution: {integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==} - engines: {node: '>=v18'} - - '@commitlint/to-lines@20.0.0': - resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} - engines: {node: '>=v18'} - - '@commitlint/top-level@20.4.3': - resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} - engines: {node: '>=v18'} - - '@commitlint/types@20.5.0': - resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==} - engines: {node: '>=v18'} - - '@conventional-changelog/git-client@2.6.0': - resolution: {integrity: sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==} - engines: {node: '>=18'} + "@commitlint/config-conventional@20.5.0": + resolution: + { + integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==, + } + engines: { node: ">=v18" } + + "@commitlint/config-validator@20.5.0": + resolution: + { + integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==, + } + engines: { node: ">=v18" } + + "@commitlint/ensure@20.5.0": + resolution: + { + integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==, + } + engines: { node: ">=v18" } + + "@commitlint/execute-rule@20.0.0": + resolution: + { + integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==, + } + engines: { node: ">=v18" } + + "@commitlint/format@20.5.0": + resolution: + { + integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==, + } + engines: { node: ">=v18" } + + "@commitlint/is-ignored@20.5.0": + resolution: + { + integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==, + } + engines: { node: ">=v18" } + + "@commitlint/lint@20.5.0": + resolution: + { + integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==, + } + engines: { node: ">=v18" } + + "@commitlint/load@20.5.0": + resolution: + { + integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==, + } + engines: { node: ">=v18" } + + "@commitlint/message@20.4.3": + resolution: + { + integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==, + } + engines: { node: ">=v18" } + + "@commitlint/parse@20.5.0": + resolution: + { + integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==, + } + engines: { node: ">=v18" } + + "@commitlint/read@20.5.0": + resolution: + { + integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==, + } + engines: { node: ">=v18" } + + "@commitlint/resolve-extends@20.5.0": + resolution: + { + integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==, + } + engines: { node: ">=v18" } + + "@commitlint/rules@20.5.0": + resolution: + { + integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==, + } + engines: { node: ">=v18" } + + "@commitlint/to-lines@20.0.0": + resolution: + { + integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==, + } + engines: { node: ">=v18" } + + "@commitlint/top-level@20.4.3": + resolution: + { + integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==, + } + engines: { node: ">=v18" } + + "@commitlint/types@20.5.0": + resolution: + { + integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==, + } + engines: { node: ">=v18" } + + "@conventional-changelog/git-client@2.6.0": + resolution: + { + integrity: sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==, + } + engines: { node: ">=18" } peerDependencies: conventional-commits-filter: ^5.0.0 conventional-commits-parser: ^6.3.0 @@ -343,1097 +449,1712 @@ packages: conventional-commits-parser: optional: true - '@csstools/color-helpers@6.0.2': - resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} - engines: {node: '>=20.19.0'} - - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} - engines: {node: '>=20.19.0'} + "@csstools/color-helpers@6.0.2": + resolution: + { + integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==, + } + engines: { node: ">=20.19.0" } + + "@csstools/css-calc@3.1.1": + resolution: + { + integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==, + } + engines: { node: ">=20.19.0" } peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} - engines: {node: '>=20.19.0'} + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + + "@csstools/css-color-parser@4.0.2": + resolution: + { + integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==, + } + engines: { node: ">=20.19.0" } peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/css-parser-algorithms@4.0.0': - resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} - engines: {node: '>=20.19.0'} + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + + "@csstools/css-parser-algorithms@4.0.0": + resolution: + { + integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, + } + engines: { node: ">=20.19.0" } peerDependencies: - '@csstools/css-tokenizer': ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1': - resolution: {integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==} + "@csstools/css-syntax-patches-for-csstree@1.1.1": + resolution: + { + integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==, + } peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: css-tree: optional: true - '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} - engines: {node: '>=20.19.0'} - - '@dnd-kit/accessibility@3.1.1': - resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + "@csstools/css-tokenizer@4.0.0": + resolution: + { + integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, + } + engines: { node: ">=20.19.0" } + + "@dnd-kit/accessibility@3.1.1": + resolution: + { + integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==, + } peerDependencies: - react: '>=16.8.0' + react: ">=16.8.0" - '@dnd-kit/core@6.3.1': - resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + "@dnd-kit/core@6.3.1": + resolution: + { + integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==, + } peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@dnd-kit/sortable@10.0.0': - resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + react: ">=16.8.0" + react-dom: ">=16.8.0" + + "@dnd-kit/sortable@10.0.0": + resolution: + { + integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==, + } peerDependencies: - '@dnd-kit/core': ^6.3.0 - react: '>=16.8.0' - - '@dnd-kit/utilities@3.2.2': - resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + "@dnd-kit/core": ^6.3.0 + react: ">=16.8.0" + + "@dnd-kit/utilities@3.2.2": + resolution: + { + integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==, + } peerDependencies: - react: '>=16.8.0' - - '@drizzle-team/brocli@0.10.2': - resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - - '@emnapi/core@1.9.1': - resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - - '@emnapi/runtime@1.9.1': - resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} - - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} - - '@epic-web/invariant@1.0.0': - resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - - '@esbuild-kit/core-utils@3.3.2': - resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild-kit/esm-loader@2.6.5': - resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild/aix-ppc64@0.27.4': - resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} - engines: {node: '>=18'} + react: ">=16.8.0" + + "@drizzle-team/brocli@0.10.2": + resolution: + { + integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==, + } + + "@emnapi/core@1.9.1": + resolution: + { + integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==, + } + + "@emnapi/runtime@1.9.1": + resolution: + { + integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==, + } + + "@emnapi/wasi-threads@1.2.0": + resolution: + { + integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==, + } + + "@epic-web/invariant@1.0.0": + resolution: + { + integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==, + } + + "@esbuild-kit/core-utils@3.3.2": + resolution: + { + integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==, + } + deprecated: "Merged into tsx: https://tsx.is" + + "@esbuild-kit/esm-loader@2.6.5": + resolution: + { + integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==, + } + deprecated: "Merged into tsx: https://tsx.is" + + "@esbuild/aix-ppc64@0.27.4": + resolution: + { + integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==, + } + engines: { node: ">=18" } cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.4': - resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} - engines: {node: '>=18'} + "@esbuild/android-arm64@0.27.4": + resolution: + { + integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==, + } + engines: { node: ">=18" } cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.4': - resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} - engines: {node: '>=18'} + "@esbuild/android-arm@0.27.4": + resolution: + { + integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==, + } + engines: { node: ">=18" } cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.4': - resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} - engines: {node: '>=18'} + "@esbuild/android-x64@0.27.4": + resolution: + { + integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==, + } + engines: { node: ">=18" } cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.4': - resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} - engines: {node: '>=18'} + "@esbuild/darwin-arm64@0.27.4": + resolution: + { + integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==, + } + engines: { node: ">=18" } cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.4': - resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} - engines: {node: '>=18'} + "@esbuild/darwin-x64@0.27.4": + resolution: + { + integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==, + } + engines: { node: ">=18" } cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.4': - resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} - engines: {node: '>=18'} + "@esbuild/freebsd-arm64@0.27.4": + resolution: + { + integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==, + } + engines: { node: ">=18" } cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.4': - resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} - engines: {node: '>=18'} + "@esbuild/freebsd-x64@0.27.4": + resolution: + { + integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==, + } + engines: { node: ">=18" } cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.4': - resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} - engines: {node: '>=18'} + "@esbuild/linux-arm64@0.27.4": + resolution: + { + integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==, + } + engines: { node: ">=18" } cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.4': - resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} - engines: {node: '>=18'} + "@esbuild/linux-arm@0.27.4": + resolution: + { + integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==, + } + engines: { node: ">=18" } cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.4': - resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} - engines: {node: '>=18'} + "@esbuild/linux-ia32@0.27.4": + resolution: + { + integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==, + } + engines: { node: ">=18" } cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.4': - resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} - engines: {node: '>=18'} + "@esbuild/linux-loong64@0.27.4": + resolution: + { + integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==, + } + engines: { node: ">=18" } cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.4': - resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} - engines: {node: '>=18'} + "@esbuild/linux-mips64el@0.27.4": + resolution: + { + integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==, + } + engines: { node: ">=18" } cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.4': - resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} - engines: {node: '>=18'} + "@esbuild/linux-ppc64@0.27.4": + resolution: + { + integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==, + } + engines: { node: ">=18" } cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.4': - resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} - engines: {node: '>=18'} + "@esbuild/linux-riscv64@0.27.4": + resolution: + { + integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==, + } + engines: { node: ">=18" } cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.4': - resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} - engines: {node: '>=18'} + "@esbuild/linux-s390x@0.27.4": + resolution: + { + integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==, + } + engines: { node: ">=18" } cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.4': - resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} - engines: {node: '>=18'} + "@esbuild/linux-x64@0.27.4": + resolution: + { + integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==, + } + engines: { node: ">=18" } cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.4': - resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} - engines: {node: '>=18'} + "@esbuild/netbsd-arm64@0.27.4": + resolution: + { + integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==, + } + engines: { node: ">=18" } cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.4': - resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} - engines: {node: '>=18'} + "@esbuild/netbsd-x64@0.27.4": + resolution: + { + integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==, + } + engines: { node: ">=18" } cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.4': - resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} - engines: {node: '>=18'} + "@esbuild/openbsd-arm64@0.27.4": + resolution: + { + integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==, + } + engines: { node: ">=18" } cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.4': - resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} - engines: {node: '>=18'} + "@esbuild/openbsd-x64@0.27.4": + resolution: + { + integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==, + } + engines: { node: ">=18" } cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.4': - resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} - engines: {node: '>=18'} + "@esbuild/openharmony-arm64@0.27.4": + resolution: + { + integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==, + } + engines: { node: ">=18" } cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.4': - resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} - engines: {node: '>=18'} + "@esbuild/sunos-x64@0.27.4": + resolution: + { + integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==, + } + engines: { node: ">=18" } cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.4': - resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} - engines: {node: '>=18'} + "@esbuild/win32-arm64@0.27.4": + resolution: + { + integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==, + } + engines: { node: ">=18" } cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.4': - resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} - engines: {node: '>=18'} + "@esbuild/win32-ia32@0.27.4": + resolution: + { + integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==, + } + engines: { node: ">=18" } cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.4': - resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} - engines: {node: '>=18'} + "@esbuild/win32-x64@0.27.4": + resolution: + { + integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==, + } + engines: { node: ">=18" } cpu: [x64] os: [win32] - '@exodus/bytes@1.15.0': - resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + "@exodus/bytes@1.15.0": + resolution: + { + integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } peerDependencies: - '@noble/hashes': ^1.8.0 || ^2.0.0 + "@noble/hashes": ^1.8.0 || ^2.0.0 peerDependenciesMeta: - '@noble/hashes': + "@noble/hashes": optional: true - '@formkit/auto-animate@0.9.0': - resolution: {integrity: sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==} - - '@hutson/parse-repository-url@3.0.2': - resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} - engines: {node: '>=6.9.0'} - - '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@formkit/auto-animate@0.9.0": + resolution: + { + integrity: sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==, + } + + "@hutson/parse-repository-url@3.0.2": + resolution: + { + integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==, + } + engines: { node: ">=6.9.0" } + + "@img/colour@1.1.0": + resolution: + { + integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, + } + engines: { node: ">=18" } + + "@img/sharp-darwin-arm64@0.34.5": + resolution: + { + integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-darwin-x64@0.34.5": + resolution: + { + integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + "@img/sharp-libvips-darwin-arm64@1.2.4": + resolution: + { + integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, + } cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + "@img/sharp-libvips-darwin-x64@1.2.4": + resolution: + { + integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, + } cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + "@img/sharp-libvips-linux-arm64@1.2.4": + resolution: + { + integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, + } cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + "@img/sharp-libvips-linux-arm@1.2.4": + resolution: + { + integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, + } cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + "@img/sharp-libvips-linux-ppc64@1.2.4": + resolution: + { + integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, + } cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + "@img/sharp-libvips-linux-riscv64@1.2.4": + resolution: + { + integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, + } cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + "@img/sharp-libvips-linux-s390x@1.2.4": + resolution: + { + integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, + } cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + "@img/sharp-libvips-linux-x64@1.2.4": + resolution: + { + integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, + } cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + resolution: + { + integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, + } cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + "@img/sharp-libvips-linuxmusl-x64@1.2.4": + resolution: + { + integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, + } cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-arm64@0.34.5": + resolution: + { + integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-arm@0.34.5": + resolution: + { + integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-ppc64@0.34.5": + resolution: + { + integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-riscv64@0.34.5": + resolution: + { + integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-s390x@0.34.5": + resolution: + { + integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linux-x64@0.34.5": + resolution: + { + integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linuxmusl-arm64@0.34.5": + resolution: + { + integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-linuxmusl-x64@0.34.5": + resolution: + { + integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-wasm32@0.34.5": + resolution: + { + integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-arm64@0.34.5": + resolution: + { + integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-ia32@0.34.5": + resolution: + { + integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + "@img/sharp-win32-x64@0.34.5": + resolution: + { + integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } cpu: [x64] os: [win32] - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@napi-rs/wasm-runtime@1.1.1': - resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} - - '@next/env@16.2.1': - resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==} - - '@next/swc-darwin-arm64@16.2.1': - resolution: {integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==} - engines: {node: '>= 10'} + "@jridgewell/gen-mapping@0.3.13": + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } + + "@jridgewell/remapping@2.3.5": + resolution: + { + integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, + } + + "@jridgewell/resolve-uri@3.1.2": + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: ">=6.0.0" } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } + + "@jridgewell/trace-mapping@0.3.31": + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } + + "@napi-rs/wasm-runtime@1.1.1": + resolution: + { + integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==, + } + + "@next/env@16.2.1": + resolution: + { + integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==, + } + + "@next/swc-darwin-arm64@16.2.1": + resolution: + { + integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.1': - resolution: {integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==} - engines: {node: '>= 10'} + "@next/swc-darwin-x64@16.2.1": + resolution: + { + integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==, + } + engines: { node: ">= 10" } cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.1': - resolution: {integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==} - engines: {node: '>= 10'} + "@next/swc-linux-arm64-gnu@16.2.1": + resolution: + { + integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.1': - resolution: {integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==} - engines: {node: '>= 10'} + "@next/swc-linux-arm64-musl@16.2.1": + resolution: + { + integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.1': - resolution: {integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==} - engines: {node: '>= 10'} + "@next/swc-linux-x64-gnu@16.2.1": + resolution: + { + integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==, + } + engines: { node: ">= 10" } cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.1': - resolution: {integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==} - engines: {node: '>= 10'} + "@next/swc-linux-x64-musl@16.2.1": + resolution: + { + integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==, + } + engines: { node: ">= 10" } cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.1': - resolution: {integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==} - engines: {node: '>= 10'} + "@next/swc-win32-arm64-msvc@16.2.1": + resolution: + { + integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==, + } + engines: { node: ">= 10" } cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.1': - resolution: {integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==} - engines: {node: '>= 10'} + "@next/swc-win32-x64-msvc@16.2.1": + resolution: + { + integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==, + } + engines: { node: ">= 10" } cpu: [x64] os: [win32] - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} - engines: {node: '>= 20.19.0'} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@oxc-parser/binding-android-arm-eabi@0.120.0': - resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} - engines: {node: ^20.19.0 || >=22.12.0} + "@noble/hashes@2.0.1": + resolution: + { + integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==, + } + engines: { node: ">= 20.19.0" } + + "@nodelib/fs.scandir@2.1.5": + resolution: + { + integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.stat@2.0.5": + resolution: + { + integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, + } + engines: { node: ">= 8" } + + "@nodelib/fs.walk@1.2.8": + resolution: + { + integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, + } + engines: { node: ">= 8" } + + "@oxc-parser/binding-android-arm-eabi@0.120.0": + resolution: + { + integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm64@0.120.0': - resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-android-arm64@0.120.0": + resolution: + { + integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [android] - '@oxc-parser/binding-darwin-arm64@0.120.0': - resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-darwin-arm64@0.120.0": + resolution: + { + integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.120.0': - resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-darwin-x64@0.120.0": + resolution: + { + integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.120.0': - resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-freebsd-x64@0.120.0": + resolution: + { + integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': - resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-arm-gnueabihf@0.120.0": + resolution: + { + integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': - resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-arm-musleabihf@0.120.0": + resolution: + { + integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': - resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-arm64-gnu@0.120.0": + resolution: + { + integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.120.0': - resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-arm64-musl@0.120.0": + resolution: + { + integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': - resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-ppc64-gnu@0.120.0": + resolution: + { + integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': - resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-riscv64-gnu@0.120.0": + resolution: + { + integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': - resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-riscv64-musl@0.120.0": + resolution: + { + integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': - resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-s390x-gnu@0.120.0": + resolution: + { + integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.120.0': - resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-x64-gnu@0.120.0": + resolution: + { + integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.120.0': - resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-linux-x64-musl@0.120.0": + resolution: + { + integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [linux] libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.120.0': - resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-openharmony-arm64@0.120.0": + resolution: + { + integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.120.0': - resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} - engines: {node: '>=14.0.0'} + "@oxc-parser/binding-wasm32-wasi@0.120.0": + resolution: + { + integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==, + } + engines: { node: ">=14.0.0" } cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': - resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-win32-arm64-msvc@0.120.0": + resolution: + { + integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': - resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-win32-ia32-msvc@0.120.0": + resolution: + { + integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.120.0': - resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} - engines: {node: ^20.19.0 || >=22.12.0} + "@oxc-parser/binding-win32-x64-msvc@0.120.0": + resolution: + { + integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } cpu: [x64] os: [win32] - '@oxc-project/types@0.120.0': - resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} - - '@oxc-resolver/binding-android-arm-eabi@11.19.1': - resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} + "@oxc-project/types@0.120.0": + resolution: + { + integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==, + } + + "@oxc-resolver/binding-android-arm-eabi@11.19.1": + resolution: + { + integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==, + } cpu: [arm] os: [android] - '@oxc-resolver/binding-android-arm64@11.19.1': - resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} + "@oxc-resolver/binding-android-arm64@11.19.1": + resolution: + { + integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==, + } cpu: [arm64] os: [android] - '@oxc-resolver/binding-darwin-arm64@11.19.1': - resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} + "@oxc-resolver/binding-darwin-arm64@11.19.1": + resolution: + { + integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==, + } cpu: [arm64] os: [darwin] - '@oxc-resolver/binding-darwin-x64@11.19.1': - resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} + "@oxc-resolver/binding-darwin-x64@11.19.1": + resolution: + { + integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==, + } cpu: [x64] os: [darwin] - '@oxc-resolver/binding-freebsd-x64@11.19.1': - resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} + "@oxc-resolver/binding-freebsd-x64@11.19.1": + resolution: + { + integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==, + } cpu: [x64] os: [freebsd] - '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': - resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} + "@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1": + resolution: + { + integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==, + } cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': - resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} + "@oxc-resolver/binding-linux-arm-musleabihf@11.19.1": + resolution: + { + integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==, + } cpu: [arm] os: [linux] - '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': - resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} + "@oxc-resolver/binding-linux-arm64-gnu@11.19.1": + resolution: + { + integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==, + } cpu: [arm64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-arm64-musl@11.19.1': - resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} + "@oxc-resolver/binding-linux-arm64-musl@11.19.1": + resolution: + { + integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==, + } cpu: [arm64] os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': - resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} + "@oxc-resolver/binding-linux-ppc64-gnu@11.19.1": + resolution: + { + integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==, + } cpu: [ppc64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': - resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} + "@oxc-resolver/binding-linux-riscv64-gnu@11.19.1": + resolution: + { + integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==, + } cpu: [riscv64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': - resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} + "@oxc-resolver/binding-linux-riscv64-musl@11.19.1": + resolution: + { + integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==, + } cpu: [riscv64] os: [linux] libc: [musl] - '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': - resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} + "@oxc-resolver/binding-linux-s390x-gnu@11.19.1": + resolution: + { + integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==, + } cpu: [s390x] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-gnu@11.19.1': - resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} + "@oxc-resolver/binding-linux-x64-gnu@11.19.1": + resolution: + { + integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==, + } cpu: [x64] os: [linux] libc: [glibc] - '@oxc-resolver/binding-linux-x64-musl@11.19.1': - resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} + "@oxc-resolver/binding-linux-x64-musl@11.19.1": + resolution: + { + integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==, + } cpu: [x64] os: [linux] libc: [musl] - '@oxc-resolver/binding-openharmony-arm64@11.19.1': - resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} + "@oxc-resolver/binding-openharmony-arm64@11.19.1": + resolution: + { + integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==, + } cpu: [arm64] os: [openharmony] - '@oxc-resolver/binding-wasm32-wasi@11.19.1': - resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==} - engines: {node: '>=14.0.0'} + "@oxc-resolver/binding-wasm32-wasi@11.19.1": + resolution: + { + integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==, + } + engines: { node: ">=14.0.0" } cpu: [wasm32] - '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': - resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==} + "@oxc-resolver/binding-win32-arm64-msvc@11.19.1": + resolution: + { + integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==, + } cpu: [arm64] os: [win32] - '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': - resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==} + "@oxc-resolver/binding-win32-ia32-msvc@11.19.1": + resolution: + { + integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==, + } cpu: [ia32] os: [win32] - '@oxc-resolver/binding-win32-x64-msvc@11.19.1': - resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==} + "@oxc-resolver/binding-win32-x64-msvc@11.19.1": + resolution: + { + integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==, + } cpu: [x64] os: [win32] - '@phc/format@1.0.0': - resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} - engines: {node: '>=10'} - - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - - '@rollup/rollup-android-arm-eabi@4.60.0': - resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} + "@phc/format@1.0.0": + resolution: + { + integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==, + } + engines: { node: ">=10" } + + "@pinojs/redact@0.4.0": + resolution: + { + integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==, + } + + "@rollup/rollup-android-arm-eabi@4.60.0": + resolution: + { + integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==, + } cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.0': - resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} + "@rollup/rollup-android-arm64@4.60.0": + resolution: + { + integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==, + } cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.0': - resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} + "@rollup/rollup-darwin-arm64@4.60.0": + resolution: + { + integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==, + } cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.0': - resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} + "@rollup/rollup-darwin-x64@4.60.0": + resolution: + { + integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==, + } cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.0': - resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} + "@rollup/rollup-freebsd-arm64@4.60.0": + resolution: + { + integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==, + } cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.0': - resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} + "@rollup/rollup-freebsd-x64@4.60.0": + resolution: + { + integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==, + } cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.0': - resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} + "@rollup/rollup-linux-arm-gnueabihf@4.60.0": + resolution: + { + integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==, + } cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.0': - resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} + "@rollup/rollup-linux-arm-musleabihf@4.60.0": + resolution: + { + integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==, + } cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.0': - resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} + "@rollup/rollup-linux-arm64-gnu@4.60.0": + resolution: + { + integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==, + } cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.0': - resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} + "@rollup/rollup-linux-arm64-musl@4.60.0": + resolution: + { + integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==, + } cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.0': - resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} + "@rollup/rollup-linux-loong64-gnu@4.60.0": + resolution: + { + integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==, + } cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.0': - resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} + "@rollup/rollup-linux-loong64-musl@4.60.0": + resolution: + { + integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==, + } cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.0': - resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} + "@rollup/rollup-linux-ppc64-gnu@4.60.0": + resolution: + { + integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==, + } cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.0': - resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} + "@rollup/rollup-linux-ppc64-musl@4.60.0": + resolution: + { + integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==, + } cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.0': - resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} + "@rollup/rollup-linux-riscv64-gnu@4.60.0": + resolution: + { + integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==, + } cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.0': - resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} + "@rollup/rollup-linux-riscv64-musl@4.60.0": + resolution: + { + integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==, + } cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.0': - resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} + "@rollup/rollup-linux-s390x-gnu@4.60.0": + resolution: + { + integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==, + } cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.0': - resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} + "@rollup/rollup-linux-x64-gnu@4.60.0": + resolution: + { + integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==, + } cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.0': - resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} + "@rollup/rollup-linux-x64-musl@4.60.0": + resolution: + { + integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==, + } cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.0': - resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} + "@rollup/rollup-openbsd-x64@4.60.0": + resolution: + { + integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==, + } cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.0': - resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} + "@rollup/rollup-openharmony-arm64@4.60.0": + resolution: + { + integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==, + } cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.0': - resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} + "@rollup/rollup-win32-arm64-msvc@4.60.0": + resolution: + { + integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==, + } cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.0': - resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} + "@rollup/rollup-win32-ia32-msvc@4.60.0": + resolution: + { + integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==, + } cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.0': - resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} + "@rollup/rollup-win32-x64-gnu@4.60.0": + resolution: + { + integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==, + } cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.0': - resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} + "@rollup/rollup-win32-x64-msvc@4.60.0": + resolution: + { + integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==, + } cpu: [x64] os: [win32] - '@simple-libs/child-process-utils@1.0.2': - resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} - engines: {node: '>=18'} - - '@simple-libs/stream-utils@1.2.0': - resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} - engines: {node: '>=18'} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - - '@tailwindcss/node@4.2.2': - resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} - - '@tailwindcss/oxide-android-arm64@4.2.2': - resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} - engines: {node: '>= 20'} + "@simple-libs/child-process-utils@1.0.2": + resolution: + { + integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==, + } + engines: { node: ">=18" } + + "@simple-libs/stream-utils@1.2.0": + resolution: + { + integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==, + } + engines: { node: ">=18" } + + "@standard-schema/spec@1.1.0": + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, + } + + "@swc/helpers@0.5.15": + resolution: + { + integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==, + } + + "@tailwindcss/node@4.2.2": + resolution: + { + integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==, + } + + "@tailwindcss/oxide-android-arm64@4.2.2": + resolution: + { + integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==, + } + engines: { node: ">= 20" } cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.2': - resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-darwin-arm64@4.2.2": + resolution: + { + integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==, + } + engines: { node: ">= 20" } cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.2': - resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-darwin-x64@4.2.2": + resolution: + { + integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==, + } + engines: { node: ">= 20" } cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.2': - resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-freebsd-x64@4.2.2": + resolution: + { + integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==, + } + engines: { node: ">= 20" } cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': - resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": + resolution: + { + integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==, + } + engines: { node: ">= 20" } cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': - resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": + resolution: + { + integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==, + } + engines: { node: ">= 20" } cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': - resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-linux-arm64-musl@4.2.2": + resolution: + { + integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==, + } + engines: { node: ">= 20" } cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': - resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-linux-x64-gnu@4.2.2": + resolution: + { + integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==, + } + engines: { node: ">= 20" } cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.2': - resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-linux-x64-musl@4.2.2": + resolution: + { + integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==, + } + engines: { node: ">= 20" } cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.2': - resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} - engines: {node: '>=14.0.0'} + "@tailwindcss/oxide-wasm32-wasi@4.2.2": + resolution: + { + integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==, + } + engines: { node: ">=14.0.0" } cpu: [wasm32] bundledDependencies: - - '@napi-rs/wasm-runtime' - - '@emnapi/core' - - '@emnapi/runtime' - - '@tybys/wasm-util' - - '@emnapi/wasi-threads' + - "@napi-rs/wasm-runtime" + - "@emnapi/core" + - "@emnapi/runtime" + - "@tybys/wasm-util" + - "@emnapi/wasi-threads" - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': - resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": + resolution: + { + integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==, + } + engines: { node: ">= 20" } cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': - resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} - engines: {node: '>= 20'} + "@tailwindcss/oxide-win32-x64-msvc@4.2.2": + resolution: + { + integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==, + } + engines: { node: ">= 20" } cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.2': - resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} - engines: {node: '>= 20'} - - '@tailwindcss/postcss@4.2.2': - resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} - - '@tailwindcss/typography@0.5.19': - resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + "@tailwindcss/oxide@4.2.2": + resolution: + { + integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==, + } + engines: { node: ">= 20" } + + "@tailwindcss/postcss@4.2.2": + resolution: + { + integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==, + } + + "@tailwindcss/typography@0.5.19": + resolution: + { + integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==, + } peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' - - '@tanstack/query-core@5.95.2': - resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==} - - '@tanstack/react-query@5.95.2': - resolution: {integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==} + tailwindcss: ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + + "@tanstack/query-core@5.95.2": + resolution: + { + integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==, + } + + "@tanstack/react-query@5.95.2": + resolution: + { + integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==, + } peerDependencies: react: ^18 || ^19 - '@testing-library/dom@10.4.1': - resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} - engines: {node: '>=18'} - - '@testing-library/jest-dom@6.9.1': - resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} - engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - - '@testing-library/react@16.3.2': - resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} - engines: {node: '>=18'} + "@testing-library/dom@10.4.1": + resolution: + { + integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, + } + engines: { node: ">=18" } + + "@testing-library/jest-dom@6.9.1": + resolution: + { + integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==, + } + engines: { node: ">=14", npm: ">=6", yarn: ">=1" } + + "@testing-library/react@16.3.2": + resolution: + { + integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==, + } + engines: { node: ">=18" } peerDependencies: - '@testing-library/dom': ^10.0.0 - '@types/react': ^18.0.0 || ^19.0.0 - '@types/react-dom': ^18.0.0 || ^19.0.0 + "@testing-library/dom": ^10.0.0 + "@types/react": ^18.0.0 || ^19.0.0 + "@types/react-dom": ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: - '@types/react': + "@types/react": optional: true - '@types/react-dom': + "@types/react-dom": optional: true - '@testing-library/user-event@14.6.1': - resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} - engines: {node: '>=12', npm: '>=6'} + "@testing-library/user-event@14.6.1": + resolution: + { + integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==, + } + engines: { node: ">=12", npm: ">=6" } peerDependencies: - '@testing-library/dom': '>=7.21.4' - - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node-cron@3.0.11': - resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==} - - '@types/node@25.5.0': - resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + "@testing-library/dom": ">=7.21.4" + + "@tybys/wasm-util@0.10.1": + resolution: + { + integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, + } + + "@types/aria-query@5.0.4": + resolution: + { + integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, + } + + "@types/chai@5.2.3": + resolution: + { + integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, + } + + "@types/debug@4.1.12": + resolution: + { + integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==, + } + + "@types/deep-eql@4.0.2": + resolution: + { + integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, + } + + "@types/estree-jsx@1.0.5": + resolution: + { + integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==, + } + + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + "@types/hast@3.0.4": + resolution: + { + integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==, + } + + "@types/mdast@4.0.4": + resolution: + { + integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, + } + + "@types/minimist@1.2.5": + resolution: + { + integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==, + } + + "@types/ms@2.1.0": + resolution: + { + integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, + } + + "@types/node-cron@3.0.11": + resolution: + { + integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==, + } + + "@types/node@25.5.0": + resolution: + { + integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==, + } + + "@types/normalize-package-data@2.4.4": + resolution: + { + integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, + } + + "@types/react-dom@19.2.3": + resolution: + { + integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==, + } peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@vitest/expect@4.1.1': - resolution: {integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==} - - '@vitest/mocker@4.1.1': - resolution: {integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==} + "@types/react": ^19.2.0 + + "@types/react@19.2.14": + resolution: + { + integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==, + } + + "@types/unist@2.0.11": + resolution: + { + integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==, + } + + "@types/unist@3.0.3": + resolution: + { + integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, + } + + "@ungap/structured-clone@1.3.0": + resolution: + { + integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==, + } + + "@vitest/expect@4.1.1": + resolution: + { + integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==, + } + + "@vitest/mocker@4.1.1": + resolution: + { + integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==, + } peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1443,470 +2164,788 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.1': - resolution: {integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==} - - '@vitest/runner@4.1.1': - resolution: {integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==} - - '@vitest/snapshot@4.1.1': - resolution: {integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==} - - '@vitest/spy@4.1.1': - resolution: {integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==} - - '@vitest/utils@4.1.1': - resolution: {integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==} + "@vitest/pretty-format@4.1.1": + resolution: + { + integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==, + } + + "@vitest/runner@4.1.1": + resolution: + { + integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==, + } + + "@vitest/snapshot@4.1.1": + resolution: + { + integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==, + } + + "@vitest/spy@4.1.1": + resolution: + { + integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==, + } + + "@vitest/utils@4.1.1": + resolution: + { + integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==, + } JSONStream@1.3.5: - resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + resolution: + { + integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, + } hasBin: true add-stream@1.0.0: - resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} + resolution: + { + integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==, + } agent-base@8.0.0: - resolution: {integrity: sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==, + } + engines: { node: ">= 14" } ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + resolution: + { + integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==, + } ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: ">=8" } ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, + } + engines: { node: ">=4" } ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: ">=8" } ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: ">=10" } argon2@0.44.0: - resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} - engines: {node: '>=16.17.0'} + resolution: + { + integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==, + } + engines: { node: ">=16.17.0" } argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + resolution: + { + integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, + } aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, + } aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, + } + engines: { node: ">= 0.4" } array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + resolution: + { + integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==, + } arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, + } + engines: { node: ">=0.10.0" } assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: ">=12" } atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} + resolution: + { + integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==, + } + engines: { node: ">=8.0.0" } bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + resolution: + { + integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==, + } balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } baseline-browser-mapping@2.10.9: - resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==, + } + engines: { node: ">=6.0.0" } hasBin: true bidi-js@1.0.3: - resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + resolution: + { + integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, + } brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + resolution: + { + integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, + } braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: ">=8" } buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + resolution: + { + integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, + } callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, + } + engines: { node: ">=6" } camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, + } + engines: { node: ">=8" } camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, + } + engines: { node: ">=6" } caniuse-lite@1.0.30001780: - resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} + resolution: + { + integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==, + } ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + resolution: + { + integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==, + } chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, + } + engines: { node: ">=18" } chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, + } + engines: { node: ">=4" } character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + resolution: + { + integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==, + } character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + resolution: + { + integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==, + } character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + resolution: + { + integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==, + } character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + resolution: + { + integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==, + } class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + resolution: + { + integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, + } claygl@1.3.0: - resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==} + resolution: + { + integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==, + } client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + resolution: + { + integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, + } cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + resolution: + { + integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, + } cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, + } + engines: { node: ">=12" } clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, + } + engines: { node: ">=6" } color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + resolution: + { + integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, + } color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: ">=7.0.0" } color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + resolution: + { + integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, + } color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + resolution: + { + integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, + } comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + resolution: + { + integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==, + } commit-and-tag-version@12.7.1: - resolution: {integrity: sha512-18+iV9VMPWQ5rryn1nKprvkwqFMx1eOcEwIprjgyGm8Blhsw7WnsFXgyYLIlBF4uG2fcbbps8P7fBZARvaA0VA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-18+iV9VMPWQ5rryn1nKprvkwqFMx1eOcEwIprjgyGm8Blhsw7WnsFXgyYLIlBF4uG2fcbbps8P7fBZARvaA0VA==, + } + engines: { node: ">=18" } hasBin: true compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + resolution: + { + integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==, + } concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: + { + integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, + } concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} + resolution: + { + integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==, + } + engines: { "0": node >= 6.0 } conventional-changelog-angular@6.0.0: - resolution: {integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==, + } + engines: { node: ">=14" } conventional-changelog-angular@8.3.0: - resolution: {integrity: sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==, + } + engines: { node: ">=18" } conventional-changelog-atom@3.0.0: - resolution: {integrity: sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g==, + } + engines: { node: ">=14" } conventional-changelog-codemirror@3.0.0: - resolution: {integrity: sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw==, + } + engines: { node: ">=14" } conventional-changelog-config-spec@2.1.0: - resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==} + resolution: + { + integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==, + } conventional-changelog-conventionalcommits@6.1.0: - resolution: {integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==, + } + engines: { node: ">=14" } conventional-changelog-conventionalcommits@9.3.0: - resolution: {integrity: sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==, + } + engines: { node: ">=18" } conventional-changelog-core@5.0.2: - resolution: {integrity: sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug==, + } + engines: { node: ">=14" } conventional-changelog-ember@3.0.0: - resolution: {integrity: sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A==, + } + engines: { node: ">=14" } conventional-changelog-eslint@4.0.0: - resolution: {integrity: sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q==, + } + engines: { node: ">=14" } conventional-changelog-express@3.0.0: - resolution: {integrity: sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA==, + } + engines: { node: ">=14" } conventional-changelog-jquery@4.0.0: - resolution: {integrity: sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw==, + } + engines: { node: ">=14" } conventional-changelog-jshint@3.0.0: - resolution: {integrity: sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA==, + } + engines: { node: ">=14" } conventional-changelog-preset-loader@3.0.0: - resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==, + } + engines: { node: ">=14" } conventional-changelog-writer@6.0.1: - resolution: {integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==, + } + engines: { node: ">=14" } hasBin: true conventional-changelog@4.0.0: - resolution: {integrity: sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ==, + } + engines: { node: ">=14" } conventional-commits-filter@3.0.0: - resolution: {integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==, + } + engines: { node: ">=14" } conventional-commits-parser@4.0.0: - resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==, + } + engines: { node: ">=14" } hasBin: true conventional-commits-parser@6.3.0: - resolution: {integrity: sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==, + } + engines: { node: ">=18" } hasBin: true conventional-recommended-bump@7.0.1: - resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==, + } + engines: { node: ">=14" } hasBin: true convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + resolution: + { + integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, + } cosmiconfig-typescript-loader@6.2.0: - resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} - engines: {node: '>=v18'} + resolution: + { + integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==, + } + engines: { node: ">=v18" } peerDependencies: - '@types/node': '*' - cosmiconfig: '>=9' - typescript: '>=5' + "@types/node": "*" + cosmiconfig: ">=9" + typescript: ">=5" cosmiconfig@9.0.1: - resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==, + } + engines: { node: ">=14" } peerDependencies: - typescript: '>=4.9.5' + typescript: ">=4.9.5" peerDependenciesMeta: typescript: optional: true cross-env@10.1.0: - resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==, + } + engines: { node: ">=20" } hasBin: true cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: ">= 8" } css-tree@3.2.1: - resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + resolution: + { + integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } css.escape@1.5.1: - resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + resolution: + { + integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, + } cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, + } + engines: { node: ">=4" } hasBin: true csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } dargs@7.0.0: - resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, + } + engines: { node: ">=8" } data-urls@7.0.0: - resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } dateformat@3.0.3: - resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + resolution: + { + integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==, + } dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + resolution: + { + integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==, + } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } peerDependencies: - supports-color: '*' + supports-color: "*" peerDependenciesMeta: supports-color: optional: true decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, + } + engines: { node: ">=0.10.0" } decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, + } + engines: { node: ">=0.10.0" } decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + resolution: + { + integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==, + } dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: ">=6" } detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==, + } + engines: { node: ">=8" } detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, + } + engines: { node: ">=8" } devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + resolution: + { + integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, + } dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + resolution: + { + integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, + } dom-accessibility-api@0.6.3: - resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + resolution: + { + integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==, + } dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==, + } + engines: { node: ">=8" } dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==, + } + engines: { node: ">=12" } dotgitignore@2.1.0: - resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==, + } + engines: { node: ">=6" } drizzle-kit@0.31.10: - resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} + resolution: + { + integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==, + } hasBin: true drizzle-orm@0.45.1: - resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==} + resolution: + { + integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==, + } peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=4' - '@electric-sql/pglite': '>=0.2.0' - '@libsql/client': '>=0.10.0' - '@libsql/client-wasm': '>=0.10.0' - '@neondatabase/serverless': '>=0.10.0' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1.13' - '@prisma/client': '*' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/sql.js': '*' - '@upstash/redis': '>=1.34.7' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=14.0.0' - gel: '>=2' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - prisma: '*' - sql.js: '>=1' - sqlite3: '>=5' + "@aws-sdk/client-rds-data": ">=3" + "@cloudflare/workers-types": ">=4" + "@electric-sql/pglite": ">=0.2.0" + "@libsql/client": ">=0.10.0" + "@libsql/client-wasm": ">=0.10.0" + "@neondatabase/serverless": ">=0.10.0" + "@op-engineering/op-sqlite": ">=2" + "@opentelemetry/api": ^1.4.1 + "@planetscale/database": ">=1.13" + "@prisma/client": "*" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/sql.js": "*" + "@upstash/redis": ">=1.34.7" + "@vercel/postgres": ">=0.8.0" + "@xata.io/client": "*" + better-sqlite3: ">=7" + bun-types: "*" + expo-sqlite: ">=14.0.0" + gel: ">=2" + knex: "*" + kysely: "*" + mysql2: ">=2" + pg: ">=8" + postgres: ">=3" + prisma: "*" + sql.js: ">=1" + sqlite3: ">=5" peerDependenciesMeta: - '@aws-sdk/client-rds-data': + "@aws-sdk/client-rds-data": optional: true - '@cloudflare/workers-types': + "@cloudflare/workers-types": optional: true - '@electric-sql/pglite': + "@electric-sql/pglite": optional: true - '@libsql/client': + "@libsql/client": optional: true - '@libsql/client-wasm': + "@libsql/client-wasm": optional: true - '@neondatabase/serverless': + "@neondatabase/serverless": optional: true - '@op-engineering/op-sqlite': + "@op-engineering/op-sqlite": optional: true - '@opentelemetry/api': + "@opentelemetry/api": optional: true - '@planetscale/database': + "@planetscale/database": optional: true - '@prisma/client': + "@prisma/client": optional: true - '@tidbcloud/serverless': + "@tidbcloud/serverless": optional: true - '@types/better-sqlite3': + "@types/better-sqlite3": optional: true - '@types/pg': + "@types/pg": optional: true - '@types/sql.js': + "@types/sql.js": optional: true - '@upstash/redis': + "@upstash/redis": optional: true - '@vercel/postgres': + "@vercel/postgres": optional: true - '@xata.io/client': + "@xata.io/client": optional: true better-sqlite3: optional: true @@ -1934,111 +2973,198 @@ packages: optional: true echarts-for-react@3.0.6: - resolution: {integrity: sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==} + resolution: + { + integrity: sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==, + } peerDependencies: echarts: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 react: ^15.0.0 || >=16.0.0 echarts-gl@2.0.9: - resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==} + resolution: + { + integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==, + } peerDependencies: echarts: ^5.1.2 echarts@6.0.0: - resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==} + resolution: + { + integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==, + } emoji-picker-react@4.18.0: - resolution: {integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==, + } + engines: { node: ">=10" } peerDependencies: - react: '>=16' + react: ">=16" emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, + } end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + resolution: + { + integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, + } enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} - engines: {node: '>=10.13.0'} + resolution: + { + integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==, + } + engines: { node: ">=10.13.0" } entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} + resolution: + { + integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, + } + engines: { node: ">=0.12" } env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==, + } + engines: { node: ">=6" } error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + resolution: + { + integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, + } es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + resolution: + { + integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==, + } esbuild@0.27.4: - resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==, + } + engines: { node: ">=18" } hasBin: true escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + resolution: + { + integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, + } + engines: { node: ">=0.8.0" } escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, + } + engines: { node: ">=12" } estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + resolution: + { + integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==, + } estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, + } + engines: { node: ">=12.0.0" } extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + resolution: + { + integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, + } fast-copy@4.0.2: - resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} + resolution: + { + integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==, + } fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} + resolution: + { + integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, + } + engines: { node: ">=8.6.0" } fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + resolution: + { + integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==, + } fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + resolution: + { + integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, + } fast-xml-builder@1.1.4: - resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} + resolution: + { + integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==, + } fast-xml-parser@5.5.7: - resolution: {integrity: sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==} + resolution: + { + integrity: sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==, + } hasBin: true fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + resolution: + { + integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, + } fd-package-json@2.0.0: - resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + resolution: + { + integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==, + } fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -2046,253 +3172,451 @@ packages: optional: true figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, + } + engines: { node: ">=8" } fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: ">=8" } find-up@2.1.0: - resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==, + } + engines: { node: ">=4" } find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==, + } + engines: { node: ">=6" } find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, + } + engines: { node: ">=8" } find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: ">=10" } flairup@1.0.0: - resolution: {integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==} + resolution: + { + integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==, + } formatly@0.3.0: - resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} - engines: {node: '>=18.3.0'} + resolution: + { + integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==, + } + engines: { node: ">=18.3.0" } hasBin: true fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } get-pkg-repo@4.2.1: - resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} - engines: {node: '>=6.9.0'} + resolution: + { + integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==, + } + engines: { node: ">=6.9.0" } hasBin: true get-tsconfig@4.13.6: - resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + resolution: + { + integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, + } get-tsconfig@4.13.7: - resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + resolution: + { + integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==, + } git-raw-commits@3.0.0: - resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==, + } + engines: { node: ">=14" } deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true git-raw-commits@5.0.1: - resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==, + } + engines: { node: ">=18" } hasBin: true git-remote-origin-url@2.0.0: - resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==, + } + engines: { node: ">=4" } git-semver-tags@5.0.1: - resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==, + } + engines: { node: ">=14" } deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true gitconfiglocal@1.0.0: - resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} + resolution: + { + integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==, + } glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: ">= 6" } global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==, + } + engines: { node: ">=18" } graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} + resolution: + { + integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==, + } + engines: { node: ">=0.4.7" } hasBin: true hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, + } + engines: { node: ">=6" } has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, + } + engines: { node: ">=4" } hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, + } + engines: { node: ">= 0.4" } hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + resolution: + { + integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==, + } hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + resolution: + { + integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==, + } help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + resolution: + { + integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==, + } hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + resolution: + { + integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, + } hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, + } + engines: { node: ">=10" } html-encoding-sniffer@6.0.0: - resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } html-url-attributes@3.0.1: - resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + resolution: + { + integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==, + } https-proxy-agent@8.0.0: - resolution: {integrity: sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==, + } + engines: { node: ">= 14" } husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, + } + engines: { node: ">=18" } hasBin: true import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, + } + engines: { node: ">=6" } import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + resolution: + { + integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, + } indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, + } + engines: { node: ">=8" } inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + resolution: + { + integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, + } ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + resolution: + { + integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, + } ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + resolution: + { + integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==, + } + engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } inline-style-parser@0.2.7: - resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + resolution: + { + integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==, + } ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} + resolution: + { + integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==, + } + engines: { node: ">= 12" } is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + resolution: + { + integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==, + } is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + resolution: + { + integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==, + } is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + resolution: + { + integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, + } is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, + } + engines: { node: ">= 0.4" } is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + resolution: + { + integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==, + } is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: ">=0.10.0" } is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, + } + engines: { node: ">=8" } is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: ">=0.10.0" } is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + resolution: + { + integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==, + } is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: ">=0.12.0" } is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==, + } + engines: { node: ">=8" } is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, + } + engines: { node: ">=0.10.0" } is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, + } + engines: { node: ">=12" } is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } is-text-path@1.0.1: - resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==, + } + engines: { node: ">=0.10.0" } isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + resolution: + { + integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, + } isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + resolution: + { + integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, + } hasBin: true jose@6.2.2: - resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + resolution: + { + integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==, + } joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, + } + engines: { node: ">=10" } js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + resolution: + { + integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, + } hasBin: true jsdom@29.0.1: - resolution: {integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + resolution: + { + integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24.0.0 } peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -2300,369 +3624,660 @@ packages: optional: true json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + resolution: + { + integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, + } json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + resolution: + { + integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, + } json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, + } json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + resolution: + { + integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==, + } jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} + resolution: + { + integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, + } + engines: { "0": node >= 0.2.0 } kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, + } + engines: { node: ">=0.10.0" } knip@6.0.5: - resolution: {integrity: sha512-+i9e/ZKuYlECB5iIK82NQwnYso4oNLBhzsTbXhSqCG1qfGi6D84GNtRENafmS3C0lABX8Wf3BKM434nPXi2AbQ==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-+i9e/ZKuYlECB5iIK82NQwnYso4oNLBhzsTbXhSqCG1qfGi6D84GNtRENafmS3C0lABX8Wf3BKM434nPXi2AbQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, + } + engines: { node: ">= 12.0.0" } cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, + } + engines: { node: ">= 12.0.0" } cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, + } + engines: { node: ">= 12.0.0" } cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, + } + engines: { node: ">= 12.0.0" } cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, + } + engines: { node: ">= 12.0.0" } cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, + } + engines: { node: ">= 12.0.0" } cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, + } + engines: { node: ">= 12.0.0" } cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, + } + engines: { node: ">= 12.0.0" } cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, + } + engines: { node: ">= 12.0.0" } cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, + } + engines: { node: ">= 12.0.0" } cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, + } + engines: { node: ">= 12.0.0" } cpu: [x64] os: [win32] lightningcss@1.32.0: - resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, + } + engines: { node: ">= 12.0.0" } lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, + } load-json-file@4.0.0: - resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==, + } + engines: { node: ">=4" } locate-path@2.0.0: - resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==, + } + engines: { node: ">=4" } locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==, + } + engines: { node: ">=6" } locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, + } + engines: { node: ">=8" } locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: ">=10" } lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + resolution: + { + integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==, + } lodash.ismatch@4.4.0: - resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + resolution: + { + integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==, + } lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + resolution: + { + integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==, + } lodash.mergewith@4.6.2: - resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + resolution: + { + integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==, + } lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + resolution: + { + integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==, + } lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + resolution: + { + integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==, + } lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + resolution: + { + integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==, + } longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + resolution: + { + integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, + } lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} - engines: {node: 20 || >=22} + resolution: + { + integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==, + } + engines: { node: 20 || >=22 } lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, + } + engines: { node: ">=10" } lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + resolution: + { + integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, + } hasBin: true magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, + } + engines: { node: ">=0.10.0" } map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, + } + engines: { node: ">=8" } markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + resolution: + { + integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, + } mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + resolution: + { + integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==, + } mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + resolution: + { + integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==, + } mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + resolution: + { + integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, + } mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + resolution: + { + integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==, + } mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + resolution: + { + integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==, + } mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + resolution: + { + integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==, + } mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + resolution: + { + integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, + } mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + resolution: + { + integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==, + } mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + resolution: + { + integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==, + } mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + resolution: + { + integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==, + } mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + resolution: + { + integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==, + } mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + resolution: + { + integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, + } mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + resolution: + { + integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==, + } mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + resolution: + { + integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==, + } mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + resolution: + { + integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, + } mdn-data@2.27.1: - resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + resolution: + { + integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, + } meow@13.2.0: - resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==, + } + engines: { node: ">=18" } meow@8.1.2: - resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==, + } + engines: { node: ">=10" } merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + resolution: + { + integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, + } + engines: { node: ">= 8" } micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + resolution: + { + integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==, + } micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + resolution: + { + integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, + } micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + resolution: + { + integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, + } micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + resolution: + { + integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, + } micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + resolution: + { + integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==, + } micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + resolution: + { + integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, + } micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + resolution: + { + integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, + } micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + resolution: + { + integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, + } micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + resolution: + { + integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, + } micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + resolution: + { + integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, + } micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + resolution: + { + integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==, + } micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + resolution: + { + integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, + } micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + resolution: + { + integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, + } micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + resolution: + { + integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, + } micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + resolution: + { + integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, + } micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + resolution: + { + integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, + } micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + resolution: + { + integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, + } micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + resolution: + { + integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, + } micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + resolution: + { + integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, + } micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + resolution: + { + integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, + } micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + resolution: + { + integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, + } micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + resolution: + { + integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, + } micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + resolution: + { + integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, + } micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + resolution: + { + integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, + } micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + resolution: + { + integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==, + } micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + resolution: + { + integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, + } micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + resolution: + { + integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==, + } micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + resolution: + { + integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==, + } micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + resolution: + { + integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, + } + engines: { node: ">=8.6" } min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, + } + engines: { node: ">=4" } minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + resolution: + { + integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, + } minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, + } + engines: { node: ">= 6" } minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + resolution: + { + integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, + } modify-values@1.0.1: - resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==, + } + engines: { node: ">=0.10.0" } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } hasBin: true neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + resolution: + { + integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, + } next@16.2.1: - resolution: {integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==} - engines: {node: '>=20.9.0'} + resolution: + { + integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==, + } + engines: { node: ">=20.9.0" } hasBin: true peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: - '@opentelemetry/api': + "@opentelemetry/api": optional: true - '@playwright/test': + "@playwright/test": optional: true babel-plugin-react-compiler: optional: true @@ -2670,650 +4285,1160 @@ packages: optional: true node-addon-api@8.6.0: - resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} - engines: {node: ^18 || ^20 || >= 21} + resolution: + { + integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==, + } + engines: { node: ^18 || ^20 || >= 21 } node-cron@4.2.1: - resolution: {integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==, + } + engines: { node: ">=6.0.0" } node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + resolution: + { + integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==, + } hasBin: true normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + resolution: + { + integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, + } normalize-package-data@3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, + } + engines: { node: ">=10" } obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + resolution: + { + integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==, + } on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==, + } + engines: { node: ">=14.0.0" } once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + resolution: + { + integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, + } otpauth@9.5.0: - resolution: {integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==} + resolution: + { + integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==, + } oxc-parser@0.120.0: - resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } oxc-resolver@11.19.1: - resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} + resolution: + { + integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==, + } p-limit@1.3.0: - resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==, + } + engines: { node: ">=4" } p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, + } + engines: { node: ">=6" } p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: ">=10" } p-locate@2.0.0: - resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==, + } + engines: { node: ">=4" } p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==, + } + engines: { node: ">=6" } p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, + } + engines: { node: ">=8" } p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: ">=10" } p-try@1.0.0: - resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==, + } + engines: { node: ">=4" } p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, + } + engines: { node: ">=6" } parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, + } + engines: { node: ">=6" } parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + resolution: + { + integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==, + } parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==, + } + engines: { node: ">=4" } parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, + } + engines: { node: ">=8" } parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + resolution: + { + integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==, + } path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==, + } + engines: { node: ">=4" } path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: ">=8" } path-expression-matcher@1.1.3: - resolution: {integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==, + } + engines: { node: ">=14.0.0" } path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: ">=8" } path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } path-type@3.0.0: - resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==, + } + engines: { node: ">=4" } pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} + resolution: + { + integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, + } + engines: { node: ">=8.6" } picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: ">=12" } picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==, + } + engines: { node: ">=12" } pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, + } + engines: { node: ">=0.10.0" } pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==, + } + engines: { node: ">=4" } pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + resolution: + { + integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==, + } pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + resolution: + { + integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==, + } hasBin: true pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + resolution: + { + integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==, + } pino@10.3.1: - resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} + resolution: + { + integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==, + } hasBin: true postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, + } + engines: { node: ">=4" } postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, + } + engines: { node: ^10 || ^12 || >=14 } postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} - engines: {node: ^10 || ^12 || >=14} + resolution: + { + integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==, + } + engines: { node: ^10 || ^12 || >=14 } postgres@3.4.8: - resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==, + } + engines: { node: ">=12" } prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, + } + engines: { node: ">=14" } hasBin: true pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + resolution: + { + integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, + } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + resolution: + { + integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, + } process-warning@5.0.0: - resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + resolution: + { + integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==, + } property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + resolution: + { + integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==, + } pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + resolution: + { + integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==, + } punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: ">=6" } qrcode.react@4.2.0: - resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + resolution: + { + integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==, + } peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + resolution: + { + integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, + } quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + resolution: + { + integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==, + } quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, + } + engines: { node: ">=8" } react-colorful@5.6.1: - resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} + resolution: + { + integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==, + } peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' + react: ">=16.8.0" + react-dom: ">=16.8.0" react-dom@19.2.4: - resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + resolution: + { + integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==, + } peerDependencies: react: ^19.2.4 react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + resolution: + { + integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, + } react-markdown@10.1.0: - resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + resolution: + { + integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==, + } peerDependencies: - '@types/react': '>=18' - react: '>=18' + "@types/react": ">=18" + react: ">=18" react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==, + } + engines: { node: ">=0.10.0" } read-pkg-up@3.0.0: - resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==, + } + engines: { node: ">=4" } read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, + } + engines: { node: ">=8" } read-pkg@3.0.0: - resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==, + } + engines: { node: ">=4" } read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, + } + engines: { node: ">=8" } readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + resolution: + { + integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, + } readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, + } + engines: { node: ">= 6" } real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} + resolution: + { + integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==, + } + engines: { node: ">= 12.13.0" } redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, + } + engines: { node: ">=8" } remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + resolution: + { + integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==, + } remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + resolution: + { + integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, + } remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + resolution: + { + integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==, + } remark-stringify@11.0.0: - resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + resolution: + { + integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, + } require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, + } + engines: { node: ">=0.10.0" } require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: ">=0.10.0" } resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, + } + engines: { node: ">=4" } resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, + } + engines: { node: ">=8" } resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, + } + engines: { node: ">= 0.4" } hasBin: true reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + resolution: + { + integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, + } + engines: { iojs: ">=1.0.0", node: ">=0.10.0" } rollup@4.60.0: - resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + resolution: + { + integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==, + } + engines: { node: ">=18.0.0", npm: ">=8.0.0" } hasBin: true run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + resolution: + { + integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, + } safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + resolution: + { + integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, + } safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + resolution: + { + integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, + } safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==, + } + engines: { node: ">=10" } saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} + resolution: + { + integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, + } + engines: { node: ">=v12.22.7" } scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + resolution: + { + integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, + } secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + resolution: + { + integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==, + } semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + resolution: + { + integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, + } hasBin: true semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, + } + engines: { node: ">=10" } hasBin: true sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + resolution: + { + integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, + } + engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: ">=8" } shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: ">=8" } siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } size-sensor@1.0.3: - resolution: {integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==} + resolution: + { + integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==, + } smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + resolution: + { + integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==, + } + engines: { node: ">= 6.0.0", npm: ">= 3.0.0" } smol-toml@1.6.0: - resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==, + } + engines: { node: ">= 18" } socks-proxy-agent@9.0.0: - resolution: {integrity: sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==} - engines: {node: '>= 14'} + resolution: + { + integrity: sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==, + } + engines: { node: ">= 14" } socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + resolution: + { + integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==, + } + engines: { node: ">= 10.0.0", npm: ">= 3.0.0" } sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + resolution: + { + integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==, + } source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: ">=0.10.0" } source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + resolution: + { + integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, + } source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, + } + engines: { node: ">=0.10.0" } space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + resolution: + { + integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, + } spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + resolution: + { + integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, + } spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + resolution: + { + integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, + } spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + resolution: + { + integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, + } spdx-license-ids@3.0.23: - resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + resolution: + { + integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==, + } split2@3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + resolution: + { + integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==, + } split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + resolution: + { + integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==, + } + engines: { node: ">= 10.x" } split@1.0.1: - resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} + resolution: + { + integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==, + } stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } std-env@4.0.0: - resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + resolution: + { + integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==, + } string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, + } + engines: { node: ">=8" } string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + resolution: + { + integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, + } string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + resolution: + { + integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, + } stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + resolution: + { + integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==, + } strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: ">=8" } strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, + } + engines: { node: ">=4" } strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, + } + engines: { node: ">=8" } strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} - engines: {node: '>=14.16'} + resolution: + { + integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==, + } + engines: { node: ">=14.16" } strnum@2.2.0: - resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} + resolution: + { + integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==, + } style-to-js@1.1.21: - resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + resolution: + { + integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==, + } style-to-object@1.0.14: - resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + resolution: + { + integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==, + } styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} + resolution: + { + integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, + } + engines: { node: ">= 12.0.0" } peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + "@babel/core": "*" + babel-plugin-macros: "*" + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" peerDependenciesMeta: - '@babel/core': + "@babel/core": optional: true babel-plugin-macros: optional: true supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + resolution: + { + integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, + } + engines: { node: ">=4" } supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: ">= 0.4" } symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } tailwindcss@4.2.2: - resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + resolution: + { + integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==, + } tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} - engines: {node: '>=6'} + resolution: + { + integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, + } + engines: { node: ">=6" } text-extensions@1.9.0: - resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} - engines: {node: '>=0.10'} + resolution: + { + integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==, + } + engines: { node: ">=0.10" } thread-stream@4.0.0: - resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==, + } + engines: { node: ">=20" } through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + resolution: + { + integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==, + } through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + resolution: + { + integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, + } tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==, + } + engines: { node: ">=18" } tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: ">=12.0.0" } tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} - engines: {node: '>=14.0.0'} + resolution: + { + integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==, + } + engines: { node: ">=14.0.0" } tldts-core@7.0.27: - resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} + resolution: + { + integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==, + } tldts@7.0.27: - resolution: {integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==} + resolution: + { + integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==, + } hasBin: true to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: ">=8.0" } tough-cookie@6.0.1: - resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} - engines: {node: '>=16'} + resolution: + { + integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==, + } + engines: { node: ">=16" } tr46@6.0.0: - resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, + } + engines: { node: ">=20" } trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + resolution: + { + integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, + } trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, + } + engines: { node: ">=8" } trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + resolution: + { + integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, + } tslib@2.3.0: - resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + resolution: + { + integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, + } tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} - engines: {node: '>=18.0.0'} + resolution: + { + integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, + } + engines: { node: ">=18.0.0" } hasBin: true type-fest@0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, + } + engines: { node: ">=10" } type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, + } + engines: { node: ">=8" } type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, + } + engines: { node: ">=8" } typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + resolution: + { + integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, + } typescript@6.0.2: - resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==, + } + engines: { node: ">=14.17" } hasBin: true uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} + resolution: + { + integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, + } + engines: { node: ">=0.8.0" } hasBin: true unbash@2.2.0: - resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==, + } + engines: { node: ">=14" } undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + resolution: + { + integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==, + } undici@7.24.5: - resolution: {integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==} - engines: {node: '>=20.18.1'} + resolution: + { + integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==, + } + engines: { node: ">=20.18.1" } unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + resolution: + { + integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, + } unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + resolution: + { + integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==, + } unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + resolution: + { + integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==, + } unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + resolution: + { + integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==, + } unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + resolution: + { + integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==, + } unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + resolution: + { + integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==, + } util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + resolution: + { + integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, + } validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + resolution: + { + integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, + } vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + resolution: + { + integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==, + } vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + resolution: + { + integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==, + } vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} - engines: {node: ^20.19.0 || >=22.12.0} + resolution: + { + integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } hasBin: true peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" less: ^4.0.0 lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 - stylus: '>=0.54.8' + stylus: ">=0.54.8" sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: - '@types/node': + "@types/node": optional: true jiti: optional: true @@ -3337,34 +5462,37 @@ packages: optional: true vitest@4.1.1: - resolution: {integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + resolution: + { + integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==, + } + engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } hasBin: true peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.1 - '@vitest/browser-preview': 4.1.1 - '@vitest/browser-webdriverio': 4.1.1 - '@vitest/ui': 4.1.1 - happy-dom: '*' - jsdom: '*' + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.1.1 + "@vitest/browser-preview": 4.1.1 + "@vitest/browser-webdriverio": 4.1.1 + "@vitest/ui": 4.1.1 + happy-dom: "*" + jsdom: "*" vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - '@edge-runtime/vm': + "@edge-runtime/vm": optional: true - '@opentelemetry/api': + "@opentelemetry/api": optional: true - '@types/node': + "@types/node": optional: true - '@vitest/browser-playwright': + "@vitest/browser-playwright": optional: true - '@vitest/browser-preview': + "@vitest/browser-preview": optional: true - '@vitest/browser-webdriverio': + "@vitest/browser-webdriverio": optional: true - '@vitest/ui': + "@vitest/ui": optional: true happy-dom: optional: true @@ -3372,258 +5500,335 @@ packages: optional: true w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, + } + engines: { node: ">=18" } walk-up-path@4.0.0: - resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} - engines: {node: 20 || >=22} + resolution: + { + integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==, + } + engines: { node: 20 || >=22 } webidl-conversions@8.0.1: - resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, + } + engines: { node: ">=20" } whatwg-mimetype@5.0.0: - resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, + } + engines: { node: ">=20" } whatwg-url@16.0.1: - resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + resolution: + { + integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: ">= 8" } hasBin: true why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: ">=8" } hasBin: true wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + resolution: + { + integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, + } wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, + } + engines: { node: ">=10" } wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + resolution: + { + integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, + } xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, + } + engines: { node: ">=18" } xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + resolution: + { + integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, + } + engines: { node: ">=0.4" } y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: ">=10" } yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + resolution: + { + integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, + } yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} + resolution: + { + integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==, + } + engines: { node: ">= 14.6" } hasBin: true yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} + resolution: + { + integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==, + } + engines: { node: ">= 14.6" } hasBin: true yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, + } + engines: { node: ">=10" } yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, + } + engines: { node: ">=12" } yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==, + } + engines: { node: ">=10" } yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, + } + engines: { node: ">=12" } yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: ">=10" } zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + resolution: + { + integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==, + } zrender@5.6.1: - resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + resolution: + { + integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==, + } zrender@6.0.0: - resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} + resolution: + { + integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==, + } zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + resolution: + { + integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==, + } snapshots: + "@adobe/css-tools@4.4.4": {} - '@adobe/css-tools@4.4.4': {} - - '@alloc/quick-lru@5.2.0': {} + "@alloc/quick-lru@5.2.0": {} - '@asamuzakjp/css-color@5.0.1': + "@asamuzakjp/css-color@5.0.1": dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + "@csstools/css-calc": 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-color-parser": 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 lru-cache: 11.2.7 - '@asamuzakjp/dom-selector@7.0.4': + "@asamuzakjp/dom-selector@7.0.4": dependencies: - '@asamuzakjp/nwsapi': 2.3.9 + "@asamuzakjp/nwsapi": 2.3.9 bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.2.7 - '@asamuzakjp/nwsapi@2.3.9': {} + "@asamuzakjp/nwsapi@2.3.9": {} - '@babel/code-frame@7.29.0': + "@babel/code-frame@7.29.0": dependencies: - '@babel/helper-validator-identifier': 7.28.5 + "@babel/helper-validator-identifier": 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-validator-identifier@7.28.5': {} + "@babel/helper-validator-identifier@7.28.5": {} - '@babel/runtime@7.29.2': {} + "@babel/runtime@7.29.2": {} - '@biomejs/biome@2.4.8': + "@biomejs/biome@2.4.8": optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.8 - '@biomejs/cli-darwin-x64': 2.4.8 - '@biomejs/cli-linux-arm64': 2.4.8 - '@biomejs/cli-linux-arm64-musl': 2.4.8 - '@biomejs/cli-linux-x64': 2.4.8 - '@biomejs/cli-linux-x64-musl': 2.4.8 - '@biomejs/cli-win32-arm64': 2.4.8 - '@biomejs/cli-win32-x64': 2.4.8 + "@biomejs/cli-darwin-arm64": 2.4.8 + "@biomejs/cli-darwin-x64": 2.4.8 + "@biomejs/cli-linux-arm64": 2.4.8 + "@biomejs/cli-linux-arm64-musl": 2.4.8 + "@biomejs/cli-linux-x64": 2.4.8 + "@biomejs/cli-linux-x64-musl": 2.4.8 + "@biomejs/cli-win32-arm64": 2.4.8 + "@biomejs/cli-win32-x64": 2.4.8 - '@biomejs/cli-darwin-arm64@2.4.8': + "@biomejs/cli-darwin-arm64@2.4.8": optional: true - '@biomejs/cli-darwin-x64@2.4.8': + "@biomejs/cli-darwin-x64@2.4.8": optional: true - '@biomejs/cli-linux-arm64-musl@2.4.8': + "@biomejs/cli-linux-arm64-musl@2.4.8": optional: true - '@biomejs/cli-linux-arm64@2.4.8': + "@biomejs/cli-linux-arm64@2.4.8": optional: true - '@biomejs/cli-linux-x64-musl@2.4.8': + "@biomejs/cli-linux-x64-musl@2.4.8": optional: true - '@biomejs/cli-linux-x64@2.4.8': + "@biomejs/cli-linux-x64@2.4.8": optional: true - '@biomejs/cli-win32-arm64@2.4.8': + "@biomejs/cli-win32-arm64@2.4.8": optional: true - '@biomejs/cli-win32-x64@2.4.8': + "@biomejs/cli-win32-x64@2.4.8": optional: true - '@bramus/specificity@2.4.2': + "@bramus/specificity@2.4.2": dependencies: css-tree: 3.2.1 - '@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2)': + "@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2)": dependencies: - '@commitlint/format': 20.5.0 - '@commitlint/lint': 20.5.0 - '@commitlint/load': 20.5.0(@types/node@25.5.0)(typescript@6.0.2) - '@commitlint/read': 20.5.0(conventional-commits-parser@6.3.0) - '@commitlint/types': 20.5.0 + "@commitlint/format": 20.5.0 + "@commitlint/lint": 20.5.0 + "@commitlint/load": 20.5.0(@types/node@25.5.0)(typescript@6.0.2) + "@commitlint/read": 20.5.0(conventional-commits-parser@6.3.0) + "@commitlint/types": 20.5.0 tinyexec: 1.0.4 yargs: 17.7.2 transitivePeerDependencies: - - '@types/node' + - "@types/node" - conventional-commits-filter - conventional-commits-parser - typescript - '@commitlint/config-conventional@20.5.0': + "@commitlint/config-conventional@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 conventional-changelog-conventionalcommits: 9.3.0 - '@commitlint/config-validator@20.5.0': + "@commitlint/config-validator@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 ajv: 8.18.0 - '@commitlint/ensure@20.5.0': + "@commitlint/ensure@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 lodash.startcase: 4.4.0 lodash.upperfirst: 4.3.1 - '@commitlint/execute-rule@20.0.0': {} + "@commitlint/execute-rule@20.0.0": {} - '@commitlint/format@20.5.0': + "@commitlint/format@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 picocolors: 1.1.1 - '@commitlint/is-ignored@20.5.0': + "@commitlint/is-ignored@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 semver: 7.7.4 - '@commitlint/lint@20.5.0': + "@commitlint/lint@20.5.0": dependencies: - '@commitlint/is-ignored': 20.5.0 - '@commitlint/parse': 20.5.0 - '@commitlint/rules': 20.5.0 - '@commitlint/types': 20.5.0 + "@commitlint/is-ignored": 20.5.0 + "@commitlint/parse": 20.5.0 + "@commitlint/rules": 20.5.0 + "@commitlint/types": 20.5.0 - '@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@6.0.2)': + "@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@6.0.2)": dependencies: - '@commitlint/config-validator': 20.5.0 - '@commitlint/execute-rule': 20.0.0 - '@commitlint/resolve-extends': 20.5.0 - '@commitlint/types': 20.5.0 + "@commitlint/config-validator": 20.5.0 + "@commitlint/execute-rule": 20.0.0 + "@commitlint/resolve-extends": 20.5.0 + "@commitlint/types": 20.5.0 cosmiconfig: 9.0.1(typescript@6.0.2) cosmiconfig-typescript-loader: 6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 transitivePeerDependencies: - - '@types/node' + - "@types/node" - typescript - '@commitlint/message@20.4.3': {} + "@commitlint/message@20.4.3": {} - '@commitlint/parse@20.5.0': + "@commitlint/parse@20.5.0": dependencies: - '@commitlint/types': 20.5.0 + "@commitlint/types": 20.5.0 conventional-changelog-angular: 8.3.0 conventional-commits-parser: 6.3.0 - '@commitlint/read@20.5.0(conventional-commits-parser@6.3.0)': + "@commitlint/read@20.5.0(conventional-commits-parser@6.3.0)": dependencies: - '@commitlint/top-level': 20.4.3 - '@commitlint/types': 20.5.0 + "@commitlint/top-level": 20.4.3 + "@commitlint/types": 20.5.0 git-raw-commits: 5.0.1(conventional-commits-parser@6.3.0) minimist: 1.2.8 tinyexec: 1.0.4 @@ -3631,589 +5836,589 @@ snapshots: - conventional-commits-filter - conventional-commits-parser - '@commitlint/resolve-extends@20.5.0': + "@commitlint/resolve-extends@20.5.0": dependencies: - '@commitlint/config-validator': 20.5.0 - '@commitlint/types': 20.5.0 + "@commitlint/config-validator": 20.5.0 + "@commitlint/types": 20.5.0 global-directory: 4.0.1 import-meta-resolve: 4.2.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - '@commitlint/rules@20.5.0': + "@commitlint/rules@20.5.0": dependencies: - '@commitlint/ensure': 20.5.0 - '@commitlint/message': 20.4.3 - '@commitlint/to-lines': 20.0.0 - '@commitlint/types': 20.5.0 + "@commitlint/ensure": 20.5.0 + "@commitlint/message": 20.4.3 + "@commitlint/to-lines": 20.0.0 + "@commitlint/types": 20.5.0 - '@commitlint/to-lines@20.0.0': {} + "@commitlint/to-lines@20.0.0": {} - '@commitlint/top-level@20.4.3': + "@commitlint/top-level@20.4.3": dependencies: escalade: 3.2.0 - '@commitlint/types@20.5.0': + "@commitlint/types@20.5.0": dependencies: conventional-commits-parser: 6.3.0 picocolors: 1.1.1 - '@conventional-changelog/git-client@2.6.0(conventional-commits-parser@6.3.0)': + "@conventional-changelog/git-client@2.6.0(conventional-commits-parser@6.3.0)": dependencies: - '@simple-libs/child-process-utils': 1.0.2 - '@simple-libs/stream-utils': 1.2.0 + "@simple-libs/child-process-utils": 1.0.2 + "@simple-libs/stream-utils": 1.2.0 semver: 7.7.4 optionalDependencies: conventional-commits-parser: 6.3.0 - '@csstools/color-helpers@6.0.2': {} + "@csstools/color-helpers@6.0.2": {} - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + "@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": dependencies: - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 - '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + "@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": dependencies: - '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + "@csstools/color-helpers": 6.0.2 + "@csstools/css-calc": 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 - '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + "@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)": dependencies: - '@csstools/css-tokenizer': 4.0.0 + "@csstools/css-tokenizer": 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)': + "@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)": optionalDependencies: css-tree: 3.2.1 - '@csstools/css-tokenizer@4.0.0': {} + "@csstools/css-tokenizer@4.0.0": {} - '@dnd-kit/accessibility@3.1.1(react@19.2.4)': + "@dnd-kit/accessibility@3.1.1(react@19.2.4)": dependencies: react: 19.2.4 tslib: 2.8.1 - '@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + "@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": dependencies: - '@dnd-kit/accessibility': 3.1.1(react@19.2.4) - '@dnd-kit/utilities': 3.2.2(react@19.2.4) + "@dnd-kit/accessibility": 3.1.1(react@19.2.4) + "@dnd-kit/utilities": 3.2.2(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) tslib: 2.8.1 - '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + "@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)": dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@dnd-kit/utilities': 3.2.2(react@19.2.4) + "@dnd-kit/core": 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + "@dnd-kit/utilities": 3.2.2(react@19.2.4) react: 19.2.4 tslib: 2.8.1 - '@dnd-kit/utilities@3.2.2(react@19.2.4)': + "@dnd-kit/utilities@3.2.2(react@19.2.4)": dependencies: react: 19.2.4 tslib: 2.8.1 - '@drizzle-team/brocli@0.10.2': {} + "@drizzle-team/brocli@0.10.2": {} - '@emnapi/core@1.9.1': + "@emnapi/core@1.9.1": dependencies: - '@emnapi/wasi-threads': 1.2.0 + "@emnapi/wasi-threads": 1.2.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.1': + "@emnapi/runtime@1.9.1": dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.0': + "@emnapi/wasi-threads@1.2.0": dependencies: tslib: 2.8.1 optional: true - '@epic-web/invariant@1.0.0': {} + "@epic-web/invariant@1.0.0": {} - '@esbuild-kit/core-utils@3.3.2': + "@esbuild-kit/core-utils@3.3.2": dependencies: esbuild: 0.27.4 source-map-support: 0.5.21 - '@esbuild-kit/esm-loader@2.6.5': + "@esbuild-kit/esm-loader@2.6.5": dependencies: - '@esbuild-kit/core-utils': 3.3.2 + "@esbuild-kit/core-utils": 3.3.2 get-tsconfig: 4.13.7 - '@esbuild/aix-ppc64@0.27.4': + "@esbuild/aix-ppc64@0.27.4": optional: true - '@esbuild/android-arm64@0.27.4': + "@esbuild/android-arm64@0.27.4": optional: true - '@esbuild/android-arm@0.27.4': + "@esbuild/android-arm@0.27.4": optional: true - '@esbuild/android-x64@0.27.4': + "@esbuild/android-x64@0.27.4": optional: true - '@esbuild/darwin-arm64@0.27.4': + "@esbuild/darwin-arm64@0.27.4": optional: true - '@esbuild/darwin-x64@0.27.4': + "@esbuild/darwin-x64@0.27.4": optional: true - '@esbuild/freebsd-arm64@0.27.4': + "@esbuild/freebsd-arm64@0.27.4": optional: true - '@esbuild/freebsd-x64@0.27.4': + "@esbuild/freebsd-x64@0.27.4": optional: true - '@esbuild/linux-arm64@0.27.4': + "@esbuild/linux-arm64@0.27.4": optional: true - '@esbuild/linux-arm@0.27.4': + "@esbuild/linux-arm@0.27.4": optional: true - '@esbuild/linux-ia32@0.27.4': + "@esbuild/linux-ia32@0.27.4": optional: true - '@esbuild/linux-loong64@0.27.4': + "@esbuild/linux-loong64@0.27.4": optional: true - '@esbuild/linux-mips64el@0.27.4': + "@esbuild/linux-mips64el@0.27.4": optional: true - '@esbuild/linux-ppc64@0.27.4': + "@esbuild/linux-ppc64@0.27.4": optional: true - '@esbuild/linux-riscv64@0.27.4': + "@esbuild/linux-riscv64@0.27.4": optional: true - '@esbuild/linux-s390x@0.27.4': + "@esbuild/linux-s390x@0.27.4": optional: true - '@esbuild/linux-x64@0.27.4': + "@esbuild/linux-x64@0.27.4": optional: true - '@esbuild/netbsd-arm64@0.27.4': + "@esbuild/netbsd-arm64@0.27.4": optional: true - '@esbuild/netbsd-x64@0.27.4': + "@esbuild/netbsd-x64@0.27.4": optional: true - '@esbuild/openbsd-arm64@0.27.4': + "@esbuild/openbsd-arm64@0.27.4": optional: true - '@esbuild/openbsd-x64@0.27.4': + "@esbuild/openbsd-x64@0.27.4": optional: true - '@esbuild/openharmony-arm64@0.27.4': + "@esbuild/openharmony-arm64@0.27.4": optional: true - '@esbuild/sunos-x64@0.27.4': + "@esbuild/sunos-x64@0.27.4": optional: true - '@esbuild/win32-arm64@0.27.4': + "@esbuild/win32-arm64@0.27.4": optional: true - '@esbuild/win32-ia32@0.27.4': + "@esbuild/win32-ia32@0.27.4": optional: true - '@esbuild/win32-x64@0.27.4': + "@esbuild/win32-x64@0.27.4": optional: true - '@exodus/bytes@1.15.0(@noble/hashes@2.0.1)': + "@exodus/bytes@1.15.0(@noble/hashes@2.0.1)": optionalDependencies: - '@noble/hashes': 2.0.1 + "@noble/hashes": 2.0.1 - '@formkit/auto-animate@0.9.0': {} + "@formkit/auto-animate@0.9.0": {} - '@hutson/parse-repository-url@3.0.2': {} + "@hutson/parse-repository-url@3.0.2": {} - '@img/colour@1.1.0': + "@img/colour@1.1.0": optional: true - '@img/sharp-darwin-arm64@0.34.5': + "@img/sharp-darwin-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + "@img/sharp-libvips-darwin-arm64": 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.5': + "@img/sharp-darwin-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + "@img/sharp-libvips-darwin-x64": 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + "@img/sharp-libvips-darwin-arm64@1.2.4": optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + "@img/sharp-libvips-darwin-x64@1.2.4": optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + "@img/sharp-libvips-linux-arm64@1.2.4": optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + "@img/sharp-libvips-linux-arm@1.2.4": optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + "@img/sharp-libvips-linux-ppc64@1.2.4": optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + "@img/sharp-libvips-linux-riscv64@1.2.4": optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + "@img/sharp-libvips-linux-s390x@1.2.4": optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + "@img/sharp-libvips-linux-x64@1.2.4": optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + "@img/sharp-libvips-linuxmusl-arm64@1.2.4": optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + "@img/sharp-libvips-linuxmusl-x64@1.2.4": optional: true - '@img/sharp-linux-arm64@0.34.5': + "@img/sharp-linux-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + "@img/sharp-libvips-linux-arm64": 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.5': + "@img/sharp-linux-arm@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + "@img/sharp-libvips-linux-arm": 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.5': + "@img/sharp-linux-ppc64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + "@img/sharp-libvips-linux-ppc64": 1.2.4 optional: true - '@img/sharp-linux-riscv64@0.34.5': + "@img/sharp-linux-riscv64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + "@img/sharp-libvips-linux-riscv64": 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.5': + "@img/sharp-linux-s390x@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + "@img/sharp-libvips-linux-s390x": 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.5': + "@img/sharp-linux-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + "@img/sharp-libvips-linux-x64": 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + "@img/sharp-linuxmusl-arm64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + "@img/sharp-linuxmusl-x64@0.34.5": optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 optional: true - '@img/sharp-wasm32@0.34.5': + "@img/sharp-wasm32@0.34.5": dependencies: - '@emnapi/runtime': 1.9.1 + "@emnapi/runtime": 1.9.1 optional: true - '@img/sharp-win32-arm64@0.34.5': + "@img/sharp-win32-arm64@0.34.5": optional: true - '@img/sharp-win32-ia32@0.34.5': + "@img/sharp-win32-ia32@0.34.5": optional: true - '@img/sharp-win32-x64@0.34.5': + "@img/sharp-win32-x64@0.34.5": optional: true - '@jridgewell/gen-mapping@0.3.13': + "@jridgewell/gen-mapping@0.3.13": dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + "@jridgewell/sourcemap-codec": 1.5.5 + "@jridgewell/trace-mapping": 0.3.31 - '@jridgewell/remapping@2.3.5': + "@jridgewell/remapping@2.3.5": dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + "@jridgewell/gen-mapping": 0.3.13 + "@jridgewell/trace-mapping": 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} + "@jridgewell/resolve-uri@3.1.2": {} - '@jridgewell/sourcemap-codec@1.5.5': {} + "@jridgewell/sourcemap-codec@1.5.5": {} - '@jridgewell/trace-mapping@0.3.31': + "@jridgewell/trace-mapping@0.3.31": dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/resolve-uri": 3.1.2 + "@jridgewell/sourcemap-codec": 1.5.5 - '@napi-rs/wasm-runtime@1.1.1': + "@napi-rs/wasm-runtime@1.1.1": dependencies: - '@emnapi/core': 1.9.1 - '@emnapi/runtime': 1.9.1 - '@tybys/wasm-util': 0.10.1 + "@emnapi/core": 1.9.1 + "@emnapi/runtime": 1.9.1 + "@tybys/wasm-util": 0.10.1 optional: true - '@next/env@16.2.1': {} + "@next/env@16.2.1": {} - '@next/swc-darwin-arm64@16.2.1': + "@next/swc-darwin-arm64@16.2.1": optional: true - '@next/swc-darwin-x64@16.2.1': + "@next/swc-darwin-x64@16.2.1": optional: true - '@next/swc-linux-arm64-gnu@16.2.1': + "@next/swc-linux-arm64-gnu@16.2.1": optional: true - '@next/swc-linux-arm64-musl@16.2.1': + "@next/swc-linux-arm64-musl@16.2.1": optional: true - '@next/swc-linux-x64-gnu@16.2.1': + "@next/swc-linux-x64-gnu@16.2.1": optional: true - '@next/swc-linux-x64-musl@16.2.1': + "@next/swc-linux-x64-musl@16.2.1": optional: true - '@next/swc-win32-arm64-msvc@16.2.1': + "@next/swc-win32-arm64-msvc@16.2.1": optional: true - '@next/swc-win32-x64-msvc@16.2.1': + "@next/swc-win32-x64-msvc@16.2.1": optional: true - '@noble/hashes@2.0.1': {} + "@noble/hashes@2.0.1": {} - '@nodelib/fs.scandir@2.1.5': + "@nodelib/fs.scandir@2.1.5": dependencies: - '@nodelib/fs.stat': 2.0.5 + "@nodelib/fs.stat": 2.0.5 run-parallel: 1.2.0 - '@nodelib/fs.stat@2.0.5': {} + "@nodelib/fs.stat@2.0.5": {} - '@nodelib/fs.walk@1.2.8': + "@nodelib/fs.walk@1.2.8": dependencies: - '@nodelib/fs.scandir': 2.1.5 + "@nodelib/fs.scandir": 2.1.5 fastq: 1.20.1 - '@oxc-parser/binding-android-arm-eabi@0.120.0': + "@oxc-parser/binding-android-arm-eabi@0.120.0": optional: true - '@oxc-parser/binding-android-arm64@0.120.0': + "@oxc-parser/binding-android-arm64@0.120.0": optional: true - '@oxc-parser/binding-darwin-arm64@0.120.0': + "@oxc-parser/binding-darwin-arm64@0.120.0": optional: true - '@oxc-parser/binding-darwin-x64@0.120.0': + "@oxc-parser/binding-darwin-x64@0.120.0": optional: true - '@oxc-parser/binding-freebsd-x64@0.120.0': + "@oxc-parser/binding-freebsd-x64@0.120.0": optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + "@oxc-parser/binding-linux-arm-gnueabihf@0.120.0": optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + "@oxc-parser/binding-linux-arm-musleabihf@0.120.0": optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + "@oxc-parser/binding-linux-arm64-gnu@0.120.0": optional: true - '@oxc-parser/binding-linux-arm64-musl@0.120.0': + "@oxc-parser/binding-linux-arm64-musl@0.120.0": optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + "@oxc-parser/binding-linux-ppc64-gnu@0.120.0": optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + "@oxc-parser/binding-linux-riscv64-gnu@0.120.0": optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + "@oxc-parser/binding-linux-riscv64-musl@0.120.0": optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + "@oxc-parser/binding-linux-s390x-gnu@0.120.0": optional: true - '@oxc-parser/binding-linux-x64-gnu@0.120.0': + "@oxc-parser/binding-linux-x64-gnu@0.120.0": optional: true - '@oxc-parser/binding-linux-x64-musl@0.120.0': + "@oxc-parser/binding-linux-x64-musl@0.120.0": optional: true - '@oxc-parser/binding-openharmony-arm64@0.120.0': + "@oxc-parser/binding-openharmony-arm64@0.120.0": optional: true - '@oxc-parser/binding-wasm32-wasi@0.120.0': + "@oxc-parser/binding-wasm32-wasi@0.120.0": dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + "@napi-rs/wasm-runtime": 1.1.1 optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + "@oxc-parser/binding-win32-arm64-msvc@0.120.0": optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + "@oxc-parser/binding-win32-ia32-msvc@0.120.0": optional: true - '@oxc-parser/binding-win32-x64-msvc@0.120.0': + "@oxc-parser/binding-win32-x64-msvc@0.120.0": optional: true - '@oxc-project/types@0.120.0': {} + "@oxc-project/types@0.120.0": {} - '@oxc-resolver/binding-android-arm-eabi@11.19.1': + "@oxc-resolver/binding-android-arm-eabi@11.19.1": optional: true - '@oxc-resolver/binding-android-arm64@11.19.1': + "@oxc-resolver/binding-android-arm64@11.19.1": optional: true - '@oxc-resolver/binding-darwin-arm64@11.19.1': + "@oxc-resolver/binding-darwin-arm64@11.19.1": optional: true - '@oxc-resolver/binding-darwin-x64@11.19.1': + "@oxc-resolver/binding-darwin-x64@11.19.1": optional: true - '@oxc-resolver/binding-freebsd-x64@11.19.1': + "@oxc-resolver/binding-freebsd-x64@11.19.1": optional: true - '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': + "@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1": optional: true - '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': + "@oxc-resolver/binding-linux-arm-musleabihf@11.19.1": optional: true - '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': + "@oxc-resolver/binding-linux-arm64-gnu@11.19.1": optional: true - '@oxc-resolver/binding-linux-arm64-musl@11.19.1': + "@oxc-resolver/binding-linux-arm64-musl@11.19.1": optional: true - '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': + "@oxc-resolver/binding-linux-ppc64-gnu@11.19.1": optional: true - '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': + "@oxc-resolver/binding-linux-riscv64-gnu@11.19.1": optional: true - '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': + "@oxc-resolver/binding-linux-riscv64-musl@11.19.1": optional: true - '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': + "@oxc-resolver/binding-linux-s390x-gnu@11.19.1": optional: true - '@oxc-resolver/binding-linux-x64-gnu@11.19.1': + "@oxc-resolver/binding-linux-x64-gnu@11.19.1": optional: true - '@oxc-resolver/binding-linux-x64-musl@11.19.1': + "@oxc-resolver/binding-linux-x64-musl@11.19.1": optional: true - '@oxc-resolver/binding-openharmony-arm64@11.19.1': + "@oxc-resolver/binding-openharmony-arm64@11.19.1": optional: true - '@oxc-resolver/binding-wasm32-wasi@11.19.1': + "@oxc-resolver/binding-wasm32-wasi@11.19.1": dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + "@napi-rs/wasm-runtime": 1.1.1 optional: true - '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': + "@oxc-resolver/binding-win32-arm64-msvc@11.19.1": optional: true - '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': + "@oxc-resolver/binding-win32-ia32-msvc@11.19.1": optional: true - '@oxc-resolver/binding-win32-x64-msvc@11.19.1': + "@oxc-resolver/binding-win32-x64-msvc@11.19.1": optional: true - '@phc/format@1.0.0': {} + "@phc/format@1.0.0": {} - '@pinojs/redact@0.4.0': {} + "@pinojs/redact@0.4.0": {} - '@rollup/rollup-android-arm-eabi@4.60.0': + "@rollup/rollup-android-arm-eabi@4.60.0": optional: true - '@rollup/rollup-android-arm64@4.60.0': + "@rollup/rollup-android-arm64@4.60.0": optional: true - '@rollup/rollup-darwin-arm64@4.60.0': + "@rollup/rollup-darwin-arm64@4.60.0": optional: true - '@rollup/rollup-darwin-x64@4.60.0': + "@rollup/rollup-darwin-x64@4.60.0": optional: true - '@rollup/rollup-freebsd-arm64@4.60.0': + "@rollup/rollup-freebsd-arm64@4.60.0": optional: true - '@rollup/rollup-freebsd-x64@4.60.0': + "@rollup/rollup-freebsd-x64@4.60.0": optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + "@rollup/rollup-linux-arm-gnueabihf@4.60.0": optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.0': + "@rollup/rollup-linux-arm-musleabihf@4.60.0": optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.0': + "@rollup/rollup-linux-arm64-gnu@4.60.0": optional: true - '@rollup/rollup-linux-arm64-musl@4.60.0': + "@rollup/rollup-linux-arm64-musl@4.60.0": optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.0': + "@rollup/rollup-linux-loong64-gnu@4.60.0": optional: true - '@rollup/rollup-linux-loong64-musl@4.60.0': + "@rollup/rollup-linux-loong64-musl@4.60.0": optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.0': + "@rollup/rollup-linux-ppc64-gnu@4.60.0": optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.0': + "@rollup/rollup-linux-ppc64-musl@4.60.0": optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.0': + "@rollup/rollup-linux-riscv64-gnu@4.60.0": optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.0': + "@rollup/rollup-linux-riscv64-musl@4.60.0": optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.0': + "@rollup/rollup-linux-s390x-gnu@4.60.0": optional: true - '@rollup/rollup-linux-x64-gnu@4.60.0': + "@rollup/rollup-linux-x64-gnu@4.60.0": optional: true - '@rollup/rollup-linux-x64-musl@4.60.0': + "@rollup/rollup-linux-x64-musl@4.60.0": optional: true - '@rollup/rollup-openbsd-x64@4.60.0': + "@rollup/rollup-openbsd-x64@4.60.0": optional: true - '@rollup/rollup-openharmony-arm64@4.60.0': + "@rollup/rollup-openharmony-arm64@4.60.0": optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.0': + "@rollup/rollup-win32-arm64-msvc@4.60.0": optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.0': + "@rollup/rollup-win32-ia32-msvc@4.60.0": optional: true - '@rollup/rollup-win32-x64-gnu@4.60.0': + "@rollup/rollup-win32-x64-gnu@4.60.0": optional: true - '@rollup/rollup-win32-x64-msvc@4.60.0': + "@rollup/rollup-win32-x64-msvc@4.60.0": optional: true - '@simple-libs/child-process-utils@1.0.2': + "@simple-libs/child-process-utils@1.0.2": dependencies: - '@simple-libs/stream-utils': 1.2.0 + "@simple-libs/stream-utils": 1.2.0 - '@simple-libs/stream-utils@1.2.0': {} + "@simple-libs/stream-utils@1.2.0": {} - '@standard-schema/spec@1.1.0': {} + "@standard-schema/spec@1.1.0": {} - '@swc/helpers@0.5.15': + "@swc/helpers@0.5.15": dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.2.2': + "@tailwindcss/node@4.2.2": dependencies: - '@jridgewell/remapping': 2.3.5 + "@jridgewell/remapping": 2.3.5 enhanced-resolve: 5.20.1 jiti: 2.6.1 lightningcss: 1.32.0 @@ -4221,207 +6426,207 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 - '@tailwindcss/oxide-android-arm64@4.2.2': + "@tailwindcss/oxide-android-arm64@4.2.2": optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.2': + "@tailwindcss/oxide-darwin-arm64@4.2.2": optional: true - '@tailwindcss/oxide-darwin-x64@4.2.2': + "@tailwindcss/oxide-darwin-x64@4.2.2": optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.2': + "@tailwindcss/oxide-freebsd-x64@4.2.2": optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + "@tailwindcss/oxide-linux-arm64-musl@4.2.2": optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + "@tailwindcss/oxide-linux-x64-gnu@4.2.2": optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.2': + "@tailwindcss/oxide-linux-x64-musl@4.2.2": optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.2': + "@tailwindcss/oxide-wasm32-wasi@4.2.2": optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + "@tailwindcss/oxide-win32-x64-msvc@4.2.2": optional: true - '@tailwindcss/oxide@4.2.2': + "@tailwindcss/oxide@4.2.2": optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.2 - '@tailwindcss/oxide-darwin-arm64': 4.2.2 - '@tailwindcss/oxide-darwin-x64': 4.2.2 - '@tailwindcss/oxide-freebsd-x64': 4.2.2 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 - '@tailwindcss/oxide-linux-x64-musl': 4.2.2 - '@tailwindcss/oxide-wasm32-wasi': 4.2.2 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - - '@tailwindcss/postcss@4.2.2': - dependencies: - '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.2.2 - '@tailwindcss/oxide': 4.2.2 + "@tailwindcss/oxide-android-arm64": 4.2.2 + "@tailwindcss/oxide-darwin-arm64": 4.2.2 + "@tailwindcss/oxide-darwin-x64": 4.2.2 + "@tailwindcss/oxide-freebsd-x64": 4.2.2 + "@tailwindcss/oxide-linux-arm-gnueabihf": 4.2.2 + "@tailwindcss/oxide-linux-arm64-gnu": 4.2.2 + "@tailwindcss/oxide-linux-arm64-musl": 4.2.2 + "@tailwindcss/oxide-linux-x64-gnu": 4.2.2 + "@tailwindcss/oxide-linux-x64-musl": 4.2.2 + "@tailwindcss/oxide-wasm32-wasi": 4.2.2 + "@tailwindcss/oxide-win32-arm64-msvc": 4.2.2 + "@tailwindcss/oxide-win32-x64-msvc": 4.2.2 + + "@tailwindcss/postcss@4.2.2": + dependencies: + "@alloc/quick-lru": 5.2.0 + "@tailwindcss/node": 4.2.2 + "@tailwindcss/oxide": 4.2.2 postcss: 8.5.8 tailwindcss: 4.2.2 - '@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)': + "@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)": dependencies: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - '@tanstack/query-core@5.95.2': {} + "@tanstack/query-core@5.95.2": {} - '@tanstack/react-query@5.95.2(react@19.2.4)': + "@tanstack/react-query@5.95.2(react@19.2.4)": dependencies: - '@tanstack/query-core': 5.95.2 + "@tanstack/query-core": 5.95.2 react: 19.2.4 - '@testing-library/dom@10.4.1': + "@testing-library/dom@10.4.1": dependencies: - '@babel/code-frame': 7.29.0 - '@babel/runtime': 7.29.2 - '@types/aria-query': 5.0.4 + "@babel/code-frame": 7.29.0 + "@babel/runtime": 7.29.2 + "@types/aria-query": 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.9.1': + "@testing-library/jest-dom@6.9.1": dependencies: - '@adobe/css-tools': 4.4.4 + "@adobe/css-tools": 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": dependencies: - '@babel/runtime': 7.29.2 - '@testing-library/dom': 10.4.1 + "@babel/runtime": 7.29.2 + "@testing-library/dom": 10.4.1 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + "@types/react": 19.2.14 + "@types/react-dom": 19.2.3(@types/react@19.2.14) - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + "@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)": dependencies: - '@testing-library/dom': 10.4.1 + "@testing-library/dom": 10.4.1 - '@tybys/wasm-util@0.10.1': + "@tybys/wasm-util@0.10.1": dependencies: tslib: 2.8.1 optional: true - '@types/aria-query@5.0.4': {} + "@types/aria-query@5.0.4": {} - '@types/chai@5.2.3': + "@types/chai@5.2.3": dependencies: - '@types/deep-eql': 4.0.2 + "@types/deep-eql": 4.0.2 assertion-error: 2.0.1 - '@types/debug@4.1.12': + "@types/debug@4.1.12": dependencies: - '@types/ms': 2.1.0 + "@types/ms": 2.1.0 - '@types/deep-eql@4.0.2': {} + "@types/deep-eql@4.0.2": {} - '@types/estree-jsx@1.0.5': + "@types/estree-jsx@1.0.5": dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 - '@types/estree@1.0.8': {} + "@types/estree@1.0.8": {} - '@types/hast@3.0.4': + "@types/hast@3.0.4": dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 - '@types/mdast@4.0.4': + "@types/mdast@4.0.4": dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 - '@types/minimist@1.2.5': {} + "@types/minimist@1.2.5": {} - '@types/ms@2.1.0': {} + "@types/ms@2.1.0": {} - '@types/node-cron@3.0.11': {} + "@types/node-cron@3.0.11": {} - '@types/node@25.5.0': + "@types/node@25.5.0": dependencies: undici-types: 7.18.2 - '@types/normalize-package-data@2.4.4': {} + "@types/normalize-package-data@2.4.4": {} - '@types/react-dom@19.2.3(@types/react@19.2.14)': + "@types/react-dom@19.2.3(@types/react@19.2.14)": dependencies: - '@types/react': 19.2.14 + "@types/react": 19.2.14 - '@types/react@19.2.14': + "@types/react@19.2.14": dependencies: csstype: 3.2.3 - '@types/unist@2.0.11': {} + "@types/unist@2.0.11": {} - '@types/unist@3.0.3': {} + "@types/unist@3.0.3": {} - '@ungap/structured-clone@1.3.0': {} + "@ungap/structured-clone@1.3.0": {} - '@vitest/expect@4.1.1': + "@vitest/expect@4.1.1": dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.1 - '@vitest/utils': 4.1.1 + "@standard-schema/spec": 1.1.0 + "@types/chai": 5.2.3 + "@vitest/spy": 4.1.1 + "@vitest/utils": 4.1.1 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': + "@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))": dependencies: - '@vitest/spy': 4.1.1 + "@vitest/spy": 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.1.1': + "@vitest/pretty-format@4.1.1": dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.1': + "@vitest/runner@4.1.1": dependencies: - '@vitest/utils': 4.1.1 + "@vitest/utils": 4.1.1 pathe: 2.0.3 - '@vitest/snapshot@4.1.1': + "@vitest/snapshot@4.1.1": dependencies: - '@vitest/pretty-format': 4.1.1 - '@vitest/utils': 4.1.1 + "@vitest/pretty-format": 4.1.1 + "@vitest/utils": 4.1.1 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.1': {} + "@vitest/spy@4.1.1": {} - '@vitest/utils@4.1.1': + "@vitest/utils@4.1.1": dependencies: - '@vitest/pretty-format': 4.1.1 + "@vitest/pretty-format": 4.1.1 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4455,7 +6660,7 @@ snapshots: argon2@0.44.0: dependencies: - '@phc/format': 1.0.0 + "@phc/format": 1.0.0 cross-env: 10.1.0 node-addon-api: 8.6.0 node-gyp-build: 4.8.4 @@ -4685,7 +6890,7 @@ snapshots: conventional-commits-parser@6.3.0: dependencies: - '@simple-libs/stream-utils': 1.2.0 + "@simple-libs/stream-utils": 1.2.0 meow: 13.2.0 conventional-recommended-bump@7.0.1: @@ -4704,7 +6909,7 @@ snapshots: cosmiconfig-typescript-loader@6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2): dependencies: - '@types/node': 25.5.0 + "@types/node": 25.5.0 cosmiconfig: 9.0.1(typescript@6.0.2) jiti: 2.6.1 typescript: 6.0.2 @@ -4720,7 +6925,7 @@ snapshots: cross-env@10.1.0: dependencies: - '@epic-web/invariant': 1.0.0 + "@epic-web/invariant": 1.0.0 cross-spawn: 7.0.6 cross-spawn@7.0.6: @@ -4747,7 +6952,7 @@ snapshots: whatwg-mimetype: 5.0.0 whatwg-url: 16.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - - '@noble/hashes' + - "@noble/hashes" dateformat@3.0.3: {} @@ -4799,8 +7004,8 @@ snapshots: drizzle-kit@0.31.10: dependencies: - '@drizzle-team/brocli': 0.10.2 - '@esbuild-kit/esm-loader': 2.6.5 + "@drizzle-team/brocli": 0.10.2 + "@esbuild-kit/esm-loader": 2.6.5 esbuild: 0.27.4 tsx: 4.21.0 @@ -4854,32 +7059,32 @@ snapshots: esbuild@0.27.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.4 - '@esbuild/android-arm': 0.27.4 - '@esbuild/android-arm64': 0.27.4 - '@esbuild/android-x64': 0.27.4 - '@esbuild/darwin-arm64': 0.27.4 - '@esbuild/darwin-x64': 0.27.4 - '@esbuild/freebsd-arm64': 0.27.4 - '@esbuild/freebsd-x64': 0.27.4 - '@esbuild/linux-arm': 0.27.4 - '@esbuild/linux-arm64': 0.27.4 - '@esbuild/linux-ia32': 0.27.4 - '@esbuild/linux-loong64': 0.27.4 - '@esbuild/linux-mips64el': 0.27.4 - '@esbuild/linux-ppc64': 0.27.4 - '@esbuild/linux-riscv64': 0.27.4 - '@esbuild/linux-s390x': 0.27.4 - '@esbuild/linux-x64': 0.27.4 - '@esbuild/netbsd-arm64': 0.27.4 - '@esbuild/netbsd-x64': 0.27.4 - '@esbuild/openbsd-arm64': 0.27.4 - '@esbuild/openbsd-x64': 0.27.4 - '@esbuild/openharmony-arm64': 0.27.4 - '@esbuild/sunos-x64': 0.27.4 - '@esbuild/win32-arm64': 0.27.4 - '@esbuild/win32-ia32': 0.27.4 - '@esbuild/win32-x64': 0.27.4 + "@esbuild/aix-ppc64": 0.27.4 + "@esbuild/android-arm": 0.27.4 + "@esbuild/android-arm64": 0.27.4 + "@esbuild/android-x64": 0.27.4 + "@esbuild/darwin-arm64": 0.27.4 + "@esbuild/darwin-x64": 0.27.4 + "@esbuild/freebsd-arm64": 0.27.4 + "@esbuild/freebsd-x64": 0.27.4 + "@esbuild/linux-arm": 0.27.4 + "@esbuild/linux-arm64": 0.27.4 + "@esbuild/linux-ia32": 0.27.4 + "@esbuild/linux-loong64": 0.27.4 + "@esbuild/linux-mips64el": 0.27.4 + "@esbuild/linux-ppc64": 0.27.4 + "@esbuild/linux-riscv64": 0.27.4 + "@esbuild/linux-s390x": 0.27.4 + "@esbuild/linux-x64": 0.27.4 + "@esbuild/netbsd-arm64": 0.27.4 + "@esbuild/netbsd-x64": 0.27.4 + "@esbuild/openbsd-arm64": 0.27.4 + "@esbuild/openbsd-x64": 0.27.4 + "@esbuild/openharmony-arm64": 0.27.4 + "@esbuild/sunos-x64": 0.27.4 + "@esbuild/win32-arm64": 0.27.4 + "@esbuild/win32-ia32": 0.27.4 + "@esbuild/win32-x64": 0.27.4 escalade@3.2.0: {} @@ -4891,7 +7096,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 expect-type@1.3.0: {} @@ -4903,8 +7108,8 @@ snapshots: fast-glob@3.3.3: dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 + "@nodelib/fs.stat": 2.0.5 + "@nodelib/fs.walk": 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.8 @@ -4976,7 +7181,7 @@ snapshots: get-pkg-repo@4.2.1: dependencies: - '@hutson/parse-repository-url': 3.0.2 + "@hutson/parse-repository-url": 3.0.2 hosted-git-info: 4.1.0 through2: 2.0.5 yargs: 16.2.0 @@ -4997,7 +7202,7 @@ snapshots: git-raw-commits@5.0.1(conventional-commits-parser@6.3.0): dependencies: - '@conventional-changelog/git-client': 2.6.0(conventional-commits-parser@6.3.0) + "@conventional-changelog/git-client": 2.6.0(conventional-commits-parser@6.3.0) meow: 13.2.0 transitivePeerDependencies: - conventional-commits-filter @@ -5046,9 +7251,9 @@ snapshots: hast-util-to-jsx-runtime@2.3.6: dependencies: - '@types/estree': 1.0.8 - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 + "@types/estree": 1.0.8 + "@types/hast": 3.0.4 + "@types/unist": 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -5066,7 +7271,7 @@ snapshots: hast-util-whitespace@3.0.0: dependencies: - '@types/hast': 3.0.4 + "@types/hast": 3.0.4 help-me@5.0.0: {} @@ -5078,9 +7283,9 @@ snapshots: html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) transitivePeerDependencies: - - '@noble/hashes' + - "@noble/hashes" html-url-attributes@3.0.1: {} @@ -5169,11 +7374,11 @@ snapshots: jsdom@29.0.1(@noble/hashes@2.0.1): dependencies: - '@asamuzakjp/css-color': 5.0.1 - '@asamuzakjp/dom-selector': 7.0.4 - '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + "@asamuzakjp/css-color": 5.0.1 + "@asamuzakjp/dom-selector": 7.0.4 + "@bramus/specificity": 2.4.2 + "@csstools/css-syntax-patches-for-csstree": 1.1.1(css-tree@3.2.1) + "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.0.1) decimal.js: 10.6.0 @@ -5191,7 +7396,7 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.0.1) xml-name-validator: 5.0.0 transitivePeerDependencies: - - '@noble/hashes' + - "@noble/hashes" json-parse-better-errors@1.0.2: {} @@ -5207,7 +7412,7 @@ snapshots: knip@6.0.5: dependencies: - '@nodelib/fs.walk': 1.2.8 + "@nodelib/fs.walk": 1.2.8 fast-glob: 3.3.3 formatly: 0.3.0 get-tsconfig: 4.13.7 @@ -5325,7 +7530,7 @@ snapshots: magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + "@jridgewell/sourcemap-codec": 1.5.5 map-obj@1.0.1: {} @@ -5335,15 +7540,15 @@ snapshots: mdast-util-find-and-replace@3.0.2: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 mdast-util-from-markdown@2.0.3: dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 @@ -5359,7 +7564,7 @@ snapshots: mdast-util-gfm-autolink-literal@2.0.1: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.2 @@ -5367,7 +7572,7 @@ snapshots: mdast-util-gfm-footnote@2.1.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -5377,7 +7582,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: @@ -5385,7 +7590,7 @@ snapshots: mdast-util-gfm-table@2.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 mdast-util-from-markdown: 2.0.3 @@ -5395,7 +7600,7 @@ snapshots: mdast-util-gfm-task-list-item@2.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -5416,9 +7621,9 @@ snapshots: mdast-util-mdx-expression@2.0.1: dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -5427,10 +7632,10 @@ snapshots: mdast-util-mdx-jsx@3.2.0: dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 ccount: 2.0.1 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -5444,9 +7649,9 @@ snapshots: mdast-util-mdxjs-esm@2.0.1: dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 + "@types/estree-jsx": 1.0.5 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -5455,14 +7660,14 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 unist-util-is: 6.0.1 mdast-util-to-hast@13.2.1: dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@ungap/structured-clone": 1.3.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -5472,8 +7677,8 @@ snapshots: mdast-util-to-markdown@2.1.2: dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 + "@types/mdast": 4.0.4 + "@types/unist": 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -5484,7 +7689,7 @@ snapshots: mdast-util-to-string@4.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 mdn-data@2.27.1: {} @@ -5492,7 +7697,7 @@ snapshots: meow@8.1.2: dependencies: - '@types/minimist': 1.2.5 + "@types/minimist": 1.2.5 camelcase-keys: 6.2.2 decamelize-keys: 1.1.1 hard-rejection: 2.1.0 @@ -5677,7 +7882,7 @@ snapshots: micromark@4.0.2: dependencies: - '@types/debug': 4.1.12 + "@types/debug": 4.1.12 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -5726,8 +7931,8 @@ snapshots: next@16.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - '@next/env': 16.2.1 - '@swc/helpers': 0.5.15 + "@next/env": 16.2.1 + "@swc/helpers": 0.5.15 baseline-browser-mapping: 2.10.9 caniuse-lite: 1.0.30001780 postcss: 8.4.31 @@ -5735,17 +7940,17 @@ snapshots: react-dom: 19.2.4(react@19.2.4) styled-jsx: 5.1.6(react@19.2.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.1 - '@next/swc-darwin-x64': 16.2.1 - '@next/swc-linux-arm64-gnu': 16.2.1 - '@next/swc-linux-arm64-musl': 16.2.1 - '@next/swc-linux-x64-gnu': 16.2.1 - '@next/swc-linux-x64-musl': 16.2.1 - '@next/swc-win32-arm64-msvc': 16.2.1 - '@next/swc-win32-x64-msvc': 16.2.1 + "@next/swc-darwin-arm64": 16.2.1 + "@next/swc-darwin-x64": 16.2.1 + "@next/swc-linux-arm64-gnu": 16.2.1 + "@next/swc-linux-arm64-musl": 16.2.1 + "@next/swc-linux-x64-gnu": 16.2.1 + "@next/swc-linux-x64-musl": 16.2.1 + "@next/swc-win32-arm64-msvc": 16.2.1 + "@next/swc-win32-x64-msvc": 16.2.1 sharp: 0.34.5 transitivePeerDependencies: - - '@babel/core' + - "@babel/core" - babel-plugin-macros node-addon-api@8.6.0: {} @@ -5778,55 +7983,55 @@ snapshots: otpauth@9.5.0: dependencies: - '@noble/hashes': 2.0.1 + "@noble/hashes": 2.0.1 oxc-parser@0.120.0: dependencies: - '@oxc-project/types': 0.120.0 + "@oxc-project/types": 0.120.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.120.0 - '@oxc-parser/binding-android-arm64': 0.120.0 - '@oxc-parser/binding-darwin-arm64': 0.120.0 - '@oxc-parser/binding-darwin-x64': 0.120.0 - '@oxc-parser/binding-freebsd-x64': 0.120.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 - '@oxc-parser/binding-linux-arm64-musl': 0.120.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-gnu': 0.120.0 - '@oxc-parser/binding-linux-x64-musl': 0.120.0 - '@oxc-parser/binding-openharmony-arm64': 0.120.0 - '@oxc-parser/binding-wasm32-wasi': 0.120.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 - '@oxc-parser/binding-win32-x64-msvc': 0.120.0 + "@oxc-parser/binding-android-arm-eabi": 0.120.0 + "@oxc-parser/binding-android-arm64": 0.120.0 + "@oxc-parser/binding-darwin-arm64": 0.120.0 + "@oxc-parser/binding-darwin-x64": 0.120.0 + "@oxc-parser/binding-freebsd-x64": 0.120.0 + "@oxc-parser/binding-linux-arm-gnueabihf": 0.120.0 + "@oxc-parser/binding-linux-arm-musleabihf": 0.120.0 + "@oxc-parser/binding-linux-arm64-gnu": 0.120.0 + "@oxc-parser/binding-linux-arm64-musl": 0.120.0 + "@oxc-parser/binding-linux-ppc64-gnu": 0.120.0 + "@oxc-parser/binding-linux-riscv64-gnu": 0.120.0 + "@oxc-parser/binding-linux-riscv64-musl": 0.120.0 + "@oxc-parser/binding-linux-s390x-gnu": 0.120.0 + "@oxc-parser/binding-linux-x64-gnu": 0.120.0 + "@oxc-parser/binding-linux-x64-musl": 0.120.0 + "@oxc-parser/binding-openharmony-arm64": 0.120.0 + "@oxc-parser/binding-wasm32-wasi": 0.120.0 + "@oxc-parser/binding-win32-arm64-msvc": 0.120.0 + "@oxc-parser/binding-win32-ia32-msvc": 0.120.0 + "@oxc-parser/binding-win32-x64-msvc": 0.120.0 oxc-resolver@11.19.1: optionalDependencies: - '@oxc-resolver/binding-android-arm-eabi': 11.19.1 - '@oxc-resolver/binding-android-arm64': 11.19.1 - '@oxc-resolver/binding-darwin-arm64': 11.19.1 - '@oxc-resolver/binding-darwin-x64': 11.19.1 - '@oxc-resolver/binding-freebsd-x64': 11.19.1 - '@oxc-resolver/binding-linux-arm-gnueabihf': 11.19.1 - '@oxc-resolver/binding-linux-arm-musleabihf': 11.19.1 - '@oxc-resolver/binding-linux-arm64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-arm64-musl': 11.19.1 - '@oxc-resolver/binding-linux-ppc64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-riscv64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-riscv64-musl': 11.19.1 - '@oxc-resolver/binding-linux-s390x-gnu': 11.19.1 - '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 - '@oxc-resolver/binding-linux-x64-musl': 11.19.1 - '@oxc-resolver/binding-openharmony-arm64': 11.19.1 - '@oxc-resolver/binding-wasm32-wasi': 11.19.1 - '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 - '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 - '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 + "@oxc-resolver/binding-android-arm-eabi": 11.19.1 + "@oxc-resolver/binding-android-arm64": 11.19.1 + "@oxc-resolver/binding-darwin-arm64": 11.19.1 + "@oxc-resolver/binding-darwin-x64": 11.19.1 + "@oxc-resolver/binding-freebsd-x64": 11.19.1 + "@oxc-resolver/binding-linux-arm-gnueabihf": 11.19.1 + "@oxc-resolver/binding-linux-arm-musleabihf": 11.19.1 + "@oxc-resolver/binding-linux-arm64-gnu": 11.19.1 + "@oxc-resolver/binding-linux-arm64-musl": 11.19.1 + "@oxc-resolver/binding-linux-ppc64-gnu": 11.19.1 + "@oxc-resolver/binding-linux-riscv64-gnu": 11.19.1 + "@oxc-resolver/binding-linux-riscv64-musl": 11.19.1 + "@oxc-resolver/binding-linux-s390x-gnu": 11.19.1 + "@oxc-resolver/binding-linux-x64-gnu": 11.19.1 + "@oxc-resolver/binding-linux-x64-musl": 11.19.1 + "@oxc-resolver/binding-openharmony-arm64": 11.19.1 + "@oxc-resolver/binding-wasm32-wasi": 11.19.1 + "@oxc-resolver/binding-win32-arm64-msvc": 11.19.1 + "@oxc-resolver/binding-win32-ia32-msvc": 11.19.1 + "@oxc-resolver/binding-win32-x64-msvc": 11.19.1 p-limit@1.3.0: dependencies: @@ -5866,7 +8071,7 @@ snapshots: parse-entities@4.0.2: dependencies: - '@types/unist': 2.0.11 + "@types/unist": 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.3.0 @@ -5881,7 +8086,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + "@babel/code-frame": 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -5942,7 +8147,7 @@ snapshots: pino@10.3.1: dependencies: - '@pinojs/redact': 0.4.0 + "@pinojs/redact": 0.4.0 atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 3.0.0 @@ -6018,9 +8223,9 @@ snapshots: react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/react': 19.2.14 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 + "@types/react": 19.2.14 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -6055,7 +8260,7 @@ snapshots: read-pkg@5.2.0: dependencies: - '@types/normalize-package-data': 2.4.4 + "@types/normalize-package-data": 2.4.4 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 @@ -6085,7 +8290,7 @@ snapshots: remark-gfm@4.0.1: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 mdast-util-gfm: 3.1.0 micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 @@ -6096,7 +8301,7 @@ snapshots: remark-parse@11.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 mdast-util-from-markdown: 2.0.3 micromark-util-types: 2.0.2 unified: 11.0.5 @@ -6105,15 +8310,15 @@ snapshots: remark-rehype@11.1.2: dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 + "@types/hast": 3.0.4 + "@types/mdast": 4.0.4 mdast-util-to-hast: 13.2.1 unified: 11.0.5 vfile: 6.0.3 remark-stringify@11.0.0: dependencies: - '@types/mdast': 4.0.4 + "@types/mdast": 4.0.4 mdast-util-to-markdown: 2.1.2 unified: 11.0.5 @@ -6137,33 +8342,33 @@ snapshots: rollup@4.60.0: dependencies: - '@types/estree': 1.0.8 + "@types/estree": 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.0 - '@rollup/rollup-android-arm64': 4.60.0 - '@rollup/rollup-darwin-arm64': 4.60.0 - '@rollup/rollup-darwin-x64': 4.60.0 - '@rollup/rollup-freebsd-arm64': 4.60.0 - '@rollup/rollup-freebsd-x64': 4.60.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 - '@rollup/rollup-linux-arm-musleabihf': 4.60.0 - '@rollup/rollup-linux-arm64-gnu': 4.60.0 - '@rollup/rollup-linux-arm64-musl': 4.60.0 - '@rollup/rollup-linux-loong64-gnu': 4.60.0 - '@rollup/rollup-linux-loong64-musl': 4.60.0 - '@rollup/rollup-linux-ppc64-gnu': 4.60.0 - '@rollup/rollup-linux-ppc64-musl': 4.60.0 - '@rollup/rollup-linux-riscv64-gnu': 4.60.0 - '@rollup/rollup-linux-riscv64-musl': 4.60.0 - '@rollup/rollup-linux-s390x-gnu': 4.60.0 - '@rollup/rollup-linux-x64-gnu': 4.60.0 - '@rollup/rollup-linux-x64-musl': 4.60.0 - '@rollup/rollup-openbsd-x64': 4.60.0 - '@rollup/rollup-openharmony-arm64': 4.60.0 - '@rollup/rollup-win32-arm64-msvc': 4.60.0 - '@rollup/rollup-win32-ia32-msvc': 4.60.0 - '@rollup/rollup-win32-x64-gnu': 4.60.0 - '@rollup/rollup-win32-x64-msvc': 4.60.0 + "@rollup/rollup-android-arm-eabi": 4.60.0 + "@rollup/rollup-android-arm64": 4.60.0 + "@rollup/rollup-darwin-arm64": 4.60.0 + "@rollup/rollup-darwin-x64": 4.60.0 + "@rollup/rollup-freebsd-arm64": 4.60.0 + "@rollup/rollup-freebsd-x64": 4.60.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.60.0 + "@rollup/rollup-linux-arm-musleabihf": 4.60.0 + "@rollup/rollup-linux-arm64-gnu": 4.60.0 + "@rollup/rollup-linux-arm64-musl": 4.60.0 + "@rollup/rollup-linux-loong64-gnu": 4.60.0 + "@rollup/rollup-linux-loong64-musl": 4.60.0 + "@rollup/rollup-linux-ppc64-gnu": 4.60.0 + "@rollup/rollup-linux-ppc64-musl": 4.60.0 + "@rollup/rollup-linux-riscv64-gnu": 4.60.0 + "@rollup/rollup-linux-riscv64-musl": 4.60.0 + "@rollup/rollup-linux-s390x-gnu": 4.60.0 + "@rollup/rollup-linux-x64-gnu": 4.60.0 + "@rollup/rollup-linux-x64-musl": 4.60.0 + "@rollup/rollup-openbsd-x64": 4.60.0 + "@rollup/rollup-openharmony-arm64": 4.60.0 + "@rollup/rollup-win32-arm64-msvc": 4.60.0 + "@rollup/rollup-win32-ia32-msvc": 4.60.0 + "@rollup/rollup-win32-x64-gnu": 4.60.0 + "@rollup/rollup-win32-x64-msvc": 4.60.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -6190,34 +8395,34 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.1.0 + "@img/colour": 1.1.0 detect-libc: 2.1.2 semver: 7.7.4 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + "@img/sharp-darwin-arm64": 0.34.5 + "@img/sharp-darwin-x64": 0.34.5 + "@img/sharp-libvips-darwin-arm64": 1.2.4 + "@img/sharp-libvips-darwin-x64": 1.2.4 + "@img/sharp-libvips-linux-arm": 1.2.4 + "@img/sharp-libvips-linux-arm64": 1.2.4 + "@img/sharp-libvips-linux-ppc64": 1.2.4 + "@img/sharp-libvips-linux-riscv64": 1.2.4 + "@img/sharp-libvips-linux-s390x": 1.2.4 + "@img/sharp-libvips-linux-x64": 1.2.4 + "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + "@img/sharp-linux-arm": 0.34.5 + "@img/sharp-linux-arm64": 0.34.5 + "@img/sharp-linux-ppc64": 0.34.5 + "@img/sharp-linux-riscv64": 0.34.5 + "@img/sharp-linux-s390x": 0.34.5 + "@img/sharp-linux-x64": 0.34.5 + "@img/sharp-linuxmusl-arm64": 0.34.5 + "@img/sharp-linuxmusl-x64": 0.34.5 + "@img/sharp-wasm32": 0.34.5 + "@img/sharp-win32-arm64": 0.34.5 + "@img/sharp-win32-ia32": 0.34.5 + "@img/sharp-win32-x64": 0.34.5 optional: true shebang-command@2.0.0: @@ -6428,7 +8633,7 @@ snapshots: unified@11.0.5: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 @@ -6438,24 +8643,24 @@ snapshots: unist-util-is@6.0.1: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-position@5.0.0: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-stringify-position@4.0.0: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-visit-parents@6.0.2: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-is: 6.0.1 unist-util-visit@5.1.0: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 @@ -6468,12 +8673,12 @@ snapshots: vfile-message@4.0.3: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 unist-util-stringify-position: 4.0.0 vfile@6.0.3: dependencies: - '@types/unist': 3.0.3 + "@types/unist": 3.0.3 vfile-message: 4.0.3 vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): @@ -6485,7 +8690,7 @@ snapshots: rollup: 4.60.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.5.0 + "@types/node": 25.5.0 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 @@ -6494,13 +8699,13 @@ snapshots: vitest@4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.1 - '@vitest/runner': 4.1.1 - '@vitest/snapshot': 4.1.1 - '@vitest/spy': 4.1.1 - '@vitest/utils': 4.1.1 + "@vitest/expect": 4.1.1 + "@vitest/mocker": 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + "@vitest/pretty-format": 4.1.1 + "@vitest/runner": 4.1.1 + "@vitest/snapshot": 4.1.1 + "@vitest/spy": 4.1.1 + "@vitest/utils": 4.1.1 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -6515,7 +8720,7 @@ snapshots: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.5.0 + "@types/node": 25.5.0 jsdom: 29.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - msw @@ -6532,11 +8737,11 @@ snapshots: whatwg-url@16.0.1(@noble/hashes@2.0.1): dependencies: - '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) + "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - - '@noble/hashes' + - "@noble/hashes" which@2.0.2: dependencies: diff --git a/src/app/api/clients/[id]/torrents/route.ts b/src/app/api/clients/[id]/torrents/route.ts index c1c26b7b..6c37767c 100644 --- a/src/app/api/clients/[id]/torrents/route.ts +++ b/src/app/api/clients/[id]/torrents/route.ts @@ -47,7 +47,7 @@ export async function GET(request: Request, props: { params: Promise<{ id: strin { route: "GET /api/clients/[id]/torrents", clientId }, "torrent fetch failed — stale session key" ) - return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) + return NextResponse.json({ error: "Session expired. Please log in again" }, { status: 401 }) } log.error( { route: "GET /api/clients/[id]/torrents", clientId }, diff --git a/src/components/dashboard/TodayAtAGlanceSkeleton.tsx b/src/components/dashboard/TodayAtAGlanceSkeleton.tsx index dd1ebf3f..be4b966a 100644 --- a/src/components/dashboard/TodayAtAGlanceSkeleton.tsx +++ b/src/components/dashboard/TodayAtAGlanceSkeleton.tsx @@ -15,7 +15,7 @@ export function TodayAtAGlanceSkeleton() { return (
- {/* Fleet headline — 5 stat card placeholders */} + {/* Fleet headline */}
{STAT_KEYS.map((key) => (
@@ -27,7 +27,7 @@ export function TodayAtAGlanceSkeleton() {
- {/* Tracker breakdowns — two panels */} + {/* Tracker breakdowns */}
{PANEL_KEYS.map((key) => (
diff --git a/src/components/ui/ProgressBar.tsx b/src/components/ui/ProgressBar.tsx index be690396..bc67cefe 100644 --- a/src/components/ui/ProgressBar.tsx +++ b/src/components/ui/ProgressBar.tsx @@ -1,9 +1,4 @@ // src/components/ui/ProgressBar.tsx -// -// Functions: ProgressBar -// -// Neumorphic progress bar matching the Toggle track/knob depth pattern. -// Track: nm-inset-sm (recessed channel). Fill: nm-raised-sm (floating bar). "use client" @@ -12,7 +7,7 @@ import clsx from "clsx" type ProgressBarSize = "sm" | "md" | "lg" interface ProgressBarProps { - /** Percentage filled (0–100). Clamped internally. */ + /** Percentage filled (0-100). Clamped internally. */ percent: number /** Fill color — accepts any CSS color value or CSS variable. Defaults to accent. */ color?: string diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index d1b7e707..c31390ea 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -395,10 +395,7 @@ export const trackerDailyCheckpoints = pgTable( snapshotCount: integer("snapshot_count").notNull().default(0), }, (table) => [ - uniqueIndex("uq_tracker_checkpoint_tracker_date").on( - table.trackerId, - table.checkpointDate - ), + uniqueIndex("uq_tracker_checkpoint_tracker_date").on(table.trackerId, table.checkpointDate), ] ) From edc10a32cc9ef8efe0610fa0d501f8b69c8392a2 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 09:58:13 -0500 Subject: [PATCH 25/59] style(docs): fixed indentation issue --- docs/kb/docs/features/backups.md | 2 +- docs/kb/docs/features/download-clients.md | 2 +- docs/kb/docs/features/proxies.md | 6 +++--- docs/kb/docs/features/qbitmanage.md | 2 +- docs/kb/docs/features/tag-groups.md | 2 +- docs/kb/docs/features/totp.md | 4 ++-- docs/kb/docs/features/transit-papers.md | 10 +++++----- docs/kb/docs/features/webhooks.md | 2 +- docs/kb/docs/getting-started/docker-config.md | 10 +++++----- docs/kb/docs/getting-started/first-setup.md | 6 +++--- docs/kb/docs/getting-started/installation.md | 6 +++--- docs/kb/docs/trackers/adding-a-tracker.md | 6 +++--- docs/kb/docs/troubleshooting/common-errors.md | 18 +++++++++--------- .../docs/troubleshooting/ratio-not-updating.md | 4 ++-- .../kb/docs/troubleshooting/tracker-offline.md | 13 ++++++++----- 15 files changed, 48 insertions(+), 45 deletions(-) diff --git a/docs/kb/docs/features/backups.md b/docs/kb/docs/features/backups.md index b7a7c8a2..3ec49882 100644 --- a/docs/kb/docs/features/backups.md +++ b/docs/kb/docs/features/backups.md @@ -88,7 +88,7 @@ If you're restoring a backup from a different Tracker Tracker installation — o If a field can't be re-encrypted (for example, because the backup was encrypted with a password you no longer know), that field is cleared rather than saved in a broken state. For TOTP, the restore screen will tell you if 2FA was turned off as a result. You can re-enable it after the restore completes. !!! warning "TOTP after a cross-instance restore" -If 2FA was active on the source instance but can't be carried over, it will be disabled. Re-enroll in **Settings → Security** after the restore. + If 2FA was active on the source instance but can't be carried over, it will be disabled. Re-enroll in **Settings → Security** after the restore. ![Backup configuration — encryption, scheduling, and storage path](../assets/images/backups-configuration.png) diff --git a/docs/kb/docs/features/download-clients.md b/docs/kb/docs/features/download-clients.md index 0dfab7e5..a3dca79a 100644 --- a/docs/kb/docs/features/download-clients.md +++ b/docs/kb/docs/features/download-clients.md @@ -30,7 +30,7 @@ Go to **Settings → Download Clients** and fill in the connection details: | Use SSL | Enable if your qBittorrent Web UI is served over HTTPS | !!! warning "SSL/port mismatch" -The form will warn you if SSL is on with port 80, or SSL is off with port 443. These combinations are usually misconfigured. You can still save, but double-check your settings. + The form will warn you if SSL is on with port 80, or SSL is off with port 443. These combinations are usually misconfigured. You can still save, but double-check your settings. After saving, use the **Test Connection** button to confirm Tracker Tracker can reach and authenticate with qBittorrent. diff --git a/docs/kb/docs/features/proxies.md b/docs/kb/docs/features/proxies.md index d23abe0b..482d8775 100644 --- a/docs/kb/docs/features/proxies.md +++ b/docs/kb/docs/features/proxies.md @@ -6,7 +6,7 @@ description: Route tracker polling through SOCKS5, HTTP, or HTTPS proxies on a p # Proxies !!! warning "Experimental" -Proxy support is experimental and may not work with all trackers or proxy configurations. Use at your own risk. + Proxy support is experimental and may not work with all trackers or proxy configurations. Use at your own risk. You can route outbound tracker API requests through a proxy. Proxy support is opt-in — you configure one global proxy, and then individually enable it per tracker. @@ -23,10 +23,10 @@ The proxy type controls how your traffic reaches the proxy server — SOCKS5 for When a proxy is enabled for a tracker, the tracker sees the proxy's IP address — not yours. !!! warning "Some trackers ban proxy and VPN traffic" -Many private trackers explicitly prohibit accessing the site from VPNs, proxies, or shared IPs. Using a proxy for API polling may trigger automated security flags or get your account disabled. Check your tracker's rules before enabling this. If a tracker allows API access from a different IP than your browsing IP, you're probably fine — but not all trackers make that distinction. + Many private trackers explicitly prohibit accessing the site from VPNs, proxies, or shared IPs. Using a proxy for API polling may trigger automated security flags or get your account disabled. Check your tracker's rules before enabling this. If a tracker allows API access from a different IP than your browsing IP, you're probably fine — but not all trackers make that distinction. !!! info "DNS resolution" -HTTP and HTTPS proxies resolve the tracker's hostname on the proxy side — your local DNS provider never sees the domain. SOCKS5 behavior depends on configuration: most SOCKS5 proxies also resolve remotely, but some setups resolve locally first. If DNS privacy matters to you, verify your SOCKS5 proxy does remote resolution. + HTTP and HTTPS proxies resolve the tracker's hostname on the proxy side — your local DNS provider never sees the domain. SOCKS5 behavior depends on configuration: most SOCKS5 proxies also resolve remotely, but some setups resolve locally first. If DNS privacy matters to you, verify your SOCKS5 proxy does remote resolution. ## Setup diff --git a/docs/kb/docs/features/qbitmanage.md b/docs/kb/docs/features/qbitmanage.md index 5cd522b6..ceeaf467 100644 --- a/docs/kb/docs/features/qbitmanage.md +++ b/docs/kb/docs/features/qbitmanage.md @@ -39,7 +39,7 @@ Here's what the qbitmanage status breakdown looks like on a tracker's Torrents t ![qbitmanage status bar chart showing No Hardlinks, Min Seeds Not Met, Last Active Limit, and Last Active Not Reached](../assets/images/tracker-page-qbitmanage.png) !!! tip "Match your config exactly" -Tag names must match character-for-character, including any emoji or special characters. Copy them directly from your qbitmanage `config.yml`. + Tag names must match character-for-character, including any emoji or special characters. Copy them directly from your qbitmanage `config.yml`. ## Tag Group Examples diff --git a/docs/kb/docs/features/tag-groups.md b/docs/kb/docs/features/tag-groups.md index 046b1a95..e721908d 100644 --- a/docs/kb/docs/features/tag-groups.md +++ b/docs/kb/docs/features/tag-groups.md @@ -61,7 +61,7 @@ When enabled, the chart includes an extra segment for torrents that don't match Tags must match **exactly** — same capitalization, same spacing, same characters. If your qBittorrent tag is `High Priority` and you type `high priority` in the group, it won't match. !!! tip "Check your qBittorrent tags" -Open qBittorrent and look at the tag list in the sidebar to see the exact tag names. Copy them character-for-character into Tracker Tracker. + Open qBittorrent and look at the tag list in the sidebar to see the exact tag names. Copy them character-for-character into Tracker Tracker. ## Automating with qbitmanage diff --git a/docs/kb/docs/features/totp.md b/docs/kb/docs/features/totp.md index b2f4882f..ad657533 100644 --- a/docs/kb/docs/features/totp.md +++ b/docs/kb/docs/features/totp.md @@ -33,7 +33,7 @@ When you enable 2FA, you get 8 backup codes. Each one looks like this: Use a backup code at the TOTP prompt the same way you'd use a 6-digit code — there's a "Use a backup code" option on the login screen. !!! warning "Save your backup codes now" -If you lose your authenticator app and don't have backup codes, you cannot log in. There is no account recovery. Store the codes in a password manager or print them and keep them somewhere secure. + If you lose your authenticator app and don't have backup codes, you cannot log in. There is no account recovery. Store the codes in a password manager or print them and keep them somewhere secure. ## Logging In With 2FA @@ -61,7 +61,7 @@ Failed TOTP attempts count toward the same lockout limit as failed password atte ## After a Backup Restore !!! warning "2FA may be disabled after restoring a backup" -If you restore a backup from a different Tracker Tracker instance — one that was set up with a different password — the 2FA secret can't be carried over. In that case, 2FA will be turned off automatically as part of the restore. + If you restore a backup from a different Tracker Tracker instance — one that was set up with a different password — the 2FA secret can't be carried over. In that case, 2FA will be turned off automatically as part of the restore. The restore confirmation screen will tell you if this happened. You'll need to go back to **Settings → Security** and set up 2FA again. diff --git a/docs/kb/docs/features/transit-papers.md b/docs/kb/docs/features/transit-papers.md index ddb14e3f..d6af957b 100644 --- a/docs/kb/docs/features/transit-papers.md +++ b/docs/kb/docs/features/transit-papers.md @@ -8,14 +8,14 @@ description: Generate tamper-resistant proof-of-membership images for private tr !!! danger "This feature is currently unreleased" !!! warning "Beta — Highly Experimental" -Transit Papers are under active development. The report format, encoding scheme, and verification behavior may change between versions. Reports generated with one version are not guaranteed to verify correctly with a future version. Use at your own risk. + Transit Papers are under active development. The report format, encoding scheme, and verification behavior may change between versions. Reports generated with one version are not guaranteed to verify correctly with a future version. Use at your own risk. Transit Papers generate a tamper-resistant PNG image showing your stats on a single tracker. The image is designed to be shared with tracker moderators as proof of your membership and standing when applying to other trackers. Instead of a browser screenshot — which can be faked in seconds with inspect element or Photoshop — Transit Papers produce a server-rendered image with cryptographically linked visual elements. Editing any part of the image (the stats, the fractal seal, or the data strip) breaks the link between them, and the verification tool detects it. Each report is called a **Proof of Citizenship**. !!! warning "What Transit Papers are NOT" -Transit Papers are **not zero-trust cryptographic proof** that the stats are real. Tracker Tracker is self-hosted. You control the machine, the database, and the network. A technically motivated user could theoretically fabricate data before generation. + Transit Papers are **not zero-trust cryptographic proof** that the stats are real. Tracker Tracker is self-hosted. You control the machine, the database, and the network. A technically motivated user could theoretically fabricate data before generation. What this system does is raise the cost of forgery from **trivial** (inspect element, 30 seconds) to **impractical** (clone the project, set up a database, fabricate internally consistent stats across 10+ mathematically related fields, understand the target tracker's API schema). @@ -62,7 +62,7 @@ If you are not using an integration, share the original PNG file directly: - Upload to any lossless image host manually (ptpimg.me, imgbox.com, catbox.moe) !!! danger "Do not screenshot the report" -The verification system reads pixel data from the image. A screenshot of the report is not the report. It will degrade verification or cause it to fail entirely. + The verification system reads pixel data from the image. A screenshot of the report is not the report. It will degrade verification or cause it to fail entirely. --- @@ -136,7 +136,7 @@ Upload or drag-and-drop the PNG onto the verification page. : The image is too degraded or has been fundamentally altered. Cannot verify. !!! info "What verification does NOT tell you" -Verification confirms the image is internally consistent and has not been tampered with **after generation**. It does not confirm the stats are truthful — the user controls their instance and could have fabricated data before generating the report. Use it as one input alongside your own judgment. + Verification confirms the image is internally consistent and has not been tampered with **after generation**. It does not confirm the stats are truthful — the user controls their instance and could have fabricated data before generating the report. Use it as one input alongside your own judgment. --- @@ -174,7 +174,7 @@ Verification confirms the image is internally consistent and has not been tamper | User modifies the source code | Open source — code is public | Faker must reproduce pixel-perfect output from the full rendering pipeline | !!! note "Honest positioning" -This system raises forgery effort from trivial to impractical. It does not make forgery impossible. A determined attacker with technical skills who controls their instance can theoretically fabricate a valid report. The practical threat — someone trying to bluff their way into a tracker invite — is effectively blocked. + This system raises forgery effort from trivial to impractical. It does not make forgery impossible. A determined attacker with technical skills who controls their instance can theoretically fabricate a valid report. The practical threat — someone trying to bluff their way into a tracker invite — is effectively blocked. --- diff --git a/docs/kb/docs/features/webhooks.md b/docs/kb/docs/features/webhooks.md index fac917f0..8fbd1d99 100644 --- a/docs/kb/docs/features/webhooks.md +++ b/docs/kb/docs/features/webhooks.md @@ -59,7 +59,7 @@ Each target subscribes to any combination of these events: Cooldowns prevent spam — if a condition persists across multiple polls, you get one alert per cooldown period, not one per poll. !!! info "First-poll behavior" -Events that compare snapshots (ratio drop, hit-and-run) need at least two polls and won't fire on the first one. Events like "account warned" fire immediately if the condition is already true. + Events that compare snapshots (ratio drop, hit-and-run) need at least two polls and won't fire on the first one. Events like "account warned" fire immediately if the condition is already true. ## Thresholds diff --git a/docs/kb/docs/getting-started/docker-config.md b/docs/kb/docs/getting-started/docker-config.md index 69ae554f..4a79db4d 100644 --- a/docs/kb/docs/getting-started/docker-config.md +++ b/docs/kb/docs/getting-started/docker-config.md @@ -35,7 +35,7 @@ Everything you need to customize how Tracker Tracker runs: environment variables | `LOG_FILE` | _(none)_ | Absolute path inside the container to write logs to disk, e.g. `/data/logs/tracker-tracker.log`. | !!! info "Settings vs environment variables" -Most day-to-day settings — polling interval, privacy mode, proxy config, backup schedule, lockout policy — live inside the app under **Settings**, not in environment variables. Environment variables are just for infrastructure stuff like database connections and ports. + Most day-to-day settings — polling interval, privacy mode, proxy config, backup schedule, lockout policy — live inside the app under **Settings**, not in environment variables. Environment variables are just for infrastructure stuff like database connections and ports. --- @@ -50,7 +50,7 @@ Most day-to-day settings — polling interval, privacy mode, proxy config, backu | `./postgres/postgresql.conf` | `/etc/postgresql/postgresql.conf` | Custom PostgreSQL config. Included in the repo and required for the bundled database to start. | !!! warning "Back up the pgdata volume" -The `pgdata` named volume holds your entire database. Use the built-in backup feature (Settings → Backups) for app-level backups, and separately snapshot the Docker volume or use `pg_dump` if you want a database-level backup. + The `pgdata` named volume holds your entire database. Use the built-in backup feature (Settings → Backups) for app-level backups, and separately snapshot the Docker volume or use `pg_dump` if you want a database-level backup. --- @@ -70,7 +70,7 @@ PORT=8080 ``` !!! tip "Behind a reverse proxy?" -If Tracker Tracker sits behind Nginx, Caddy, or Traefik, you don't need to expose port 3000 to the outside world at all. Remove the `ports:` block from `docker-compose.yml` and let the reverse proxy talk to the container over the Docker network directly. + If Tracker Tracker sits behind Nginx, Caddy, or Traefik, you don't need to expose port 3000 to the outside world at all. Remove the `ports:` block from `docker-compose.yml` and let the reverse proxy talk to the container over the Docker network directly. --- @@ -144,7 +144,7 @@ If Tracker Tracker sits behind Nginx, Caddy, or Traefik, you don't need to expos This assumes Traefik is already running with a `websecure` entrypoint and a `letsencrypt` certificate resolver. !!! info "Set BASE_URL when using a reverse proxy" -Set `BASE_URL=https://trackertracker.example.com` in `.env`. This enables secure session cookies automatically and ensures backup files and notification links use your public address. + Set `BASE_URL=https://trackertracker.example.com` in `.env`. This enables secure session cookies automatically and ensures backup files and notification links use your public address. --- @@ -170,7 +170,7 @@ docker compose pull && docker compose up -d The database schema updates automatically on startup. No manual steps required. !!! tip "Check the changelog first" -Read the [CHANGELOG](https://github.com/jordanlambrecht/tracker-tracker/blob/main/CHANGELOG.md) before pulling a new image — especially for major version bumps, which may include breaking changes to backup formats or environment variables. + Read the [CHANGELOG](https://github.com/jordanlambrecht/tracker-tracker/blob/main/CHANGELOG.md) before pulling a new image — especially for major version bumps, which may include breaking changes to backup formats or environment variables. To pin to a specific version and update deliberately: diff --git a/docs/kb/docs/getting-started/first-setup.md b/docs/kb/docs/getting-started/first-setup.md index a0182ec7..c52958fb 100644 --- a/docs/kb/docs/getting-started/first-setup.md +++ b/docs/kb/docs/getting-started/first-setup.md @@ -19,10 +19,10 @@ Your password does two things: 2. **Protects your stored API tokens.** All tracker API tokens are encrypted at rest using a key derived from your password. If you lose your password, those tokens can't be recovered — but you can re-enter them manually. !!! warning "Choose strong credentials" -There is no recovery mechanism. If you forget your password, you'll need to reset the database and start fresh. Keep it in a password manager. + There is no recovery mechanism. If you forget your password, you'll need to reset the database and start fresh. Keep it in a password manager. !!! info "Your password stays on your machine" -It's hashed on the server before being stored. The raw password is never saved anywhere. + It's hashed on the server before being stored. The raw password is never saved anywhere. Click **Create Account**. You'll be logged in and land on the dashboard. @@ -41,7 +41,7 @@ The left sidebar has three sections: The main area shows the tracker overview grid, charts, and leaderboard. Charts fill in automatically as polling history builds up over time. !!! tip "Polling starts right away" -The moment you add a tracker, the app polls it and records a snapshot. Stats start charting from that first poll. The default polling interval is 60 minutes — you can change it in **Settings → General**. + The moment you add a tracker, the app polls it and records a snapshot. Stats start charting from that first poll. The default polling interval is 60 minutes — you can change it in **Settings → General**. --- diff --git a/docs/kb/docs/getting-started/installation.md b/docs/kb/docs/getting-started/installation.md index a66e544f..072783e7 100644 --- a/docs/kb/docs/getting-started/installation.md +++ b/docs/kb/docs/getting-started/installation.md @@ -15,7 +15,7 @@ Tracker Tracker runs as a Docker image. The easiest way to get it running is wit Nothing else needs to be installed on your host. !!! info "Architecture support" -The image supports **linux/amd64** and **linux/arm64**. It runs on x86-64 servers and ARM machines like Raspberry Pi 4/5 or Apple Silicon in Linux VMs — Docker picks the right version automatically. + The image supports **linux/amd64** and **linux/arm64**. It runs on x86-64 servers and ARM machines like Raspberry Pi 4/5 or Apple Silicon in Linux VMs — Docker picks the right version automatically. --- @@ -53,7 +53,7 @@ TZ=America/Chicago ``` !!! warning "Don't reuse these values" -`SESSION_SECRET` protects your session cookies. `POSTGRES_PASSWORD` protects your database. Generate fresh values — never copy the placeholder text from `.env.example`. + `SESSION_SECRET` protects your session cookies. `POSTGRES_PASSWORD` protects your database. Generate fresh values — never copy the placeholder text from `.env.example`. ## Step 4 — Start the stack @@ -149,4 +149,4 @@ TZ=America/Chicago ``` !!! tip -You only need to create the database itself beforehand. The app handles the rest on first startup — no manual SQL required. + You only need to create the database itself beforehand. The app handles the rest on first startup — no manual SQL required. diff --git a/docs/kb/docs/trackers/adding-a-tracker.md b/docs/kb/docs/trackers/adding-a-tracker.md index 595f4111..d4481e91 100644 --- a/docs/kb/docs/trackers/adding-a-tracker.md +++ b/docs/kb/docs/trackers/adding-a-tracker.md @@ -61,7 +61,7 @@ Click the **+** button next to "Trackers" in the sidebar, or go to `/trackers/ne | Nebulance | NBL | !!! note "Draft entries" -Some trackers in the registry are marked as drafts — dashed border, "Stats tracking not yet supported." You can pin them as quicklinks but no stats will be polled. + Some trackers in the registry are marked as drafts — dashed border, "Stats tracking not yet supported." You can pin them as quicklinks but no stats will be polled. Trackers you've already added are hidden from the list automatically. @@ -101,7 +101,7 @@ Where to find it depends on the platform: GGn keys don't expire on their own, but they can be regenerated from your settings. !!! warning "Keep your token private" -Your API token acts like a password for your account. Tracker Tracker encrypts it before storing it. + Your API token acts like a password for your account. Tracker Tracker encrypts it before storing it. ## Optional fields @@ -121,7 +121,7 @@ If the tracker needs a proxy (e.g., for geo-restrictions), toggle **Use Proxy** ![PulseDot states showing healthy, warning, and error](../assets/images/pulsedots-states.png) !!! tip "Red dot right after adding?" -Check your API token. The most common cause is a copy-paste error or a token that was rotated after you copied it. + Check your API token. The most common cause is a copy-paste error or a token that was rotated after you copied it. ## Polling manually diff --git a/docs/kb/docs/troubleshooting/common-errors.md b/docs/kb/docs/troubleshooting/common-errors.md index 4f05fb58..bdb14e44 100644 --- a/docs/kb/docs/troubleshooting/common-errors.md +++ b/docs/kb/docs/troubleshooting/common-errors.md @@ -99,7 +99,7 @@ These appear in the **Poll Error Banner** on the tracker detail page, under the **Cause:** An unexpected error occurred that did not match any of the known patterns above. !!! success "Solution" -Check the Tracker Tracker server logs (`docker compose logs app`) for the full error. Look for a line containing `Poll failed for tracker` followed by the raw error string. + Check the Tracker Tracker server logs (`docker compose logs app`) for the full error. Look for a line containing `Poll failed for tracker` followed by the raw error string. --- @@ -122,7 +122,7 @@ These appear on the **Download Clients** panel in Settings, on individual client **Cause:** qBittorrent returned a 403 on an authenticated request, indicating the session has expired. Tracker Tracker handles this automatically by re-authenticating on the next request. !!! info "No action needed" -Session expiry is handled transparently. If you see persistent errors in the **Download Clients** panel, the issue is more likely wrong credentials rather than session expiry. + Session expiry is handled transparently. If you see persistent errors in the **Download Clients** panel, the issue is more likely wrong credentials rather than session expiry. --- @@ -139,7 +139,7 @@ Session expiry is handled transparently. If you see persistent errors in the **D **Cause:** DNS resolution failed for the qBittorrent host. !!! success "Solution" -Use the container name (if qBittorrent is in the same Docker Compose stack) or an IP address instead of a hostname that depends on external DNS. + Use the container name (if qBittorrent is in the same Docker Compose stack) or an IP address instead of a hostname that depends on external DNS. --- @@ -148,7 +148,7 @@ Use the container name (if qBittorrent is in the same Docker Compose stack) or a **Cause:** qBittorrent accepted the connection but did not respond within 15 seconds. This can happen during heavy indexing operations. !!! success "Solution" -Wait for qBittorrent to finish processing and retry. If timeouts are persistent, check qBittorrent's CPU and memory usage. + Wait for qBittorrent to finish processing and retry. If timeouts are persistent, check qBittorrent's CPU and memory usage. --- @@ -161,7 +161,7 @@ Wait for qBittorrent to finish processing and retry. If timeouts are persistent, **Cause:** The auto-lockout feature triggered. After a configurable number of consecutive failed login attempts, the app blocks further attempts until the lockout duration expires. !!! success "Solution" -Wait for the lockout duration to expire — the remaining time is shown on the login page. If you are locked out and cannot wait, you will need to connect to the database directly and clear the `lockedUntil` field in the `appSettings` table. + Wait for the lockout duration to expire. The remaining time is shown on the login page. If you are locked out and cannot wait, you will need to connect to the database directly and clear the `lockedUntil` field in the `appSettings` table. --- @@ -170,7 +170,7 @@ Wait for the lockout duration to expire — the remaining time is shown on the l **Cause:** The server logs show "Login successful" but the browser keeps returning to the login screen. This happens when session cookies are marked `Secure` but the app is accessed over plain HTTP — browsers silently discard `Secure` cookies on non-HTTPS connections. !!! success "Solution" -If you access Tracker Tracker over plain HTTP (e.g. `http://192.168.1.x:3000`), no action is needed on recent versions — cookies default to non-secure. If you are on an older version, update to the latest image. If you serve over HTTPS via a reverse proxy, set `BASE_URL=https://your-domain.com` in `.env` to enable secure cookies. + If you access Tracker Tracker over plain HTTP (e.g. `http://192.168.1.x:3000`), no action is needed on recent versions — cookies default to non-secure. If you are on an older version, update to the latest image. If you serve over HTTPS via a reverse proxy, set `BASE_URL=https://your-domain.com` in `.env` to enable secure cookies. --- @@ -187,7 +187,7 @@ If you access Tracker Tracker over plain HTTP (e.g. `http://192.168.1.x:3000`), **Cause:** You no longer have access to the authenticator app and have no backup codes. !!! warning "Database access required" -There is no in-app recovery path for this situation. You will need to connect to the PostgreSQL database directly and run: + There is no in-app recovery path for this situation. You will need to connect to the PostgreSQL database directly and run: ```sql UPDATE app_settings SET totp_secret = NULL, totp_backup_codes = NULL; @@ -206,7 +206,7 @@ There is no in-app recovery path for this situation. You will need to connect to **Cause:** The URL you entered uses an unsupported scheme (e.g. `ftp://`) or is a bare hostname without a scheme. !!! success "Solution" -Prefix the URL with `https://` or `http://`. + Prefix the URL with `https://` or `http://`. --- @@ -215,4 +215,4 @@ Prefix the URL with `https://` or `http://`. **Cause:** The URL resolves to a private IP range (192.168.x.x, 10.x.x.x, 172.16-31.x.x) or localhost. Tracker Tracker blocks these to prevent your internal network from being probed through the app. !!! info "By design" -Tracker URLs must be publicly routable hostnames. Internal or self-hosted trackers only accessible via private IPs cannot be added. + Tracker URLs must be publicly routable hostnames. Internal or self-hosted trackers only accessible via private IPs cannot be added. diff --git a/docs/kb/docs/troubleshooting/ratio-not-updating.md b/docs/kb/docs/troubleshooting/ratio-not-updating.md index 4359d59c..a63bbccd 100644 --- a/docs/kb/docs/troubleshooting/ratio-not-updating.md +++ b/docs/kb/docs/troubleshooting/ratio-not-updating.md @@ -16,7 +16,7 @@ Go to **Settings → General** and find the **Tracker Poll Interval** field. The If this is set to 240, stats will only update every 4 hours. That is expected behavior, not a bug. !!! info "Minimum update rate" -Setting the interval to 15 minutes is the fastest update rate available. Stats will not update more frequently than this regardless of how often you reload the page. + Setting the interval to 15 minutes is the fastest update rate available. Stats will not update more frequently than this regardless of how often you reload the page. --- @@ -82,4 +82,4 @@ If you have configured a **Snapshot Retention** period in Settings → General, If retention is set to 7 days and you are looking at a 30-day chart, the older portion of the chart will be empty. That data was pruned. !!! tip "Retention defaults" -If retention is not configured, snapshots are kept forever. If you see gaps in older data, check the retention setting in Settings → General. + If retention is not configured, snapshots are kept forever. If you see gaps in older data, check the retention setting in Settings → General. diff --git a/docs/kb/docs/troubleshooting/tracker-offline.md b/docs/kb/docs/troubleshooting/tracker-offline.md index db485b05..e1dd772d 100644 --- a/docs/kb/docs/troubleshooting/tracker-offline.md +++ b/docs/kb/docs/troubleshooting/tracker-offline.md @@ -38,7 +38,7 @@ When this happens: Paused trackers are skipped entirely until you manually resume them. !!! warning "Verify the cause before resuming" -The banner reads: _"Polling was paused after repeated failures. Verify your API key is correct before resuming."_ If you resume without fixing the underlying problem, the tracker will fail again immediately and re-pause within the same poll cycle. + The banner reads: _"Polling was paused after repeated failures. Verify your API key is correct before resuming."_ If you resume without fixing the underlying problem, the tracker will fail again immediately and re-pause within the same poll cycle. --- @@ -91,7 +91,8 @@ The hostname resolved but the connection was refused or the host was unreachable **Symptom:** The Poll Error Banner shows `Connection refused` or `Host unreachable`. -!!! success "Solution" 1. Check whether the tracker site loads in your browser. 2. If the tracker is down, wait for it to recover, then resume polling. 3. If you route tracker traffic through a VPN, confirm the VPN is up. 4. Verify the base URL uses the correct scheme (`https://` vs `http://`) and the right port if the tracker uses a non-standard one. +!!! success "Solution" 1. + Check whether the tracker site loads in your browser. 2. If the tracker is down, wait for it to recover, then resume polling. 3. If you route tracker traffic through a VPN, confirm the VPN is up. 4. Verify the base URL uses the correct scheme (`https://` vs `http://`) and the right port if the tracker uses a non-standard one. --- @@ -101,7 +102,8 @@ The connection was established but the tracker API did not respond within 15 sec **Symptom:** The Poll Error Banner shows `Request timed out`. -!!! success "Solution" 1. This is often transient. Try **Poll Now** again after a few minutes. 2. If timeouts are persistent, check whether a proxy is adding significant latency. 3. If the tracker's API is consistently slow, there is no configurable timeout override. +!!! success "Solution" 1. + This is often transient. Try **Poll Now** again after a few minutes. 2. If timeouts are persistent, check whether a proxy is adding significant latency. 3. If the tracker's API is consistently slow, there is no configurable timeout override. --- @@ -111,7 +113,8 @@ Some trackers block repeated requests from a single IP if polls come in too freq **Symptom:** The Poll Error Banner shows `IP temporarily banned by tracker`. -!!! success "Solution" 1. Go to **Settings → General** and increase the **Poll Interval**. The minimum is 15 minutes, but 60 minutes (the default) is recommended for most trackers. 2. Wait for the IP ban to expire on the tracker side — this varies by site, typically minutes to hours. 3. Resume polling only after the ban has likely cleared. +!!! success "Solution" 1. + Go to **Settings → General** and increase the **Poll Interval**. The minimum is 15 minutes, but 60 minutes (the default) is recommended for most trackers. 2. Wait for the IP ban to expire on the tracker side — this varies by site, typically minutes to hours. 3. Resume polling only after the ban has likely cleared. --- @@ -120,4 +123,4 @@ Some trackers block repeated requests from a single IP if polls come in too freq If polling has not paused but you still see a **Last Error** banner — without the "Polling Paused" heading — it means a recent poll failed but not enough times to trigger a pause. The banner clears automatically on the next successful poll. !!! info "No action needed if the last poll succeeded" -The Last Error banner always shows the most recent error, even if subsequent polls recovered. If the PulseDot is `healthy`, `warning`, or `critical` (not `error` or `paused`), polling has already recovered on its own. + The Last Error banner always shows the most recent error, even if subsequent polls recovered. If the PulseDot is `healthy`, `warning`, or `critical` (not `error` or `paused`), polling has already recovered on its own. From e57fa0708ad6255ca6c76ab15c05f6d69a72fa71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:00:19 +0000 Subject: [PATCH 26/59] chore(deps-dev): bump knip from 6.0.5 to 6.0.6 Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.0.5 to 6.0.6. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.0.6/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.0.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 6589 ++++++++++++++++-------------------------------- 2 files changed, 2190 insertions(+), 4401 deletions(-) diff --git a/package.json b/package.json index 96b05d44..37112130 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "dotenv": "^17.3.1", "husky": "^9.1.7", "jsdom": "^29.0.1", - "knip": "^6.0.4", + "knip": "^6.0.6", "postcss": "^8.5.8", "prettier": "^3.8.1", "tailwindcss": "^4.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ad4dc29..fd988e60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,31 +1,32 @@ -lockfileVersion: "9.0" +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false overrides: - esbuild: ">=0.25.0" + esbuild: '>=0.25.0' importers: + .: dependencies: - "@dnd-kit/core": + '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@dnd-kit/sortable": + '@dnd-kit/sortable': specifier: ^10.0.0 version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) - "@dnd-kit/utilities": + '@dnd-kit/utilities': specifier: ^3.2.2 version: 3.2.2(react@19.2.4) - "@formkit/auto-animate": + '@formkit/auto-animate': specifier: ^0.9.0 version: 0.9.0 - "@tailwindcss/typography": + '@tailwindcss/typography': specifier: ^0.5.19 version: 0.5.19(tailwindcss@4.2.2) - "@tanstack/react-query": + '@tanstack/react-query': specifier: ^5.95.2 version: 5.95.2(react@19.2.4) argon2: @@ -101,37 +102,37 @@ importers: specifier: ^9.0.0 version: 9.0.0 devDependencies: - "@biomejs/biome": + '@biomejs/biome': specifier: ^2.4.8 version: 2.4.8 - "@commitlint/cli": + '@commitlint/cli': specifier: ^20.5.0 version: 20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2) - "@commitlint/config-conventional": + '@commitlint/config-conventional': specifier: ^20.5.0 version: 20.5.0 - "@tailwindcss/postcss": + '@tailwindcss/postcss': specifier: ^4.2.2 version: 4.2.2 - "@testing-library/jest-dom": + '@testing-library/jest-dom': specifier: ^6.9.1 version: 6.9.1 - "@testing-library/react": + '@testing-library/react': specifier: ^16.3.2 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@testing-library/user-event": + '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) - "@types/node": + '@types/node': specifier: ^25.5.0 version: 25.5.0 - "@types/node-cron": + '@types/node-cron': specifier: ^3.0.11 version: 3.0.11 - "@types/react": + '@types/react': specifier: ^19.2.14 version: 19.2.14 - "@types/react-dom": + '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.14) commit-and-tag-version: @@ -147,8 +148,8 @@ importers: specifier: ^29.0.1 version: 29.0.1(@noble/hashes@2.0.1) knip: - specifier: ^6.0.4 - version: 6.0.5 + specifier: ^6.0.6 + version: 6.0.6 postcss: specifier: ^8.5.8 version: 8.5.8 @@ -169,277 +170,170 @@ importers: version: 4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages: - "@adobe/css-tools@4.4.4": - resolution: - { - integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==, - } - - "@alloc/quick-lru@5.2.0": - resolution: - { - integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, - } - engines: { node: ">=10" } - - "@asamuzakjp/css-color@5.0.1": - resolution: - { - integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } - - "@asamuzakjp/dom-selector@7.0.4": - resolution: - { - integrity: sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } - - "@asamuzakjp/nwsapi@2.3.9": - resolution: - { - integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, - } - - "@babel/code-frame@7.29.0": - resolution: - { - integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-identifier@7.28.5": - resolution: - { - integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, - } - engines: { node: ">=6.9.0" } - - "@babel/runtime@7.29.2": - resolution: - { - integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==, - } - engines: { node: ">=6.9.0" } - - "@biomejs/biome@2.4.8": - resolution: - { - integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==, - } - engines: { node: ">=14.21.3" } + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@asamuzakjp/css-color@5.0.1': + resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.0.4': + resolution: {integrity: sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.4.8': + resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} + engines: {node: '>=14.21.3'} hasBin: true - "@biomejs/cli-darwin-arm64@2.4.8": - resolution: - { - integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-darwin-arm64@2.4.8': + resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} + engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - "@biomejs/cli-darwin-x64@2.4.8": - resolution: - { - integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-darwin-x64@2.4.8': + resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} + engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - "@biomejs/cli-linux-arm64-musl@2.4.8": - resolution: - { - integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-linux-arm64-musl@2.4.8': + resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} + engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - "@biomejs/cli-linux-arm64@2.4.8": - resolution: - { - integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-linux-arm64@2.4.8': + resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} + engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - "@biomejs/cli-linux-x64-musl@2.4.8": - resolution: - { - integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-linux-x64-musl@2.4.8': + resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} + engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - "@biomejs/cli-linux-x64@2.4.8": - resolution: - { - integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-linux-x64@2.4.8': + resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} + engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - "@biomejs/cli-win32-arm64@2.4.8": - resolution: - { - integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-win32-arm64@2.4.8': + resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} + engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - "@biomejs/cli-win32-x64@2.4.8": - resolution: - { - integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==, - } - engines: { node: ">=14.21.3" } + '@biomejs/cli-win32-x64@2.4.8': + resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} + engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] - "@bramus/specificity@2.4.2": - resolution: - { - integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==, - } + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - "@commitlint/cli@20.5.0": - resolution: - { - integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==, - } - engines: { node: ">=v18" } + '@commitlint/cli@20.5.0': + resolution: {integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==} + engines: {node: '>=v18'} hasBin: true - "@commitlint/config-conventional@20.5.0": - resolution: - { - integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==, - } - engines: { node: ">=v18" } - - "@commitlint/config-validator@20.5.0": - resolution: - { - integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==, - } - engines: { node: ">=v18" } - - "@commitlint/ensure@20.5.0": - resolution: - { - integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==, - } - engines: { node: ">=v18" } - - "@commitlint/execute-rule@20.0.0": - resolution: - { - integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==, - } - engines: { node: ">=v18" } - - "@commitlint/format@20.5.0": - resolution: - { - integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==, - } - engines: { node: ">=v18" } - - "@commitlint/is-ignored@20.5.0": - resolution: - { - integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==, - } - engines: { node: ">=v18" } - - "@commitlint/lint@20.5.0": - resolution: - { - integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==, - } - engines: { node: ">=v18" } - - "@commitlint/load@20.5.0": - resolution: - { - integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==, - } - engines: { node: ">=v18" } - - "@commitlint/message@20.4.3": - resolution: - { - integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==, - } - engines: { node: ">=v18" } - - "@commitlint/parse@20.5.0": - resolution: - { - integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==, - } - engines: { node: ">=v18" } - - "@commitlint/read@20.5.0": - resolution: - { - integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==, - } - engines: { node: ">=v18" } - - "@commitlint/resolve-extends@20.5.0": - resolution: - { - integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==, - } - engines: { node: ">=v18" } - - "@commitlint/rules@20.5.0": - resolution: - { - integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==, - } - engines: { node: ">=v18" } - - "@commitlint/to-lines@20.0.0": - resolution: - { - integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==, - } - engines: { node: ">=v18" } - - "@commitlint/top-level@20.4.3": - resolution: - { - integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==, - } - engines: { node: ">=v18" } - - "@commitlint/types@20.5.0": - resolution: - { - integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==, - } - engines: { node: ">=v18" } - - "@conventional-changelog/git-client@2.6.0": - resolution: - { - integrity: sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==, - } - engines: { node: ">=18" } + '@commitlint/config-conventional@20.5.0': + resolution: {integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@20.5.0': + resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==} + engines: {node: '>=v18'} + + '@commitlint/ensure@20.5.0': + resolution: {integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@20.0.0': + resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} + engines: {node: '>=v18'} + + '@commitlint/format@20.5.0': + resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@20.5.0': + resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==} + engines: {node: '>=v18'} + + '@commitlint/lint@20.5.0': + resolution: {integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==} + engines: {node: '>=v18'} + + '@commitlint/load@20.5.0': + resolution: {integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==} + engines: {node: '>=v18'} + + '@commitlint/message@20.4.3': + resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} + engines: {node: '>=v18'} + + '@commitlint/parse@20.5.0': + resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==} + engines: {node: '>=v18'} + + '@commitlint/read@20.5.0': + resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@20.5.0': + resolution: {integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==} + engines: {node: '>=v18'} + + '@commitlint/rules@20.5.0': + resolution: {integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@20.0.0': + resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@20.4.3': + resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} + engines: {node: '>=v18'} + + '@commitlint/types@20.5.0': + resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==} + engines: {node: '>=v18'} + + '@conventional-changelog/git-client@2.6.0': + resolution: {integrity: sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==} + engines: {node: '>=18'} peerDependencies: conventional-commits-filter: ^5.0.0 conventional-commits-parser: ^6.3.0 @@ -449,1712 +343,1097 @@ packages: conventional-commits-parser: optional: true - "@csstools/color-helpers@6.0.2": - resolution: - { - integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==, - } - engines: { node: ">=20.19.0" } - - "@csstools/css-calc@3.1.1": - resolution: - { - integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==, - } - engines: { node: ">=20.19.0" } + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.1.1': + resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-parser-algorithms": ^4.0.0 - "@csstools/css-tokenizer": ^4.0.0 - - "@csstools/css-color-parser@4.0.2": - resolution: - { - integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==, - } - engines: { node: ">=20.19.0" } + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.0.2': + resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-parser-algorithms": ^4.0.0 - "@csstools/css-tokenizer": ^4.0.0 - - "@csstools/css-parser-algorithms@4.0.0": - resolution: - { - integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, - } - engines: { node: ">=20.19.0" } + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-tokenizer": ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 - "@csstools/css-syntax-patches-for-csstree@1.1.1": - resolution: - { - integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==, - } + '@csstools/css-syntax-patches-for-csstree@1.1.1': + resolution: {integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: css-tree: optional: true - "@csstools/css-tokenizer@4.0.0": - resolution: - { - integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, - } - engines: { node: ">=20.19.0" } - - "@dnd-kit/accessibility@3.1.1": - resolution: - { - integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==, - } + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} peerDependencies: - react: ">=16.8.0" + react: '>=16.8.0' - "@dnd-kit/core@6.3.1": - resolution: - { - integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==, - } + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - - "@dnd-kit/sortable@10.0.0": - resolution: - { - integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==, - } + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} peerDependencies: - "@dnd-kit/core": ^6.3.0 - react: ">=16.8.0" - - "@dnd-kit/utilities@3.2.2": - resolution: - { - integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==, - } + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} peerDependencies: - react: ">=16.8.0" - - "@drizzle-team/brocli@0.10.2": - resolution: - { - integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==, - } - - "@emnapi/core@1.9.1": - resolution: - { - integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==, - } - - "@emnapi/runtime@1.9.1": - resolution: - { - integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==, - } - - "@emnapi/wasi-threads@1.2.0": - resolution: - { - integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==, - } - - "@epic-web/invariant@1.0.0": - resolution: - { - integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==, - } - - "@esbuild-kit/core-utils@3.3.2": - resolution: - { - integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==, - } - deprecated: "Merged into tsx: https://tsx.is" - - "@esbuild-kit/esm-loader@2.6.5": - resolution: - { - integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==, - } - deprecated: "Merged into tsx: https://tsx.is" - - "@esbuild/aix-ppc64@0.27.4": - resolution: - { - integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==, - } - engines: { node: ">=18" } + react: '>=16.8.0' + + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@emnapi/core@1.9.1': + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + + '@emnapi/wasi-threads@1.2.0': + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - "@esbuild/android-arm64@0.27.4": - resolution: - { - integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==, - } - engines: { node: ">=18" } + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - "@esbuild/android-arm@0.27.4": - resolution: - { - integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==, - } - engines: { node: ">=18" } + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - "@esbuild/android-x64@0.27.4": - resolution: - { - integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==, - } - engines: { node: ">=18" } + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} cpu: [x64] os: [android] - "@esbuild/darwin-arm64@0.27.4": - resolution: - { - integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==, - } - engines: { node: ">=18" } + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-x64@0.27.4": - resolution: - { - integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==, - } - engines: { node: ">=18" } + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - "@esbuild/freebsd-arm64@0.27.4": - resolution: - { - integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-x64@0.27.4": - resolution: - { - integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - "@esbuild/linux-arm64@0.27.4": - resolution: - { - integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm@0.27.4": - resolution: - { - integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - "@esbuild/linux-ia32@0.27.4": - resolution: - { - integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==, - } - engines: { node: ">=18" } + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - "@esbuild/linux-loong64@0.27.4": - resolution: - { - integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==, - } - engines: { node: ">=18" } + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - "@esbuild/linux-mips64el@0.27.4": - resolution: - { - integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==, - } - engines: { node: ">=18" } + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - "@esbuild/linux-ppc64@0.27.4": - resolution: - { - integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==, - } - engines: { node: ">=18" } + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - "@esbuild/linux-riscv64@0.27.4": - resolution: - { - integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==, - } - engines: { node: ">=18" } + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - "@esbuild/linux-s390x@0.27.4": - resolution: - { - integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==, - } - engines: { node: ">=18" } + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - "@esbuild/linux-x64@0.27.4": - resolution: - { - integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==, - } - engines: { node: ">=18" } + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - "@esbuild/netbsd-arm64@0.27.4": - resolution: - { - integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - "@esbuild/netbsd-x64@0.27.4": - resolution: - { - integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - "@esbuild/openbsd-arm64@0.27.4": - resolution: - { - integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - "@esbuild/openbsd-x64@0.27.4": - resolution: - { - integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - "@esbuild/openharmony-arm64@0.27.4": - resolution: - { - integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==, - } - engines: { node: ">=18" } + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - "@esbuild/sunos-x64@0.27.4": - resolution: - { - integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==, - } - engines: { node: ">=18" } + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - "@esbuild/win32-arm64@0.27.4": - resolution: - { - integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==, - } - engines: { node: ">=18" } + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - "@esbuild/win32-ia32@0.27.4": - resolution: - { - integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==, - } - engines: { node: ">=18" } + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - "@esbuild/win32-x64@0.27.4": - resolution: - { - integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==, - } - engines: { node: ">=18" } + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - "@exodus/bytes@1.15.0": - resolution: - { - integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - "@noble/hashes": ^1.8.0 || ^2.0.0 + '@noble/hashes': ^1.8.0 || ^2.0.0 peerDependenciesMeta: - "@noble/hashes": + '@noble/hashes': optional: true - "@formkit/auto-animate@0.9.0": - resolution: - { - integrity: sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==, - } - - "@hutson/parse-repository-url@3.0.2": - resolution: - { - integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==, - } - engines: { node: ">=6.9.0" } - - "@img/colour@1.1.0": - resolution: - { - integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, - } - engines: { node: ">=18" } - - "@img/sharp-darwin-arm64@0.34.5": - resolution: - { - integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@formkit/auto-animate@0.9.0': + resolution: {integrity: sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==} + + '@hutson/parse-repository-url@3.0.2': + resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} + engines: {node: '>=6.9.0'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - "@img/sharp-darwin-x64@0.34.5": - resolution: - { - integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - "@img/sharp-libvips-darwin-arm64@1.2.4": - resolution: - { - integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, - } + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - "@img/sharp-libvips-darwin-x64@1.2.4": - resolution: - { - integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, - } + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - "@img/sharp-libvips-linux-arm64@1.2.4": - resolution: - { - integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, - } + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-arm@1.2.4": - resolution: - { - integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, - } + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-ppc64@1.2.4": - resolution: - { - integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, - } + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-riscv64@1.2.4": - resolution: - { - integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, - } + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-s390x@1.2.4": - resolution: - { - integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, - } + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-x64@1.2.4": - resolution: - { - integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, - } + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": - resolution: - { - integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, - } + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-libvips-linuxmusl-x64@1.2.4": - resolution: - { - integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, - } + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-linux-arm64@0.34.5": - resolution: - { - integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-linux-arm@0.34.5": - resolution: - { - integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-linux-ppc64@0.34.5": - resolution: - { - integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-linux-riscv64@0.34.5": - resolution: - { - integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-linux-s390x@0.34.5": - resolution: - { - integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-linux-x64@0.34.5": - resolution: - { - integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-linuxmusl-arm64@0.34.5": - resolution: - { - integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-linuxmusl-x64@0.34.5": - resolution: - { - integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-wasm32@0.34.5": - resolution: - { - integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - "@img/sharp-win32-arm64@0.34.5": - resolution: - { - integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - "@img/sharp-win32-ia32@0.34.5": - resolution: - { - integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - "@img/sharp-win32-x64@0.34.5": - resolution: - { - integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - "@jridgewell/gen-mapping@0.3.13": - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } - - "@jridgewell/remapping@2.3.5": - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } - - "@jridgewell/resolve-uri@3.1.2": - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: ">=6.0.0" } - - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } - - "@jridgewell/trace-mapping@0.3.31": - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } - - "@napi-rs/wasm-runtime@1.1.1": - resolution: - { - integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==, - } - - "@next/env@16.2.1": - resolution: - { - integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==, - } - - "@next/swc-darwin-arm64@16.2.1": - resolution: - { - integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==, - } - engines: { node: ">= 10" } + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + + '@next/env@16.2.1': + resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==} + + '@next/swc-darwin-arm64@16.2.1': + resolution: {integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - "@next/swc-darwin-x64@16.2.1": - resolution: - { - integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==, - } - engines: { node: ">= 10" } + '@next/swc-darwin-x64@16.2.1': + resolution: {integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] - "@next/swc-linux-arm64-gnu@16.2.1": - resolution: - { - integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==, - } - engines: { node: ">= 10" } + '@next/swc-linux-arm64-gnu@16.2.1': + resolution: {integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - "@next/swc-linux-arm64-musl@16.2.1": - resolution: - { - integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==, - } - engines: { node: ">= 10" } + '@next/swc-linux-arm64-musl@16.2.1': + resolution: {integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - "@next/swc-linux-x64-gnu@16.2.1": - resolution: - { - integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==, - } - engines: { node: ">= 10" } + '@next/swc-linux-x64-gnu@16.2.1': + resolution: {integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - "@next/swc-linux-x64-musl@16.2.1": - resolution: - { - integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==, - } - engines: { node: ">= 10" } + '@next/swc-linux-x64-musl@16.2.1': + resolution: {integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - "@next/swc-win32-arm64-msvc@16.2.1": - resolution: - { - integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==, - } - engines: { node: ">= 10" } + '@next/swc-win32-arm64-msvc@16.2.1': + resolution: {integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] - "@next/swc-win32-x64-msvc@16.2.1": - resolution: - { - integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==, - } - engines: { node: ">= 10" } + '@next/swc-win32-x64-msvc@16.2.1': + resolution: {integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] - "@noble/hashes@2.0.1": - resolution: - { - integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==, - } - engines: { node: ">= 20.19.0" } - - "@nodelib/fs.scandir@2.1.5": - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: ">= 8" } - - "@nodelib/fs.stat@2.0.5": - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: ">= 8" } - - "@nodelib/fs.walk@1.2.8": - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: ">= 8" } - - "@oxc-parser/binding-android-arm-eabi@0.120.0": - resolution: - { - integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxc-parser/binding-android-arm-eabi@0.120.0': + resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - "@oxc-parser/binding-android-arm64@0.120.0": - resolution: - { - integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-android-arm64@0.120.0': + resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - "@oxc-parser/binding-darwin-arm64@0.120.0": - resolution: - { - integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-darwin-arm64@0.120.0': + resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - "@oxc-parser/binding-darwin-x64@0.120.0": - resolution: - { - integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-darwin-x64@0.120.0': + resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - "@oxc-parser/binding-freebsd-x64@0.120.0": - resolution: - { - integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-freebsd-x64@0.120.0': + resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - "@oxc-parser/binding-linux-arm-gnueabihf@0.120.0": - resolution: - { - integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': + resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - "@oxc-parser/binding-linux-arm-musleabihf@0.120.0": - resolution: - { - integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': + resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - "@oxc-parser/binding-linux-arm64-gnu@0.120.0": - resolution: - { - integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': + resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - "@oxc-parser/binding-linux-arm64-musl@0.120.0": - resolution: - { - integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-arm64-musl@0.120.0': + resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - "@oxc-parser/binding-linux-ppc64-gnu@0.120.0": - resolution: - { - integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': + resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - "@oxc-parser/binding-linux-riscv64-gnu@0.120.0": - resolution: - { - integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': + resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - "@oxc-parser/binding-linux-riscv64-musl@0.120.0": - resolution: - { - integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': + resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - "@oxc-parser/binding-linux-s390x-gnu@0.120.0": - resolution: - { - integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': + resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - "@oxc-parser/binding-linux-x64-gnu@0.120.0": - resolution: - { - integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-x64-gnu@0.120.0': + resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - "@oxc-parser/binding-linux-x64-musl@0.120.0": - resolution: - { - integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-linux-x64-musl@0.120.0': + resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - "@oxc-parser/binding-openharmony-arm64@0.120.0": - resolution: - { - integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-openharmony-arm64@0.120.0': + resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - "@oxc-parser/binding-wasm32-wasi@0.120.0": - resolution: - { - integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==, - } - engines: { node: ">=14.0.0" } + '@oxc-parser/binding-wasm32-wasi@0.120.0': + resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==} + engines: {node: '>=14.0.0'} cpu: [wasm32] - "@oxc-parser/binding-win32-arm64-msvc@0.120.0": - resolution: - { - integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': + resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - "@oxc-parser/binding-win32-ia32-msvc@0.120.0": - resolution: - { - integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': + resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - "@oxc-parser/binding-win32-x64-msvc@0.120.0": - resolution: - { - integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@oxc-parser/binding-win32-x64-msvc@0.120.0': + resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - "@oxc-project/types@0.120.0": - resolution: - { - integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==, - } - - "@oxc-resolver/binding-android-arm-eabi@11.19.1": - resolution: - { - integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==, - } + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} + + '@oxc-resolver/binding-android-arm-eabi@11.19.1': + resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} cpu: [arm] os: [android] - "@oxc-resolver/binding-android-arm64@11.19.1": - resolution: - { - integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==, - } + '@oxc-resolver/binding-android-arm64@11.19.1': + resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} cpu: [arm64] os: [android] - "@oxc-resolver/binding-darwin-arm64@11.19.1": - resolution: - { - integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==, - } + '@oxc-resolver/binding-darwin-arm64@11.19.1': + resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} cpu: [arm64] os: [darwin] - "@oxc-resolver/binding-darwin-x64@11.19.1": - resolution: - { - integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==, - } + '@oxc-resolver/binding-darwin-x64@11.19.1': + resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} cpu: [x64] os: [darwin] - "@oxc-resolver/binding-freebsd-x64@11.19.1": - resolution: - { - integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==, - } + '@oxc-resolver/binding-freebsd-x64@11.19.1': + resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} cpu: [x64] os: [freebsd] - "@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1": - resolution: - { - integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==, - } + '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': + resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} cpu: [arm] os: [linux] - "@oxc-resolver/binding-linux-arm-musleabihf@11.19.1": - resolution: - { - integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==, - } + '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': + resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} cpu: [arm] os: [linux] - "@oxc-resolver/binding-linux-arm64-gnu@11.19.1": - resolution: - { - integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==, - } + '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': + resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} cpu: [arm64] os: [linux] libc: [glibc] - "@oxc-resolver/binding-linux-arm64-musl@11.19.1": - resolution: - { - integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==, - } + '@oxc-resolver/binding-linux-arm64-musl@11.19.1': + resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} cpu: [arm64] os: [linux] libc: [musl] - "@oxc-resolver/binding-linux-ppc64-gnu@11.19.1": - resolution: - { - integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==, - } + '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': + resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} cpu: [ppc64] os: [linux] libc: [glibc] - "@oxc-resolver/binding-linux-riscv64-gnu@11.19.1": - resolution: - { - integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==, - } + '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': + resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} cpu: [riscv64] os: [linux] libc: [glibc] - "@oxc-resolver/binding-linux-riscv64-musl@11.19.1": - resolution: - { - integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==, - } + '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': + resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} cpu: [riscv64] os: [linux] libc: [musl] - "@oxc-resolver/binding-linux-s390x-gnu@11.19.1": - resolution: - { - integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==, - } + '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': + resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} cpu: [s390x] os: [linux] libc: [glibc] - "@oxc-resolver/binding-linux-x64-gnu@11.19.1": - resolution: - { - integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==, - } + '@oxc-resolver/binding-linux-x64-gnu@11.19.1': + resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} cpu: [x64] os: [linux] libc: [glibc] - "@oxc-resolver/binding-linux-x64-musl@11.19.1": - resolution: - { - integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==, - } + '@oxc-resolver/binding-linux-x64-musl@11.19.1': + resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} cpu: [x64] os: [linux] libc: [musl] - "@oxc-resolver/binding-openharmony-arm64@11.19.1": - resolution: - { - integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==, - } + '@oxc-resolver/binding-openharmony-arm64@11.19.1': + resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} cpu: [arm64] os: [openharmony] - "@oxc-resolver/binding-wasm32-wasi@11.19.1": - resolution: - { - integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==, - } - engines: { node: ">=14.0.0" } + '@oxc-resolver/binding-wasm32-wasi@11.19.1': + resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==} + engines: {node: '>=14.0.0'} cpu: [wasm32] - "@oxc-resolver/binding-win32-arm64-msvc@11.19.1": - resolution: - { - integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==, - } + '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': + resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==} cpu: [arm64] os: [win32] - "@oxc-resolver/binding-win32-ia32-msvc@11.19.1": - resolution: - { - integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==, - } + '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': + resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==} cpu: [ia32] os: [win32] - "@oxc-resolver/binding-win32-x64-msvc@11.19.1": - resolution: - { - integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==, - } + '@oxc-resolver/binding-win32-x64-msvc@11.19.1': + resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==} cpu: [x64] os: [win32] - "@phc/format@1.0.0": - resolution: - { - integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==, - } - engines: { node: ">=10" } - - "@pinojs/redact@0.4.0": - resolution: - { - integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==, - } - - "@rollup/rollup-android-arm-eabi@4.60.0": - resolution: - { - integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==, - } + '@phc/format@1.0.0': + resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} + engines: {node: '>=10'} + + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + + '@rollup/rollup-android-arm-eabi@4.60.0': + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} cpu: [arm] os: [android] - "@rollup/rollup-android-arm64@4.60.0": - resolution: - { - integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==, - } + '@rollup/rollup-android-arm64@4.60.0': + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} cpu: [arm64] os: [android] - "@rollup/rollup-darwin-arm64@4.60.0": - resolution: - { - integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==, - } + '@rollup/rollup-darwin-arm64@4.60.0': + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} cpu: [arm64] os: [darwin] - "@rollup/rollup-darwin-x64@4.60.0": - resolution: - { - integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==, - } + '@rollup/rollup-darwin-x64@4.60.0': + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} cpu: [x64] os: [darwin] - "@rollup/rollup-freebsd-arm64@4.60.0": - resolution: - { - integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==, - } + '@rollup/rollup-freebsd-arm64@4.60.0': + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} cpu: [arm64] os: [freebsd] - "@rollup/rollup-freebsd-x64@4.60.0": - resolution: - { - integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==, - } + '@rollup/rollup-freebsd-x64@4.60.0': + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} cpu: [x64] os: [freebsd] - "@rollup/rollup-linux-arm-gnueabihf@4.60.0": - resolution: - { - integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==, - } + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} cpu: [arm] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm-musleabihf@4.60.0": - resolution: - { - integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==, - } + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} cpu: [arm] os: [linux] libc: [musl] - "@rollup/rollup-linux-arm64-gnu@4.60.0": - resolution: - { - integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==, - } + '@rollup/rollup-linux-arm64-gnu@4.60.0': + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} cpu: [arm64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm64-musl@4.60.0": - resolution: - { - integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==, - } + '@rollup/rollup-linux-arm64-musl@4.60.0': + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} cpu: [arm64] os: [linux] libc: [musl] - "@rollup/rollup-linux-loong64-gnu@4.60.0": - resolution: - { - integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==, - } + '@rollup/rollup-linux-loong64-gnu@4.60.0': + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} cpu: [loong64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-loong64-musl@4.60.0": - resolution: - { - integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==, - } + '@rollup/rollup-linux-loong64-musl@4.60.0': + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} cpu: [loong64] os: [linux] libc: [musl] - "@rollup/rollup-linux-ppc64-gnu@4.60.0": - resolution: - { - integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==, - } + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} cpu: [ppc64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-ppc64-musl@4.60.0": - resolution: - { - integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==, - } + '@rollup/rollup-linux-ppc64-musl@4.60.0': + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} cpu: [ppc64] os: [linux] libc: [musl] - "@rollup/rollup-linux-riscv64-gnu@4.60.0": - resolution: - { - integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==, - } + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} cpu: [riscv64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-riscv64-musl@4.60.0": - resolution: - { - integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==, - } + '@rollup/rollup-linux-riscv64-musl@4.60.0': + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} cpu: [riscv64] os: [linux] libc: [musl] - "@rollup/rollup-linux-s390x-gnu@4.60.0": - resolution: - { - integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==, - } + '@rollup/rollup-linux-s390x-gnu@4.60.0': + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} cpu: [s390x] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-gnu@4.60.0": - resolution: - { - integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==, - } + '@rollup/rollup-linux-x64-gnu@4.60.0': + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} cpu: [x64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-musl@4.60.0": - resolution: - { - integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==, - } + '@rollup/rollup-linux-x64-musl@4.60.0': + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} cpu: [x64] os: [linux] libc: [musl] - "@rollup/rollup-openbsd-x64@4.60.0": - resolution: - { - integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==, - } + '@rollup/rollup-openbsd-x64@4.60.0': + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} cpu: [x64] os: [openbsd] - "@rollup/rollup-openharmony-arm64@4.60.0": - resolution: - { - integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==, - } + '@rollup/rollup-openharmony-arm64@4.60.0': + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} cpu: [arm64] os: [openharmony] - "@rollup/rollup-win32-arm64-msvc@4.60.0": - resolution: - { - integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==, - } + '@rollup/rollup-win32-arm64-msvc@4.60.0': + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} cpu: [arm64] os: [win32] - "@rollup/rollup-win32-ia32-msvc@4.60.0": - resolution: - { - integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==, - } + '@rollup/rollup-win32-ia32-msvc@4.60.0': + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} cpu: [ia32] os: [win32] - "@rollup/rollup-win32-x64-gnu@4.60.0": - resolution: - { - integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==, - } + '@rollup/rollup-win32-x64-gnu@4.60.0': + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} cpu: [x64] os: [win32] - "@rollup/rollup-win32-x64-msvc@4.60.0": - resolution: - { - integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==, - } + '@rollup/rollup-win32-x64-msvc@4.60.0': + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} cpu: [x64] os: [win32] - "@simple-libs/child-process-utils@1.0.2": - resolution: - { - integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==, - } - engines: { node: ">=18" } - - "@simple-libs/stream-utils@1.2.0": - resolution: - { - integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==, - } - engines: { node: ">=18" } - - "@standard-schema/spec@1.1.0": - resolution: - { - integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, - } - - "@swc/helpers@0.5.15": - resolution: - { - integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==, - } - - "@tailwindcss/node@4.2.2": - resolution: - { - integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==, - } - - "@tailwindcss/oxide-android-arm64@4.2.2": - resolution: - { - integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==, - } - engines: { node: ">= 20" } + '@simple-libs/child-process-utils@1.0.2': + resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} + engines: {node: '>=18'} + + '@simple-libs/stream-utils@1.2.0': + resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} + engines: {node: '>=18'} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + engines: {node: '>= 20'} cpu: [arm64] os: [android] - "@tailwindcss/oxide-darwin-arm64@4.2.2": - resolution: - { - integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - "@tailwindcss/oxide-darwin-x64@4.2.2": - resolution: - { - integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + engines: {node: '>= 20'} cpu: [x64] os: [darwin] - "@tailwindcss/oxide-freebsd-x64@4.2.2": - resolution: - { - integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": - resolution: - { - integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + engines: {node: '>= 20'} cpu: [arm] os: [linux] - "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": - resolution: - { - integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-arm64-musl@4.2.2": - resolution: - { - integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - "@tailwindcss/oxide-linux-x64-gnu@4.2.2": - resolution: - { - integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-x64-musl@4.2.2": - resolution: - { - integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - "@tailwindcss/oxide-wasm32-wasi@4.2.2": - resolution: - { - integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==, - } - engines: { node: ">=14.0.0" } + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: - - "@napi-rs/wasm-runtime" - - "@emnapi/core" - - "@emnapi/runtime" - - "@tybys/wasm-util" - - "@emnapi/wasi-threads" + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' - tslib - "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": - resolution: - { - integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + engines: {node: '>= 20'} cpu: [arm64] os: [win32] - "@tailwindcss/oxide-win32-x64-msvc@4.2.2": - resolution: - { - integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + engines: {node: '>= 20'} cpu: [x64] os: [win32] - "@tailwindcss/oxide@4.2.2": - resolution: - { - integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==, - } - engines: { node: ">= 20" } - - "@tailwindcss/postcss@4.2.2": - resolution: - { - integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==, - } - - "@tailwindcss/typography@0.5.19": - resolution: - { - integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==, - } + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.2.2': + resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} peerDependencies: - tailwindcss: ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" - - "@tanstack/query-core@5.95.2": - resolution: - { - integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==, - } - - "@tanstack/react-query@5.95.2": - resolution: - { - integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==, - } + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tanstack/query-core@5.95.2': + resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==} + + '@tanstack/react-query@5.95.2': + resolution: {integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==} peerDependencies: react: ^18 || ^19 - "@testing-library/dom@10.4.1": - resolution: - { - integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==, - } - engines: { node: ">=18" } - - "@testing-library/jest-dom@6.9.1": - resolution: - { - integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==, - } - engines: { node: ">=14", npm: ">=6", yarn: ">=1" } - - "@testing-library/react@16.3.2": - resolution: - { - integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==, - } - engines: { node: ">=18" } + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} peerDependencies: - "@testing-library/dom": ^10.0.0 - "@types/react": ^18.0.0 || ^19.0.0 - "@types/react-dom": ^18.0.0 || ^19.0.0 + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@testing-library/user-event@14.6.1": - resolution: - { - integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==, - } - engines: { node: ">=12", npm: ">=6" } + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} peerDependencies: - "@testing-library/dom": ">=7.21.4" - - "@tybys/wasm-util@0.10.1": - resolution: - { - integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, - } - - "@types/aria-query@5.0.4": - resolution: - { - integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, - } - - "@types/chai@5.2.3": - resolution: - { - integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, - } - - "@types/debug@4.1.12": - resolution: - { - integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==, - } - - "@types/deep-eql@4.0.2": - resolution: - { - integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, - } - - "@types/estree-jsx@1.0.5": - resolution: - { - integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==, - } - - "@types/estree@1.0.8": - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } - - "@types/hast@3.0.4": - resolution: - { - integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==, - } - - "@types/mdast@4.0.4": - resolution: - { - integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==, - } - - "@types/minimist@1.2.5": - resolution: - { - integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==, - } - - "@types/ms@2.1.0": - resolution: - { - integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, - } - - "@types/node-cron@3.0.11": - resolution: - { - integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==, - } - - "@types/node@25.5.0": - resolution: - { - integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==, - } - - "@types/normalize-package-data@2.4.4": - resolution: - { - integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, - } - - "@types/react-dom@19.2.3": - resolution: - { - integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==, - } + '@testing-library/dom': '>=7.21.4' + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node-cron@3.0.11': + resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==} + + '@types/node@25.5.0': + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - "@types/react": ^19.2.0 - - "@types/react@19.2.14": - resolution: - { - integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==, - } - - "@types/unist@2.0.11": - resolution: - { - integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==, - } - - "@types/unist@3.0.3": - resolution: - { - integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==, - } - - "@ungap/structured-clone@1.3.0": - resolution: - { - integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==, - } - - "@vitest/expect@4.1.1": - resolution: - { - integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==, - } - - "@vitest/mocker@4.1.1": - resolution: - { - integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==, - } + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitest/expect@4.1.1': + resolution: {integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==} + + '@vitest/mocker@4.1.1': + resolution: {integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2164,788 +1443,470 @@ packages: vite: optional: true - "@vitest/pretty-format@4.1.1": - resolution: - { - integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==, - } - - "@vitest/runner@4.1.1": - resolution: - { - integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==, - } - - "@vitest/snapshot@4.1.1": - resolution: - { - integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==, - } - - "@vitest/spy@4.1.1": - resolution: - { - integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==, - } - - "@vitest/utils@4.1.1": - resolution: - { - integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==, - } + '@vitest/pretty-format@4.1.1': + resolution: {integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==} + + '@vitest/runner@4.1.1': + resolution: {integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==} + + '@vitest/snapshot@4.1.1': + resolution: {integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==} + + '@vitest/spy@4.1.1': + resolution: {integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==} + + '@vitest/utils@4.1.1': + resolution: {integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==} JSONStream@1.3.5: - resolution: - { - integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, - } + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true add-stream@1.0.0: - resolution: - { - integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==, - } + resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} agent-base@8.0.0: - resolution: - { - integrity: sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==} + engines: {node: '>= 14'} ajv@8.18.0: - resolution: - { - integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==, - } + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: - { - integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} argon2@0.44.0: - resolution: - { - integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==, - } - engines: { node: ">=16.17.0" } + resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} + engines: {node: '>=16.17.0'} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-query@5.3.0: - resolution: - { - integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, - } + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} aria-query@5.3.2: - resolution: - { - integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-ify@1.0.0: - resolution: - { - integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==, - } + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} arrify@1.0.1: - resolution: - { - integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} atomic-sleep@1.0.0: - resolution: - { - integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==, - } - engines: { node: ">=8.0.0" } + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} bail@2.0.2: - resolution: - { - integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==, - } + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} baseline-browser-mapping@2.10.9: - resolution: - { - integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==, - } - engines: { node: ">=6.0.0" } + resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} + engines: {node: '>=6.0.0'} hasBin: true bidi-js@1.0.3: - resolution: - { - integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, - } + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} brace-expansion@1.1.12: - resolution: - { - integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, - } + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} camelcase-keys@6.2.2: - resolution: - { - integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} camelcase@5.3.1: - resolution: - { - integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} caniuse-lite@1.0.30001780: - resolution: - { - integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==, - } + resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} ccount@2.0.1: - resolution: - { - integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==, - } + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} chai@6.2.2: - resolution: - { - integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} character-entities-html4@2.1.0: - resolution: - { - integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==, - } + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} character-entities-legacy@3.0.0: - resolution: - { - integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==, - } + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} character-entities@2.0.2: - resolution: - { - integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==, - } + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} character-reference-invalid@2.0.1: - resolution: - { - integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==, - } + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} class-variance-authority@0.7.1: - resolution: - { - integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, - } + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} claygl@1.3.0: - resolution: - { - integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==, - } + resolution: {integrity: sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ==} client-only@0.0.1: - resolution: - { - integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, - } + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} cliui@7.0.4: - resolution: - { - integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, - } + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} cliui@8.0.1: - resolution: - { - integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} clsx@2.1.1: - resolution: - { - integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, - } + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: ">=7.0.0" } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, - } + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} colorette@2.0.20: - resolution: - { - integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, - } + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} comma-separated-tokens@2.0.3: - resolution: - { - integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==, - } + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} commit-and-tag-version@12.7.1: - resolution: - { - integrity: sha512-18+iV9VMPWQ5rryn1nKprvkwqFMx1eOcEwIprjgyGm8Blhsw7WnsFXgyYLIlBF4uG2fcbbps8P7fBZARvaA0VA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-18+iV9VMPWQ5rryn1nKprvkwqFMx1eOcEwIprjgyGm8Blhsw7WnsFXgyYLIlBF4uG2fcbbps8P7fBZARvaA0VA==} + engines: {node: '>=18'} hasBin: true compare-func@2.0.0: - resolution: - { - integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==, - } + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@2.0.0: - resolution: - { - integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==, - } - engines: { "0": node >= 6.0 } + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} conventional-changelog-angular@6.0.0: - resolution: - { - integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==} + engines: {node: '>=14'} conventional-changelog-angular@8.3.0: - resolution: - { - integrity: sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==} + engines: {node: '>=18'} conventional-changelog-atom@3.0.0: - resolution: - { - integrity: sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g==} + engines: {node: '>=14'} conventional-changelog-codemirror@3.0.0: - resolution: - { - integrity: sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw==} + engines: {node: '>=14'} conventional-changelog-config-spec@2.1.0: - resolution: - { - integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==, - } + resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==} conventional-changelog-conventionalcommits@6.1.0: - resolution: - { - integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==} + engines: {node: '>=14'} conventional-changelog-conventionalcommits@9.3.0: - resolution: - { - integrity: sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-kYFx6gAyjSIMwNtASkI3ZE99U1fuVDJr0yTYgVy+I2QG46zNZfl2her+0+eoviG82c5WQvW1jMt1eOQTeJLodA==} + engines: {node: '>=18'} conventional-changelog-core@5.0.2: - resolution: - { - integrity: sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug==} + engines: {node: '>=14'} conventional-changelog-ember@3.0.0: - resolution: - { - integrity: sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A==} + engines: {node: '>=14'} conventional-changelog-eslint@4.0.0: - resolution: - { - integrity: sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q==} + engines: {node: '>=14'} conventional-changelog-express@3.0.0: - resolution: - { - integrity: sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA==} + engines: {node: '>=14'} conventional-changelog-jquery@4.0.0: - resolution: - { - integrity: sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw==} + engines: {node: '>=14'} conventional-changelog-jshint@3.0.0: - resolution: - { - integrity: sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA==} + engines: {node: '>=14'} conventional-changelog-preset-loader@3.0.0: - resolution: - { - integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} + engines: {node: '>=14'} conventional-changelog-writer@6.0.1: - resolution: - { - integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==} + engines: {node: '>=14'} hasBin: true conventional-changelog@4.0.0: - resolution: - { - integrity: sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ==} + engines: {node: '>=14'} conventional-commits-filter@3.0.0: - resolution: - { - integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==} + engines: {node: '>=14'} conventional-commits-parser@4.0.0: - resolution: - { - integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} + engines: {node: '>=14'} hasBin: true conventional-commits-parser@6.3.0: - resolution: - { - integrity: sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==} + engines: {node: '>=18'} hasBin: true conventional-recommended-bump@7.0.1: - resolution: - { - integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} + engines: {node: '>=14'} hasBin: true convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} core-util-is@1.0.3: - resolution: - { - integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, - } + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cosmiconfig-typescript-loader@6.2.0: - resolution: - { - integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==, - } - engines: { node: ">=v18" } + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} peerDependencies: - "@types/node": "*" - cosmiconfig: ">=9" - typescript: ">=5" + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' cosmiconfig@9.0.1: - resolution: - { - integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} + engines: {node: '>=14'} peerDependencies: - typescript: ">=4.9.5" + typescript: '>=4.9.5' peerDependenciesMeta: typescript: optional: true cross-env@10.1.0: - resolution: - { - integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} hasBin: true cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} css-tree@3.2.1: - resolution: - { - integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css.escape@1.5.1: - resolution: - { - integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, - } + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} dargs@7.0.0: - resolution: - { - integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} data-urls@7.0.0: - resolution: - { - integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} dateformat@3.0.3: - resolution: - { - integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==, - } + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} dateformat@4.6.3: - resolution: - { - integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==, - } + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: - supports-color: "*" + supports-color: '*' peerDependenciesMeta: supports-color: optional: true decamelize-keys@1.1.1: - resolution: - { - integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} decamelize@1.2.0: - resolution: - { - integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} decimal.js@10.6.0: - resolution: - { - integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, - } + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} decode-named-character-reference@1.3.0: - resolution: - { - integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==, - } + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} dequal@2.0.3: - resolution: - { - integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} detect-indent@6.1.0: - resolution: - { - integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} detect-libc@2.1.2: - resolution: - { - integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} detect-newline@3.1.0: - resolution: - { - integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} devlop@1.1.0: - resolution: - { - integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==, - } + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} dom-accessibility-api@0.5.16: - resolution: - { - integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, - } + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dom-accessibility-api@0.6.3: - resolution: - { - integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==, - } + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} dot-prop@5.3.0: - resolution: - { - integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} dotenv@17.3.1: - resolution: - { - integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + engines: {node: '>=12'} dotgitignore@2.1.0: - resolution: - { - integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} + engines: {node: '>=6'} drizzle-kit@0.31.10: - resolution: - { - integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==, - } + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} hasBin: true drizzle-orm@0.45.1: - resolution: - { - integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==, - } + resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==} peerDependencies: - "@aws-sdk/client-rds-data": ">=3" - "@cloudflare/workers-types": ">=4" - "@electric-sql/pglite": ">=0.2.0" - "@libsql/client": ">=0.10.0" - "@libsql/client-wasm": ">=0.10.0" - "@neondatabase/serverless": ">=0.10.0" - "@op-engineering/op-sqlite": ">=2" - "@opentelemetry/api": ^1.4.1 - "@planetscale/database": ">=1.13" - "@prisma/client": "*" - "@tidbcloud/serverless": "*" - "@types/better-sqlite3": "*" - "@types/pg": "*" - "@types/sql.js": "*" - "@upstash/redis": ">=1.34.7" - "@vercel/postgres": ">=0.8.0" - "@xata.io/client": "*" - better-sqlite3: ">=7" - bun-types: "*" - expo-sqlite: ">=14.0.0" - gel: ">=2" - knex: "*" - kysely: "*" - mysql2: ">=2" - pg: ">=8" - postgres: ">=3" - prisma: "*" - sql.js: ">=1" - sqlite3: ">=5" + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' peerDependenciesMeta: - "@aws-sdk/client-rds-data": + '@aws-sdk/client-rds-data': optional: true - "@cloudflare/workers-types": + '@cloudflare/workers-types': optional: true - "@electric-sql/pglite": + '@electric-sql/pglite': optional: true - "@libsql/client": + '@libsql/client': optional: true - "@libsql/client-wasm": + '@libsql/client-wasm': optional: true - "@neondatabase/serverless": + '@neondatabase/serverless': optional: true - "@op-engineering/op-sqlite": + '@op-engineering/op-sqlite': optional: true - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@planetscale/database": + '@planetscale/database': optional: true - "@prisma/client": + '@prisma/client': optional: true - "@tidbcloud/serverless": + '@tidbcloud/serverless': optional: true - "@types/better-sqlite3": + '@types/better-sqlite3': optional: true - "@types/pg": + '@types/pg': optional: true - "@types/sql.js": + '@types/sql.js': optional: true - "@upstash/redis": + '@upstash/redis': optional: true - "@vercel/postgres": + '@vercel/postgres': optional: true - "@xata.io/client": + '@xata.io/client': optional: true better-sqlite3: optional: true @@ -2973,198 +1934,111 @@ packages: optional: true echarts-for-react@3.0.6: - resolution: - { - integrity: sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==, - } + resolution: {integrity: sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==} peerDependencies: echarts: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 react: ^15.0.0 || >=16.0.0 echarts-gl@2.0.9: - resolution: - { - integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==, - } + resolution: {integrity: sha512-oKeMdkkkpJGWOzjgZUsF41DOh6cMsyrGGXimbjK2l6Xeq/dBQu4ShG2w2Dzrs/1bD27b2pLTGSaUzouY191gzA==} peerDependencies: echarts: ^5.1.2 echarts@6.0.0: - resolution: - { - integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==, - } + resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==} emoji-picker-react@4.18.0: - resolution: - { - integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==} + engines: {node: '>=10'} peerDependencies: - react: ">=16" + react: '>=16' emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} end-of-stream@1.4.5: - resolution: - { - integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, - } + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} enhanced-resolve@5.20.1: - resolution: - { - integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==, - } - engines: { node: ">=10.13.0" } + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} entities@6.0.1: - resolution: - { - integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, - } - engines: { node: ">=0.12" } + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} env-paths@2.2.1: - resolution: - { - integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} error-ex@1.3.4: - resolution: - { - integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, - } + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-module-lexer@2.0.0: - resolution: - { - integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==, - } + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} esbuild@0.27.4: - resolution: - { - integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: ">=0.8.0" } + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} escape-string-regexp@5.0.0: - resolution: - { - integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} estree-util-is-identifier-name@3.0.0: - resolution: - { - integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==, - } + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} expect-type@1.3.0: - resolution: - { - integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} extend@3.0.2: - resolution: - { - integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, - } + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} fast-copy@4.0.2: - resolution: - { - integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==, - } + resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-glob@3.3.3: - resolution: - { - integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, - } - engines: { node: ">=8.6.0" } + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} fast-safe-stringify@2.1.1: - resolution: - { - integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==, - } + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} fast-uri@3.1.0: - resolution: - { - integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, - } + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fast-xml-builder@1.1.4: - resolution: - { - integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==, - } + resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} fast-xml-parser@5.5.7: - resolution: - { - integrity: sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==, - } + resolution: {integrity: sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==} hasBin: true fastq@1.20.1: - resolution: - { - integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, - } + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fd-package-json@2.0.0: - resolution: - { - integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==, - } + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -3172,451 +2046,253 @@ packages: optional: true figures@3.2.0: - resolution: - { - integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} find-up@2.1.0: - resolution: - { - integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} find-up@3.0.0: - resolution: - { - integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} find-up@4.1.0: - resolution: - { - integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} flairup@1.0.0: - resolution: - { - integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==, - } + resolution: {integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==} formatly@0.3.0: - resolution: - { - integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==, - } - engines: { node: ">=18.3.0" } + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} hasBin: true fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} get-pkg-repo@4.2.1: - resolution: - { - integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==, - } - engines: { node: ">=6.9.0" } + resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} + engines: {node: '>=6.9.0'} hasBin: true get-tsconfig@4.13.6: - resolution: - { - integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==, - } + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} get-tsconfig@4.13.7: - resolution: - { - integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==, - } + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} git-raw-commits@3.0.0: - resolution: - { - integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} + engines: {node: '>=14'} deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true git-raw-commits@5.0.1: - resolution: - { - integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} + engines: {node: '>=18'} hasBin: true git-remote-origin-url@2.0.0: - resolution: - { - integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} + engines: {node: '>=4'} git-semver-tags@5.0.1: - resolution: - { - integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} + engines: {node: '>=14'} deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true gitconfiglocal@1.0.0: - resolution: - { - integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==, - } + resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: ">= 6" } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} global-directory@4.0.1: - resolution: - { - integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} handlebars@4.7.8: - resolution: - { - integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==, - } - engines: { node: ">=0.4.7" } + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} hasBin: true hard-rejection@2.1.0: - resolution: - { - integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} hasown@2.0.2: - resolution: - { - integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} hast-util-to-jsx-runtime@2.3.6: - resolution: - { - integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==, - } + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} hast-util-whitespace@3.0.0: - resolution: - { - integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==, - } + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} help-me@5.0.0: - resolution: - { - integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==, - } + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} hosted-git-info@2.8.9: - resolution: - { - integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, - } + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} hosted-git-info@4.1.0: - resolution: - { - integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} html-encoding-sniffer@6.0.0: - resolution: - { - integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} html-url-attributes@3.0.1: - resolution: - { - integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==, - } + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} https-proxy-agent@8.0.0: - resolution: - { - integrity: sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==} + engines: {node: '>= 14'} husky@9.1.7: - resolution: - { - integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} hasBin: true import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} import-meta-resolve@4.2.0: - resolution: - { - integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==, - } + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} indent-string@4.0.0: - resolution: - { - integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} inherits@2.0.4: - resolution: - { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, - } + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: - resolution: - { - integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, - } + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} ini@4.1.1: - resolution: - { - integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} inline-style-parser@0.2.7: - resolution: - { - integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==, - } + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} ip-address@10.1.0: - resolution: - { - integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==, - } - engines: { node: ">= 12" } + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} is-alphabetical@2.0.1: - resolution: - { - integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==, - } + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} is-alphanumerical@2.0.1: - resolution: - { - integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==, - } + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} is-arrayish@0.2.1: - resolution: - { - integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, - } + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-core-module@2.16.1: - resolution: - { - integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} is-decimal@2.0.1: - resolution: - { - integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==, - } + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-hexadecimal@2.0.1: - resolution: - { - integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==, - } + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: ">=0.12.0" } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} is-obj@2.0.0: - resolution: - { - integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} is-plain-obj@1.1.0: - resolution: - { - integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} is-plain-obj@4.1.0: - resolution: - { - integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} is-potential-custom-element-name@1.0.1: - resolution: - { - integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, - } + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} is-text-path@1.0.1: - resolution: - { - integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} isarray@1.0.0: - resolution: - { - integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, - } + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} jiti@2.6.1: - resolution: - { - integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, - } + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true jose@6.2.2: - resolution: - { - integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==, - } + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} joycon@3.1.1: - resolution: - { - integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@4.1.1: - resolution: - { - integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, - } + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsdom@29.0.1: - resolution: - { - integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24.0.0 } + resolution: {integrity: sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -3624,660 +2300,369 @@ packages: optional: true json-parse-better-errors@1.0.2: - resolution: - { - integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, - } + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: - resolution: - { - integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, - } + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-schema-traverse@1.0.0: - resolution: - { - integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, - } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json-stringify-safe@5.0.1: - resolution: - { - integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==, - } + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} jsonparse@1.3.1: - resolution: - { - integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, - } - engines: { "0": node >= 0.2.0 } + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} kind-of@6.0.3: - resolution: - { - integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, - } - engines: { node: ">=0.10.0" } - - knip@6.0.5: - resolution: - { - integrity: sha512-+i9e/ZKuYlECB5iIK82NQwnYso4oNLBhzsTbXhSqCG1qfGi6D84GNtRENafmS3C0lABX8Wf3BKM434nPXi2AbQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + knip@6.0.6: + resolution: {integrity: sha512-PA+r1mTDLHH3eShlffn2ZDyH1hHvmgDj7JsTP3JKuhV/jZTyHbRkGcOd+uaSxfJZmcZyOE5zw3naP33WllTIlA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true lightningcss-android-arm64@1.32.0: - resolution: - { - integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: - { - integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: - { - integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: - { - integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: - { - integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: - { - integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: - { - integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: - { - integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: - { - integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: - { - integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: - { - integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.32.0: - resolution: - { - integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} load-json-file@4.0.0: - resolution: - { - integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} locate-path@2.0.0: - resolution: - { - integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} locate-path@3.0.0: - resolution: - { - integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} locate-path@5.0.0: - resolution: - { - integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.camelcase@4.3.0: - resolution: - { - integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==, - } + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} lodash.ismatch@4.4.0: - resolution: - { - integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==, - } + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} lodash.kebabcase@4.1.1: - resolution: - { - integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==, - } + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} lodash.mergewith@4.6.2: - resolution: - { - integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==, - } + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} lodash.snakecase@4.1.1: - resolution: - { - integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==, - } + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} lodash.startcase@4.4.0: - resolution: - { - integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==, - } + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} lodash.upperfirst@4.3.1: - resolution: - { - integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==, - } + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} longest-streak@3.1.0: - resolution: - { - integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==, - } + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} lru-cache@11.2.7: - resolution: - { - integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} lru-cache@6.0.0: - resolution: - { - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} lz-string@1.5.0: - resolution: - { - integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, - } + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} map-obj@1.0.1: - resolution: - { - integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} map-obj@4.3.0: - resolution: - { - integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} markdown-table@3.0.4: - resolution: - { - integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==, - } + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} mdast-util-find-and-replace@3.0.2: - resolution: - { - integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==, - } + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} mdast-util-from-markdown@2.0.3: - resolution: - { - integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==, - } + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} mdast-util-gfm-autolink-literal@2.0.1: - resolution: - { - integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==, - } + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} mdast-util-gfm-footnote@2.1.0: - resolution: - { - integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==, - } + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} mdast-util-gfm-strikethrough@2.0.0: - resolution: - { - integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==, - } + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} mdast-util-gfm-table@2.0.0: - resolution: - { - integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==, - } + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} mdast-util-gfm-task-list-item@2.0.0: - resolution: - { - integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==, - } + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} mdast-util-gfm@3.1.0: - resolution: - { - integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==, - } + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} mdast-util-mdx-expression@2.0.1: - resolution: - { - integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==, - } + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} mdast-util-mdx-jsx@3.2.0: - resolution: - { - integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==, - } + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} mdast-util-mdxjs-esm@2.0.1: - resolution: - { - integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==, - } + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} mdast-util-phrasing@4.1.0: - resolution: - { - integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==, - } + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} mdast-util-to-hast@13.2.1: - resolution: - { - integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==, - } + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} mdast-util-to-markdown@2.1.2: - resolution: - { - integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==, - } + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} mdast-util-to-string@4.0.0: - resolution: - { - integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==, - } + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} mdn-data@2.27.1: - resolution: - { - integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, - } + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} meow@13.2.0: - resolution: - { - integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} meow@8.1.2: - resolution: - { - integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} micromark-core-commonmark@2.0.3: - resolution: - { - integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==, - } + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} micromark-extension-gfm-autolink-literal@2.1.0: - resolution: - { - integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==, - } + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} micromark-extension-gfm-footnote@2.1.0: - resolution: - { - integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==, - } + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} micromark-extension-gfm-strikethrough@2.1.0: - resolution: - { - integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==, - } + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} micromark-extension-gfm-table@2.1.1: - resolution: - { - integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==, - } + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} micromark-extension-gfm-tagfilter@2.0.0: - resolution: - { - integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==, - } + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} micromark-extension-gfm-task-list-item@2.1.0: - resolution: - { - integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==, - } + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} micromark-extension-gfm@3.0.0: - resolution: - { - integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==, - } + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} micromark-factory-destination@2.0.1: - resolution: - { - integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==, - } + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} micromark-factory-label@2.0.1: - resolution: - { - integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==, - } + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} micromark-factory-space@2.0.1: - resolution: - { - integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==, - } + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} micromark-factory-title@2.0.1: - resolution: - { - integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==, - } + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} micromark-factory-whitespace@2.0.1: - resolution: - { - integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==, - } + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} micromark-util-character@2.1.1: - resolution: - { - integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==, - } + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} micromark-util-chunked@2.0.1: - resolution: - { - integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==, - } + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} micromark-util-classify-character@2.0.1: - resolution: - { - integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==, - } + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} micromark-util-combine-extensions@2.0.1: - resolution: - { - integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==, - } + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} micromark-util-decode-numeric-character-reference@2.0.2: - resolution: - { - integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==, - } + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} micromark-util-decode-string@2.0.1: - resolution: - { - integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==, - } + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} micromark-util-encode@2.0.1: - resolution: - { - integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==, - } + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} micromark-util-html-tag-name@2.0.1: - resolution: - { - integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==, - } + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} micromark-util-normalize-identifier@2.0.1: - resolution: - { - integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==, - } + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} micromark-util-resolve-all@2.0.1: - resolution: - { - integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==, - } + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} micromark-util-sanitize-uri@2.0.1: - resolution: - { - integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==, - } + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} micromark-util-subtokenize@2.1.0: - resolution: - { - integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==, - } + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} micromark-util-symbol@2.0.1: - resolution: - { - integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==, - } + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} micromark-util-types@2.0.2: - resolution: - { - integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==, - } + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} micromark@4.0.2: - resolution: - { - integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==, - } + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: ">=8.6" } + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} min-indent@1.0.1: - resolution: - { - integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimatch@3.1.5: - resolution: - { - integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, - } + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} minimist-options@4.1.0: - resolution: - { - integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, - } - engines: { node: ">= 6" } + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} modify-values@1.0.1: - resolution: - { - integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true neo-async@2.6.2: - resolution: - { - integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, - } + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} next@16.2.1: - resolution: - { - integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==, - } - engines: { node: ">=20.9.0" } + resolution: {integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: - "@opentelemetry/api": ^1.1.0 - "@playwright/test": ^1.51.1 - babel-plugin-react-compiler: "*" + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@playwright/test": + '@playwright/test': optional: true babel-plugin-react-compiler: optional: true @@ -4285,1160 +2670,646 @@ packages: optional: true node-addon-api@8.6.0: - resolution: - { - integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==, - } - engines: { node: ^18 || ^20 || >= 21 } + resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} + engines: {node: ^18 || ^20 || >= 21} node-cron@4.2.1: - resolution: - { - integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==, - } - engines: { node: ">=6.0.0" } + resolution: {integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==} + engines: {node: '>=6.0.0'} node-gyp-build@4.8.4: - resolution: - { - integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==, - } + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true normalize-package-data@2.5.0: - resolution: - { - integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, - } + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} normalize-package-data@3.0.3: - resolution: - { - integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} obug@2.1.1: - resolution: - { - integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==, - } + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} on-exit-leak-free@2.1.2: - resolution: - { - integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} once@1.4.0: - resolution: - { - integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, - } + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} otpauth@9.5.0: - resolution: - { - integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==, - } + resolution: {integrity: sha512-Ldhc6UYl4baR5toGr8nfKC+L/b8/RgHKoIixAebgoNGzUUCET02g04rMEZ2ZsPfeVQhMHcuaOgb28nwMr81zCA==} oxc-parser@0.120.0: - resolution: - { - integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==} + engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.19.1: - resolution: - { - integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==, - } + resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} p-limit@1.3.0: - resolution: - { - integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} p-limit@2.3.0: - resolution: - { - integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-locate@2.0.0: - resolution: - { - integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} p-locate@3.0.0: - resolution: - { - integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} p-locate@4.1.0: - resolution: - { - integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} p-try@1.0.0: - resolution: - { - integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} p-try@2.2.0: - resolution: - { - integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse-entities@4.0.2: - resolution: - { - integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==, - } + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} parse-json@4.0.0: - resolution: - { - integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} parse-json@5.2.0: - resolution: - { - integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} parse5@8.0.0: - resolution: - { - integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==, - } + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} path-exists@3.0.0: - resolution: - { - integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-expression-matcher@1.1.3: - resolution: - { - integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==} + engines: {node: '>=14.0.0'} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} path-type@3.0.0: - resolution: - { - integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.2: - resolution: - { - integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, - } - engines: { node: ">=8.6" } - - picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} picomatch@4.0.4: - resolution: - { - integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} pify@2.3.0: - resolution: - { - integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} pify@3.0.0: - resolution: - { - integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} pino-abstract-transport@3.0.0: - resolution: - { - integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==, - } + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} pino-pretty@13.1.3: - resolution: - { - integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==, - } + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true pino-std-serializers@7.1.0: - resolution: - { - integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==, - } + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} pino@10.3.1: - resolution: - { - integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==, - } + resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} hasBin: true postcss-selector-parser@6.0.10: - resolution: - { - integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} postcss@8.4.31: - resolution: - { - integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} postcss@8.5.8: - resolution: - { - integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} postgres@3.4.8: - resolution: - { - integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} + engines: {node: '>=12'} prettier@3.8.1: - resolution: - { - integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} hasBin: true pretty-format@27.5.1: - resolution: - { - integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} process-nextick-args@2.0.1: - resolution: - { - integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, - } + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} process-warning@5.0.0: - resolution: - { - integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==, - } + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} property-information@7.1.0: - resolution: - { - integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==, - } + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} pump@3.0.4: - resolution: - { - integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==, - } + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} qrcode.react@4.2.0: - resolution: - { - integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==, - } + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} quick-format-unescaped@4.0.4: - resolution: - { - integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==, - } + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} quick-lru@4.0.1: - resolution: - { - integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} react-colorful@5.6.1: - resolution: - { - integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==, - } + resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" + react: '>=16.8.0' + react-dom: '>=16.8.0' react-dom@19.2.4: - resolution: - { - integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==, - } + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: react: ^19.2.4 react-is@17.0.2: - resolution: - { - integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, - } + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react-markdown@10.1.0: - resolution: - { - integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==, - } + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: - "@types/react": ">=18" - react: ">=18" + '@types/react': '>=18' + react: '>=18' react@19.2.4: - resolution: - { - integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} read-pkg-up@3.0.0: - resolution: - { - integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} + engines: {node: '>=4'} read-pkg-up@7.0.1: - resolution: - { - integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} read-pkg@3.0.0: - resolution: - { - integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} read-pkg@5.2.0: - resolution: - { - integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} readable-stream@2.3.8: - resolution: - { - integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, - } + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readable-stream@3.6.2: - resolution: - { - integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, - } - engines: { node: ">= 6" } + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} real-require@0.2.0: - resolution: - { - integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==, - } - engines: { node: ">= 12.13.0" } + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} redent@3.0.0: - resolution: - { - integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} remark-gfm@4.0.1: - resolution: - { - integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==, - } + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} remark-parse@11.0.0: - resolution: - { - integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==, - } + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} remark-rehype@11.1.2: - resolution: - { - integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==, - } + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} remark-stringify@11.0.0: - resolution: - { - integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==, - } + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} require-directory@2.1.1: - resolution: - { - integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} resolve@1.22.11: - resolution: - { - integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} hasBin: true reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: ">=1.0.0", node: ">=0.10.0" } + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rollup@4.60.0: - resolution: - { - integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==, - } - engines: { node: ">=18.0.0", npm: ">=8.0.0" } + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} safe-buffer@5.1.2: - resolution: - { - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, - } + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: - resolution: - { - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, - } + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} safe-stable-stringify@2.5.0: - resolution: - { - integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} saxes@6.0.0: - resolution: - { - integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, - } - engines: { node: ">=v12.22.7" } + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} secure-json-parse@4.1.0: - resolution: - { - integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==, - } + resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} semver@5.7.2: - resolution: - { - integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, - } + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true semver@7.7.4: - resolution: - { - integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} hasBin: true sharp@0.34.5: - resolution: - { - integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} size-sensor@1.0.3: - resolution: - { - integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==, - } + resolution: {integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==} smart-buffer@4.2.0: - resolution: - { - integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==, - } - engines: { node: ">= 6.0.0", npm: ">= 3.0.0" } - - smol-toml@1.6.0: - resolution: - { - integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==, - } - engines: { node: ">= 18" } + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + smol-toml@1.6.1: + resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + engines: {node: '>= 18'} socks-proxy-agent@9.0.0: - resolution: - { - integrity: sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==} + engines: {node: '>= 14'} socks@2.8.7: - resolution: - { - integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==, - } - engines: { node: ">= 10.0.0", npm: ">= 3.0.0" } + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} sonic-boom@4.2.1: - resolution: - { - integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==, - } + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-support@0.5.21: - resolution: - { - integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, - } + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} space-separated-tokens@2.0.2: - resolution: - { - integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, - } + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} spdx-correct@3.2.0: - resolution: - { - integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, - } + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} spdx-exceptions@2.5.0: - resolution: - { - integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, - } + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: - resolution: - { - integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, - } + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} spdx-license-ids@3.0.23: - resolution: - { - integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==, - } + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} split2@3.2.2: - resolution: - { - integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==, - } + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} split2@4.2.0: - resolution: - { - integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==, - } - engines: { node: ">= 10.x" } + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} split@1.0.1: - resolution: - { - integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==, - } + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@4.0.0: - resolution: - { - integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==, - } + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} string_decoder@1.1.1: - resolution: - { - integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, - } + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: - resolution: - { - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, - } + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} stringify-entities@4.0.4: - resolution: - { - integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==, - } + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} strip-bom@3.0.0: - resolution: - { - integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} strip-indent@3.0.0: - resolution: - { - integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} strip-json-comments@5.0.3: - resolution: - { - integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==, - } - engines: { node: ">=14.16" } + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} strnum@2.2.0: - resolution: - { - integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==, - } + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} style-to-js@1.1.21: - resolution: - { - integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==, - } + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} style-to-object@1.0.14: - resolution: - { - integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==, - } + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} styled-jsx@5.1.6: - resolution: - { - integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} peerDependencies: - "@babel/core": "*" - babel-plugin-macros: "*" - react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' peerDependenciesMeta: - "@babel/core": + '@babel/core': optional: true babel-plugin-macros: optional: true supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} symbol-tree@3.2.4: - resolution: - { - integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, - } + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} tailwindcss@4.2.2: - resolution: - { - integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==, - } + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} tapable@2.3.0: - resolution: - { - integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} text-extensions@1.9.0: - resolution: - { - integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==, - } - engines: { node: ">=0.10" } + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} thread-stream@4.0.0: - resolution: - { - integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} + engines: {node: '>=20'} through2@2.0.5: - resolution: - { - integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==, - } + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} through@2.3.8: - resolution: - { - integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, - } + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@1.0.4: - resolution: - { - integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} tinyrainbow@3.1.0: - resolution: - { - integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} tldts-core@7.0.27: - resolution: - { - integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==, - } + resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} tldts@7.0.27: - resolution: - { - integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==, - } + resolution: {integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==} hasBin: true to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: ">=8.0" } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} tough-cookie@6.0.1: - resolution: - { - integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} tr46@6.0.0: - resolution: - { - integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} trim-lines@3.0.1: - resolution: - { - integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==, - } + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} trim-newlines@3.0.1: - resolution: - { - integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} trough@2.2.0: - resolution: - { - integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==, - } + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} tslib@2.3.0: - resolution: - { - integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==, - } + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsx@4.21.0: - resolution: - { - integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, - } - engines: { node: ">=18.0.0" } + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} hasBin: true type-fest@0.18.1: - resolution: - { - integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} type-fest@0.6.0: - resolution: - { - integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} type-fest@0.8.1: - resolution: - { - integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} typedarray@0.0.6: - resolution: - { - integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, - } + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} typescript@6.0.2: - resolution: - { - integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==, - } - engines: { node: ">=14.17" } + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} + engines: {node: '>=14.17'} hasBin: true uglify-js@3.19.3: - resolution: - { - integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, - } - engines: { node: ">=0.8.0" } + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} hasBin: true unbash@2.2.0: - resolution: - { - integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==} + engines: {node: '>=14'} undici-types@7.18.2: - resolution: - { - integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==, - } + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} undici@7.24.5: - resolution: - { - integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==, - } - engines: { node: ">=20.18.1" } + resolution: {integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==} + engines: {node: '>=20.18.1'} unified@11.0.5: - resolution: - { - integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==, - } + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} unist-util-is@6.0.1: - resolution: - { - integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==, - } + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-position@5.0.0: - resolution: - { - integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==, - } + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} unist-util-stringify-position@4.0.0: - resolution: - { - integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==, - } + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} unist-util-visit-parents@6.0.2: - resolution: - { - integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==, - } + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} unist-util-visit@5.1.0: - resolution: - { - integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==, - } + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} validate-npm-package-license@3.0.4: - resolution: - { - integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, - } + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} vfile-message@4.0.3: - resolution: - { - integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==, - } + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: - resolution: - { - integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==, - } + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} vite@7.3.1: - resolution: - { - integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - jiti: ">=1.21.0" + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' less: ^4.0.0 lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 - stylus: ">=0.54.8" + stylus: '>=0.54.8' sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: - "@types/node": + '@types/node': optional: true jiti: optional: true @@ -5462,37 +3333,34 @@ packages: optional: true vitest@4.1.1: - resolution: - { - integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==, - } - engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: - "@edge-runtime/vm": "*" - "@opentelemetry/api": ^1.9.0 - "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 - "@vitest/browser-playwright": 4.1.1 - "@vitest/browser-preview": 4.1.1 - "@vitest/browser-webdriverio": 4.1.1 - "@vitest/ui": 4.1.1 - happy-dom: "*" - jsdom: "*" + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.1 + '@vitest/browser-preview': 4.1.1 + '@vitest/browser-webdriverio': 4.1.1 + '@vitest/ui': 4.1.1 + happy-dom: '*' + jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - "@edge-runtime/vm": + '@edge-runtime/vm': optional: true - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@types/node": + '@types/node': optional: true - "@vitest/browser-playwright": + '@vitest/browser-playwright': optional: true - "@vitest/browser-preview": + '@vitest/browser-preview': optional: true - "@vitest/browser-webdriverio": + '@vitest/browser-webdriverio': optional: true - "@vitest/ui": + '@vitest/ui': optional: true happy-dom: optional: true @@ -5500,335 +3368,258 @@ packages: optional: true w3c-xmlserializer@5.0.0: - resolution: - { - integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} walk-up-path@4.0.0: - resolution: - { - integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} webidl-conversions@8.0.1: - resolution: - { - integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} whatwg-mimetype@5.0.0: - resolution: - { - integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} whatwg-url@16.0.1: - resolution: - { - integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} hasBin: true wordwrap@1.0.0: - resolution: - { - integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, - } + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} wrappy@1.0.2: - resolution: - { - integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, - } + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} xml-name-validator@5.0.0: - resolution: - { - integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} xmlchars@2.2.0: - resolution: - { - integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, - } + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} xtend@4.0.2: - resolution: - { - integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, - } - engines: { node: ">=0.4" } + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@4.0.0: - resolution: - { - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, - } + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} yaml@2.8.2: - resolution: - { - integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==, - } - engines: { node: ">= 14.6" } + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} hasBin: true yaml@2.8.3: - resolution: - { - integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==, - } - engines: { node: ">= 14.6" } + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@20.2.9: - resolution: - { - integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} yargs@16.2.0: - resolution: - { - integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} yargs@17.7.2: - resolution: - { - integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} zod@4.3.6: - resolution: - { - integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==, - } + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} zrender@5.6.1: - resolution: - { - integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==, - } + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} zrender@6.0.0: - resolution: - { - integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==, - } + resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} zwitch@2.0.4: - resolution: - { - integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==, - } + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - "@adobe/css-tools@4.4.4": {} - "@alloc/quick-lru@5.2.0": {} + '@adobe/css-tools@4.4.4': {} - "@asamuzakjp/css-color@5.0.1": + '@alloc/quick-lru@5.2.0': {} + + '@asamuzakjp/css-color@5.0.1': dependencies: - "@csstools/css-calc": 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-color-parser": 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.2.7 - "@asamuzakjp/dom-selector@7.0.4": + '@asamuzakjp/dom-selector@7.0.4': dependencies: - "@asamuzakjp/nwsapi": 2.3.9 + '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.2.7 - "@asamuzakjp/nwsapi@2.3.9": {} + '@asamuzakjp/nwsapi@2.3.9': {} - "@babel/code-frame@7.29.0": + '@babel/code-frame@7.29.0': dependencies: - "@babel/helper-validator-identifier": 7.28.5 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - "@babel/helper-validator-identifier@7.28.5": {} + '@babel/helper-validator-identifier@7.28.5': {} - "@babel/runtime@7.29.2": {} + '@babel/runtime@7.29.2': {} - "@biomejs/biome@2.4.8": + '@biomejs/biome@2.4.8': optionalDependencies: - "@biomejs/cli-darwin-arm64": 2.4.8 - "@biomejs/cli-darwin-x64": 2.4.8 - "@biomejs/cli-linux-arm64": 2.4.8 - "@biomejs/cli-linux-arm64-musl": 2.4.8 - "@biomejs/cli-linux-x64": 2.4.8 - "@biomejs/cli-linux-x64-musl": 2.4.8 - "@biomejs/cli-win32-arm64": 2.4.8 - "@biomejs/cli-win32-x64": 2.4.8 + '@biomejs/cli-darwin-arm64': 2.4.8 + '@biomejs/cli-darwin-x64': 2.4.8 + '@biomejs/cli-linux-arm64': 2.4.8 + '@biomejs/cli-linux-arm64-musl': 2.4.8 + '@biomejs/cli-linux-x64': 2.4.8 + '@biomejs/cli-linux-x64-musl': 2.4.8 + '@biomejs/cli-win32-arm64': 2.4.8 + '@biomejs/cli-win32-x64': 2.4.8 - "@biomejs/cli-darwin-arm64@2.4.8": + '@biomejs/cli-darwin-arm64@2.4.8': optional: true - "@biomejs/cli-darwin-x64@2.4.8": + '@biomejs/cli-darwin-x64@2.4.8': optional: true - "@biomejs/cli-linux-arm64-musl@2.4.8": + '@biomejs/cli-linux-arm64-musl@2.4.8': optional: true - "@biomejs/cli-linux-arm64@2.4.8": + '@biomejs/cli-linux-arm64@2.4.8': optional: true - "@biomejs/cli-linux-x64-musl@2.4.8": + '@biomejs/cli-linux-x64-musl@2.4.8': optional: true - "@biomejs/cli-linux-x64@2.4.8": + '@biomejs/cli-linux-x64@2.4.8': optional: true - "@biomejs/cli-win32-arm64@2.4.8": + '@biomejs/cli-win32-arm64@2.4.8': optional: true - "@biomejs/cli-win32-x64@2.4.8": + '@biomejs/cli-win32-x64@2.4.8': optional: true - "@bramus/specificity@2.4.2": + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 - "@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2)": + '@commitlint/cli@20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2)': dependencies: - "@commitlint/format": 20.5.0 - "@commitlint/lint": 20.5.0 - "@commitlint/load": 20.5.0(@types/node@25.5.0)(typescript@6.0.2) - "@commitlint/read": 20.5.0(conventional-commits-parser@6.3.0) - "@commitlint/types": 20.5.0 + '@commitlint/format': 20.5.0 + '@commitlint/lint': 20.5.0 + '@commitlint/load': 20.5.0(@types/node@25.5.0)(typescript@6.0.2) + '@commitlint/read': 20.5.0(conventional-commits-parser@6.3.0) + '@commitlint/types': 20.5.0 tinyexec: 1.0.4 yargs: 17.7.2 transitivePeerDependencies: - - "@types/node" + - '@types/node' - conventional-commits-filter - conventional-commits-parser - typescript - "@commitlint/config-conventional@20.5.0": + '@commitlint/config-conventional@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 conventional-changelog-conventionalcommits: 9.3.0 - "@commitlint/config-validator@20.5.0": + '@commitlint/config-validator@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 ajv: 8.18.0 - "@commitlint/ensure@20.5.0": + '@commitlint/ensure@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 lodash.startcase: 4.4.0 lodash.upperfirst: 4.3.1 - "@commitlint/execute-rule@20.0.0": {} + '@commitlint/execute-rule@20.0.0': {} - "@commitlint/format@20.5.0": + '@commitlint/format@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 picocolors: 1.1.1 - "@commitlint/is-ignored@20.5.0": + '@commitlint/is-ignored@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 semver: 7.7.4 - "@commitlint/lint@20.5.0": + '@commitlint/lint@20.5.0': dependencies: - "@commitlint/is-ignored": 20.5.0 - "@commitlint/parse": 20.5.0 - "@commitlint/rules": 20.5.0 - "@commitlint/types": 20.5.0 + '@commitlint/is-ignored': 20.5.0 + '@commitlint/parse': 20.5.0 + '@commitlint/rules': 20.5.0 + '@commitlint/types': 20.5.0 - "@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@6.0.2)": + '@commitlint/load@20.5.0(@types/node@25.5.0)(typescript@6.0.2)': dependencies: - "@commitlint/config-validator": 20.5.0 - "@commitlint/execute-rule": 20.0.0 - "@commitlint/resolve-extends": 20.5.0 - "@commitlint/types": 20.5.0 + '@commitlint/config-validator': 20.5.0 + '@commitlint/execute-rule': 20.0.0 + '@commitlint/resolve-extends': 20.5.0 + '@commitlint/types': 20.5.0 cosmiconfig: 9.0.1(typescript@6.0.2) cosmiconfig-typescript-loader: 6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 transitivePeerDependencies: - - "@types/node" + - '@types/node' - typescript - "@commitlint/message@20.4.3": {} + '@commitlint/message@20.4.3': {} - "@commitlint/parse@20.5.0": + '@commitlint/parse@20.5.0': dependencies: - "@commitlint/types": 20.5.0 + '@commitlint/types': 20.5.0 conventional-changelog-angular: 8.3.0 conventional-commits-parser: 6.3.0 - "@commitlint/read@20.5.0(conventional-commits-parser@6.3.0)": + '@commitlint/read@20.5.0(conventional-commits-parser@6.3.0)': dependencies: - "@commitlint/top-level": 20.4.3 - "@commitlint/types": 20.5.0 + '@commitlint/top-level': 20.4.3 + '@commitlint/types': 20.5.0 git-raw-commits: 5.0.1(conventional-commits-parser@6.3.0) minimist: 1.2.8 tinyexec: 1.0.4 @@ -5836,589 +3627,589 @@ snapshots: - conventional-commits-filter - conventional-commits-parser - "@commitlint/resolve-extends@20.5.0": + '@commitlint/resolve-extends@20.5.0': dependencies: - "@commitlint/config-validator": 20.5.0 - "@commitlint/types": 20.5.0 + '@commitlint/config-validator': 20.5.0 + '@commitlint/types': 20.5.0 global-directory: 4.0.1 import-meta-resolve: 4.2.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - "@commitlint/rules@20.5.0": + '@commitlint/rules@20.5.0': dependencies: - "@commitlint/ensure": 20.5.0 - "@commitlint/message": 20.4.3 - "@commitlint/to-lines": 20.0.0 - "@commitlint/types": 20.5.0 + '@commitlint/ensure': 20.5.0 + '@commitlint/message': 20.4.3 + '@commitlint/to-lines': 20.0.0 + '@commitlint/types': 20.5.0 - "@commitlint/to-lines@20.0.0": {} + '@commitlint/to-lines@20.0.0': {} - "@commitlint/top-level@20.4.3": + '@commitlint/top-level@20.4.3': dependencies: escalade: 3.2.0 - "@commitlint/types@20.5.0": + '@commitlint/types@20.5.0': dependencies: conventional-commits-parser: 6.3.0 picocolors: 1.1.1 - "@conventional-changelog/git-client@2.6.0(conventional-commits-parser@6.3.0)": + '@conventional-changelog/git-client@2.6.0(conventional-commits-parser@6.3.0)': dependencies: - "@simple-libs/child-process-utils": 1.0.2 - "@simple-libs/stream-utils": 1.2.0 + '@simple-libs/child-process-utils': 1.0.2 + '@simple-libs/stream-utils': 1.2.0 semver: 7.7.4 optionalDependencies: conventional-commits-parser: 6.3.0 - "@csstools/color-helpers@6.0.2": {} + '@csstools/color-helpers@6.0.2': {} - "@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/color-helpers": 6.0.2 - "@csstools/css-calc": 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)": + '@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 - "@csstools/css-tokenizer@4.0.0": {} + '@csstools/css-tokenizer@4.0.0': {} - "@dnd-kit/accessibility@3.1.1(react@19.2.4)": + '@dnd-kit/accessibility@3.1.1(react@19.2.4)': dependencies: react: 19.2.4 tslib: 2.8.1 - "@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": + '@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - "@dnd-kit/accessibility": 3.1.1(react@19.2.4) - "@dnd-kit/utilities": 3.2.2(react@19.2.4) + '@dnd-kit/accessibility': 3.1.1(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) tslib: 2.8.1 - "@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)": + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': dependencies: - "@dnd-kit/core": 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - "@dnd-kit/utilities": 3.2.2(react@19.2.4) + '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) react: 19.2.4 tslib: 2.8.1 - "@dnd-kit/utilities@3.2.2(react@19.2.4)": + '@dnd-kit/utilities@3.2.2(react@19.2.4)': dependencies: react: 19.2.4 tslib: 2.8.1 - "@drizzle-team/brocli@0.10.2": {} + '@drizzle-team/brocli@0.10.2': {} - "@emnapi/core@1.9.1": + '@emnapi/core@1.9.1': dependencies: - "@emnapi/wasi-threads": 1.2.0 + '@emnapi/wasi-threads': 1.2.0 tslib: 2.8.1 optional: true - "@emnapi/runtime@1.9.1": + '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 optional: true - "@emnapi/wasi-threads@1.2.0": + '@emnapi/wasi-threads@1.2.0': dependencies: tslib: 2.8.1 optional: true - "@epic-web/invariant@1.0.0": {} + '@epic-web/invariant@1.0.0': {} - "@esbuild-kit/core-utils@3.3.2": + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.27.4 source-map-support: 0.5.21 - "@esbuild-kit/esm-loader@2.6.5": + '@esbuild-kit/esm-loader@2.6.5': dependencies: - "@esbuild-kit/core-utils": 3.3.2 + '@esbuild-kit/core-utils': 3.3.2 get-tsconfig: 4.13.7 - "@esbuild/aix-ppc64@0.27.4": + '@esbuild/aix-ppc64@0.27.4': optional: true - "@esbuild/android-arm64@0.27.4": + '@esbuild/android-arm64@0.27.4': optional: true - "@esbuild/android-arm@0.27.4": + '@esbuild/android-arm@0.27.4': optional: true - "@esbuild/android-x64@0.27.4": + '@esbuild/android-x64@0.27.4': optional: true - "@esbuild/darwin-arm64@0.27.4": + '@esbuild/darwin-arm64@0.27.4': optional: true - "@esbuild/darwin-x64@0.27.4": + '@esbuild/darwin-x64@0.27.4': optional: true - "@esbuild/freebsd-arm64@0.27.4": + '@esbuild/freebsd-arm64@0.27.4': optional: true - "@esbuild/freebsd-x64@0.27.4": + '@esbuild/freebsd-x64@0.27.4': optional: true - "@esbuild/linux-arm64@0.27.4": + '@esbuild/linux-arm64@0.27.4': optional: true - "@esbuild/linux-arm@0.27.4": + '@esbuild/linux-arm@0.27.4': optional: true - "@esbuild/linux-ia32@0.27.4": + '@esbuild/linux-ia32@0.27.4': optional: true - "@esbuild/linux-loong64@0.27.4": + '@esbuild/linux-loong64@0.27.4': optional: true - "@esbuild/linux-mips64el@0.27.4": + '@esbuild/linux-mips64el@0.27.4': optional: true - "@esbuild/linux-ppc64@0.27.4": + '@esbuild/linux-ppc64@0.27.4': optional: true - "@esbuild/linux-riscv64@0.27.4": + '@esbuild/linux-riscv64@0.27.4': optional: true - "@esbuild/linux-s390x@0.27.4": + '@esbuild/linux-s390x@0.27.4': optional: true - "@esbuild/linux-x64@0.27.4": + '@esbuild/linux-x64@0.27.4': optional: true - "@esbuild/netbsd-arm64@0.27.4": + '@esbuild/netbsd-arm64@0.27.4': optional: true - "@esbuild/netbsd-x64@0.27.4": + '@esbuild/netbsd-x64@0.27.4': optional: true - "@esbuild/openbsd-arm64@0.27.4": + '@esbuild/openbsd-arm64@0.27.4': optional: true - "@esbuild/openbsd-x64@0.27.4": + '@esbuild/openbsd-x64@0.27.4': optional: true - "@esbuild/openharmony-arm64@0.27.4": + '@esbuild/openharmony-arm64@0.27.4': optional: true - "@esbuild/sunos-x64@0.27.4": + '@esbuild/sunos-x64@0.27.4': optional: true - "@esbuild/win32-arm64@0.27.4": + '@esbuild/win32-arm64@0.27.4': optional: true - "@esbuild/win32-ia32@0.27.4": + '@esbuild/win32-ia32@0.27.4': optional: true - "@esbuild/win32-x64@0.27.4": + '@esbuild/win32-x64@0.27.4': optional: true - "@exodus/bytes@1.15.0(@noble/hashes@2.0.1)": + '@exodus/bytes@1.15.0(@noble/hashes@2.0.1)': optionalDependencies: - "@noble/hashes": 2.0.1 + '@noble/hashes': 2.0.1 - "@formkit/auto-animate@0.9.0": {} + '@formkit/auto-animate@0.9.0': {} - "@hutson/parse-repository-url@3.0.2": {} + '@hutson/parse-repository-url@3.0.2': {} - "@img/colour@1.1.0": + '@img/colour@1.1.0': optional: true - "@img/sharp-darwin-arm64@0.34.5": + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-arm64": 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - "@img/sharp-darwin-x64@0.34.5": + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-x64": 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - "@img/sharp-libvips-darwin-arm64@1.2.4": + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - "@img/sharp-libvips-darwin-x64@1.2.4": + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm64@1.2.4": + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm@1.2.4": + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - "@img/sharp-libvips-linux-ppc64@1.2.4": + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - "@img/sharp-libvips-linux-riscv64@1.2.4": + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - "@img/sharp-libvips-linux-s390x@1.2.4": + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - "@img/sharp-libvips-linux-x64@1.2.4": + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-x64@1.2.4": + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - "@img/sharp-linux-arm64@0.34.5": + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm64": 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - "@img/sharp-linux-arm@0.34.5": + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm": 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - "@img/sharp-linux-ppc64@0.34.5": + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-ppc64": 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - "@img/sharp-linux-riscv64@0.34.5": + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-riscv64": 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - "@img/sharp-linux-s390x@0.34.5": + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-s390x": 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - "@img/sharp-linux-x64@0.34.5": + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-x64": 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - "@img/sharp-linuxmusl-arm64@0.34.5": + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - "@img/sharp-linuxmusl-x64@0.34.5": + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true - "@img/sharp-wasm32@0.34.5": + '@img/sharp-wasm32@0.34.5': dependencies: - "@emnapi/runtime": 1.9.1 + '@emnapi/runtime': 1.9.1 optional: true - "@img/sharp-win32-arm64@0.34.5": + '@img/sharp-win32-arm64@0.34.5': optional: true - "@img/sharp-win32-ia32@0.34.5": + '@img/sharp-win32-ia32@0.34.5': optional: true - "@img/sharp-win32-x64@0.34.5": + '@img/sharp-win32-x64@0.34.5': optional: true - "@jridgewell/gen-mapping@0.3.13": + '@jridgewell/gen-mapping@0.3.13': dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/remapping@2.3.5": + '@jridgewell/remapping@2.3.5': dependencies: - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/resolve-uri@3.1.2": {} + '@jridgewell/resolve-uri@3.1.2': {} - "@jridgewell/sourcemap-codec@1.5.5": {} + '@jridgewell/sourcemap-codec@1.5.5': {} - "@jridgewell/trace-mapping@0.3.31": + '@jridgewell/trace-mapping@0.3.31': dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.5.5 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - "@napi-rs/wasm-runtime@1.1.1": + '@napi-rs/wasm-runtime@1.1.1': dependencies: - "@emnapi/core": 1.9.1 - "@emnapi/runtime": 1.9.1 - "@tybys/wasm-util": 0.10.1 + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.1 + '@tybys/wasm-util': 0.10.1 optional: true - "@next/env@16.2.1": {} + '@next/env@16.2.1': {} - "@next/swc-darwin-arm64@16.2.1": + '@next/swc-darwin-arm64@16.2.1': optional: true - "@next/swc-darwin-x64@16.2.1": + '@next/swc-darwin-x64@16.2.1': optional: true - "@next/swc-linux-arm64-gnu@16.2.1": + '@next/swc-linux-arm64-gnu@16.2.1': optional: true - "@next/swc-linux-arm64-musl@16.2.1": + '@next/swc-linux-arm64-musl@16.2.1': optional: true - "@next/swc-linux-x64-gnu@16.2.1": + '@next/swc-linux-x64-gnu@16.2.1': optional: true - "@next/swc-linux-x64-musl@16.2.1": + '@next/swc-linux-x64-musl@16.2.1': optional: true - "@next/swc-win32-arm64-msvc@16.2.1": + '@next/swc-win32-arm64-msvc@16.2.1': optional: true - "@next/swc-win32-x64-msvc@16.2.1": + '@next/swc-win32-x64-msvc@16.2.1': optional: true - "@noble/hashes@2.0.1": {} + '@noble/hashes@2.0.1': {} - "@nodelib/fs.scandir@2.1.5": + '@nodelib/fs.scandir@2.1.5': dependencies: - "@nodelib/fs.stat": 2.0.5 + '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - "@nodelib/fs.stat@2.0.5": {} + '@nodelib/fs.stat@2.0.5': {} - "@nodelib/fs.walk@1.2.8": + '@nodelib/fs.walk@1.2.8': dependencies: - "@nodelib/fs.scandir": 2.1.5 + '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - "@oxc-parser/binding-android-arm-eabi@0.120.0": + '@oxc-parser/binding-android-arm-eabi@0.120.0': optional: true - "@oxc-parser/binding-android-arm64@0.120.0": + '@oxc-parser/binding-android-arm64@0.120.0': optional: true - "@oxc-parser/binding-darwin-arm64@0.120.0": + '@oxc-parser/binding-darwin-arm64@0.120.0': optional: true - "@oxc-parser/binding-darwin-x64@0.120.0": + '@oxc-parser/binding-darwin-x64@0.120.0': optional: true - "@oxc-parser/binding-freebsd-x64@0.120.0": + '@oxc-parser/binding-freebsd-x64@0.120.0': optional: true - "@oxc-parser/binding-linux-arm-gnueabihf@0.120.0": + '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': optional: true - "@oxc-parser/binding-linux-arm-musleabihf@0.120.0": + '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': optional: true - "@oxc-parser/binding-linux-arm64-gnu@0.120.0": + '@oxc-parser/binding-linux-arm64-gnu@0.120.0': optional: true - "@oxc-parser/binding-linux-arm64-musl@0.120.0": + '@oxc-parser/binding-linux-arm64-musl@0.120.0': optional: true - "@oxc-parser/binding-linux-ppc64-gnu@0.120.0": + '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': optional: true - "@oxc-parser/binding-linux-riscv64-gnu@0.120.0": + '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': optional: true - "@oxc-parser/binding-linux-riscv64-musl@0.120.0": + '@oxc-parser/binding-linux-riscv64-musl@0.120.0': optional: true - "@oxc-parser/binding-linux-s390x-gnu@0.120.0": + '@oxc-parser/binding-linux-s390x-gnu@0.120.0': optional: true - "@oxc-parser/binding-linux-x64-gnu@0.120.0": + '@oxc-parser/binding-linux-x64-gnu@0.120.0': optional: true - "@oxc-parser/binding-linux-x64-musl@0.120.0": + '@oxc-parser/binding-linux-x64-musl@0.120.0': optional: true - "@oxc-parser/binding-openharmony-arm64@0.120.0": + '@oxc-parser/binding-openharmony-arm64@0.120.0': optional: true - "@oxc-parser/binding-wasm32-wasi@0.120.0": + '@oxc-parser/binding-wasm32-wasi@0.120.0': dependencies: - "@napi-rs/wasm-runtime": 1.1.1 + '@napi-rs/wasm-runtime': 1.1.1 optional: true - "@oxc-parser/binding-win32-arm64-msvc@0.120.0": + '@oxc-parser/binding-win32-arm64-msvc@0.120.0': optional: true - "@oxc-parser/binding-win32-ia32-msvc@0.120.0": + '@oxc-parser/binding-win32-ia32-msvc@0.120.0': optional: true - "@oxc-parser/binding-win32-x64-msvc@0.120.0": + '@oxc-parser/binding-win32-x64-msvc@0.120.0': optional: true - "@oxc-project/types@0.120.0": {} + '@oxc-project/types@0.120.0': {} - "@oxc-resolver/binding-android-arm-eabi@11.19.1": + '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true - "@oxc-resolver/binding-android-arm64@11.19.1": + '@oxc-resolver/binding-android-arm64@11.19.1': optional: true - "@oxc-resolver/binding-darwin-arm64@11.19.1": + '@oxc-resolver/binding-darwin-arm64@11.19.1': optional: true - "@oxc-resolver/binding-darwin-x64@11.19.1": + '@oxc-resolver/binding-darwin-x64@11.19.1': optional: true - "@oxc-resolver/binding-freebsd-x64@11.19.1": + '@oxc-resolver/binding-freebsd-x64@11.19.1': optional: true - "@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1": + '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': optional: true - "@oxc-resolver/binding-linux-arm-musleabihf@11.19.1": + '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': optional: true - "@oxc-resolver/binding-linux-arm64-gnu@11.19.1": + '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': optional: true - "@oxc-resolver/binding-linux-arm64-musl@11.19.1": + '@oxc-resolver/binding-linux-arm64-musl@11.19.1': optional: true - "@oxc-resolver/binding-linux-ppc64-gnu@11.19.1": + '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': optional: true - "@oxc-resolver/binding-linux-riscv64-gnu@11.19.1": + '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': optional: true - "@oxc-resolver/binding-linux-riscv64-musl@11.19.1": + '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': optional: true - "@oxc-resolver/binding-linux-s390x-gnu@11.19.1": + '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': optional: true - "@oxc-resolver/binding-linux-x64-gnu@11.19.1": + '@oxc-resolver/binding-linux-x64-gnu@11.19.1': optional: true - "@oxc-resolver/binding-linux-x64-musl@11.19.1": + '@oxc-resolver/binding-linux-x64-musl@11.19.1': optional: true - "@oxc-resolver/binding-openharmony-arm64@11.19.1": + '@oxc-resolver/binding-openharmony-arm64@11.19.1': optional: true - "@oxc-resolver/binding-wasm32-wasi@11.19.1": + '@oxc-resolver/binding-wasm32-wasi@11.19.1': dependencies: - "@napi-rs/wasm-runtime": 1.1.1 + '@napi-rs/wasm-runtime': 1.1.1 optional: true - "@oxc-resolver/binding-win32-arm64-msvc@11.19.1": + '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': optional: true - "@oxc-resolver/binding-win32-ia32-msvc@11.19.1": + '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': optional: true - "@oxc-resolver/binding-win32-x64-msvc@11.19.1": + '@oxc-resolver/binding-win32-x64-msvc@11.19.1': optional: true - "@phc/format@1.0.0": {} + '@phc/format@1.0.0': {} - "@pinojs/redact@0.4.0": {} + '@pinojs/redact@0.4.0': {} - "@rollup/rollup-android-arm-eabi@4.60.0": + '@rollup/rollup-android-arm-eabi@4.60.0': optional: true - "@rollup/rollup-android-arm64@4.60.0": + '@rollup/rollup-android-arm64@4.60.0': optional: true - "@rollup/rollup-darwin-arm64@4.60.0": + '@rollup/rollup-darwin-arm64@4.60.0': optional: true - "@rollup/rollup-darwin-x64@4.60.0": + '@rollup/rollup-darwin-x64@4.60.0': optional: true - "@rollup/rollup-freebsd-arm64@4.60.0": + '@rollup/rollup-freebsd-arm64@4.60.0': optional: true - "@rollup/rollup-freebsd-x64@4.60.0": + '@rollup/rollup-freebsd-x64@4.60.0': optional: true - "@rollup/rollup-linux-arm-gnueabihf@4.60.0": + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': optional: true - "@rollup/rollup-linux-arm-musleabihf@4.60.0": + '@rollup/rollup-linux-arm-musleabihf@4.60.0': optional: true - "@rollup/rollup-linux-arm64-gnu@4.60.0": + '@rollup/rollup-linux-arm64-gnu@4.60.0': optional: true - "@rollup/rollup-linux-arm64-musl@4.60.0": + '@rollup/rollup-linux-arm64-musl@4.60.0': optional: true - "@rollup/rollup-linux-loong64-gnu@4.60.0": + '@rollup/rollup-linux-loong64-gnu@4.60.0': optional: true - "@rollup/rollup-linux-loong64-musl@4.60.0": + '@rollup/rollup-linux-loong64-musl@4.60.0': optional: true - "@rollup/rollup-linux-ppc64-gnu@4.60.0": + '@rollup/rollup-linux-ppc64-gnu@4.60.0': optional: true - "@rollup/rollup-linux-ppc64-musl@4.60.0": + '@rollup/rollup-linux-ppc64-musl@4.60.0': optional: true - "@rollup/rollup-linux-riscv64-gnu@4.60.0": + '@rollup/rollup-linux-riscv64-gnu@4.60.0': optional: true - "@rollup/rollup-linux-riscv64-musl@4.60.0": + '@rollup/rollup-linux-riscv64-musl@4.60.0': optional: true - "@rollup/rollup-linux-s390x-gnu@4.60.0": + '@rollup/rollup-linux-s390x-gnu@4.60.0': optional: true - "@rollup/rollup-linux-x64-gnu@4.60.0": + '@rollup/rollup-linux-x64-gnu@4.60.0': optional: true - "@rollup/rollup-linux-x64-musl@4.60.0": + '@rollup/rollup-linux-x64-musl@4.60.0': optional: true - "@rollup/rollup-openbsd-x64@4.60.0": + '@rollup/rollup-openbsd-x64@4.60.0': optional: true - "@rollup/rollup-openharmony-arm64@4.60.0": + '@rollup/rollup-openharmony-arm64@4.60.0': optional: true - "@rollup/rollup-win32-arm64-msvc@4.60.0": + '@rollup/rollup-win32-arm64-msvc@4.60.0': optional: true - "@rollup/rollup-win32-ia32-msvc@4.60.0": + '@rollup/rollup-win32-ia32-msvc@4.60.0': optional: true - "@rollup/rollup-win32-x64-gnu@4.60.0": + '@rollup/rollup-win32-x64-gnu@4.60.0': optional: true - "@rollup/rollup-win32-x64-msvc@4.60.0": + '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true - "@simple-libs/child-process-utils@1.0.2": + '@simple-libs/child-process-utils@1.0.2': dependencies: - "@simple-libs/stream-utils": 1.2.0 + '@simple-libs/stream-utils': 1.2.0 - "@simple-libs/stream-utils@1.2.0": {} + '@simple-libs/stream-utils@1.2.0': {} - "@standard-schema/spec@1.1.0": {} + '@standard-schema/spec@1.1.0': {} - "@swc/helpers@0.5.15": + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 - "@tailwindcss/node@4.2.2": + '@tailwindcss/node@4.2.2': dependencies: - "@jridgewell/remapping": 2.3.5 + '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.20.1 jiti: 2.6.1 lightningcss: 1.32.0 @@ -6426,207 +4217,207 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.2.2 - "@tailwindcss/oxide-android-arm64@4.2.2": + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true - "@tailwindcss/oxide-darwin-arm64@4.2.2": + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true - "@tailwindcss/oxide-darwin-x64@4.2.2": + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true - "@tailwindcss/oxide-freebsd-x64@4.2.2": + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true - "@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2": + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true - "@tailwindcss/oxide-linux-arm64-gnu@4.2.2": + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true - "@tailwindcss/oxide-linux-arm64-musl@4.2.2": + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true - "@tailwindcss/oxide-linux-x64-gnu@4.2.2": + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true - "@tailwindcss/oxide-linux-x64-musl@4.2.2": + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true - "@tailwindcss/oxide-wasm32-wasi@4.2.2": + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true - "@tailwindcss/oxide-win32-arm64-msvc@4.2.2": + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true - "@tailwindcss/oxide-win32-x64-msvc@4.2.2": + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true - "@tailwindcss/oxide@4.2.2": + '@tailwindcss/oxide@4.2.2': optionalDependencies: - "@tailwindcss/oxide-android-arm64": 4.2.2 - "@tailwindcss/oxide-darwin-arm64": 4.2.2 - "@tailwindcss/oxide-darwin-x64": 4.2.2 - "@tailwindcss/oxide-freebsd-x64": 4.2.2 - "@tailwindcss/oxide-linux-arm-gnueabihf": 4.2.2 - "@tailwindcss/oxide-linux-arm64-gnu": 4.2.2 - "@tailwindcss/oxide-linux-arm64-musl": 4.2.2 - "@tailwindcss/oxide-linux-x64-gnu": 4.2.2 - "@tailwindcss/oxide-linux-x64-musl": 4.2.2 - "@tailwindcss/oxide-wasm32-wasi": 4.2.2 - "@tailwindcss/oxide-win32-arm64-msvc": 4.2.2 - "@tailwindcss/oxide-win32-x64-msvc": 4.2.2 - - "@tailwindcss/postcss@4.2.2": - dependencies: - "@alloc/quick-lru": 5.2.0 - "@tailwindcss/node": 4.2.2 - "@tailwindcss/oxide": 4.2.2 + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + + '@tailwindcss/postcss@4.2.2': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 postcss: 8.5.8 tailwindcss: 4.2.2 - "@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)": + '@tailwindcss/typography@0.5.19(tailwindcss@4.2.2)': dependencies: postcss-selector-parser: 6.0.10 tailwindcss: 4.2.2 - "@tanstack/query-core@5.95.2": {} + '@tanstack/query-core@5.95.2': {} - "@tanstack/react-query@5.95.2(react@19.2.4)": + '@tanstack/react-query@5.95.2(react@19.2.4)': dependencies: - "@tanstack/query-core": 5.95.2 + '@tanstack/query-core': 5.95.2 react: 19.2.4 - "@testing-library/dom@10.4.1": + '@testing-library/dom@10.4.1': dependencies: - "@babel/code-frame": 7.29.0 - "@babel/runtime": 7.29.2 - "@types/aria-query": 5.0.4 + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 - "@testing-library/jest-dom@6.9.1": + '@testing-library/jest-dom@6.9.1': dependencies: - "@adobe/css-tools": 4.4.4 + '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)": + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - "@babel/runtime": 7.29.2 - "@testing-library/dom": 10.4.1 + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - "@types/react": 19.2.14 - "@types/react-dom": 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) - "@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)": + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: - "@testing-library/dom": 10.4.1 + '@testing-library/dom': 10.4.1 - "@tybys/wasm-util@0.10.1": + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 optional: true - "@types/aria-query@5.0.4": {} + '@types/aria-query@5.0.4': {} - "@types/chai@5.2.3": + '@types/chai@5.2.3': dependencies: - "@types/deep-eql": 4.0.2 + '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - "@types/debug@4.1.12": + '@types/debug@4.1.12': dependencies: - "@types/ms": 2.1.0 + '@types/ms': 2.1.0 - "@types/deep-eql@4.0.2": {} + '@types/deep-eql@4.0.2': {} - "@types/estree-jsx@1.0.5": + '@types/estree-jsx@1.0.5': dependencies: - "@types/estree": 1.0.8 + '@types/estree': 1.0.8 - "@types/estree@1.0.8": {} + '@types/estree@1.0.8': {} - "@types/hast@3.0.4": + '@types/hast@3.0.4': dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 - "@types/mdast@4.0.4": + '@types/mdast@4.0.4': dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 - "@types/minimist@1.2.5": {} + '@types/minimist@1.2.5': {} - "@types/ms@2.1.0": {} + '@types/ms@2.1.0': {} - "@types/node-cron@3.0.11": {} + '@types/node-cron@3.0.11': {} - "@types/node@25.5.0": + '@types/node@25.5.0': dependencies: undici-types: 7.18.2 - "@types/normalize-package-data@2.4.4": {} + '@types/normalize-package-data@2.4.4': {} - "@types/react-dom@19.2.3(@types/react@19.2.14)": + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: - "@types/react": 19.2.14 + '@types/react': 19.2.14 - "@types/react@19.2.14": + '@types/react@19.2.14': dependencies: csstype: 3.2.3 - "@types/unist@2.0.11": {} + '@types/unist@2.0.11': {} - "@types/unist@3.0.3": {} + '@types/unist@3.0.3': {} - "@ungap/structured-clone@1.3.0": {} + '@ungap/structured-clone@1.3.0': {} - "@vitest/expect@4.1.1": + '@vitest/expect@4.1.1': dependencies: - "@standard-schema/spec": 1.1.0 - "@types/chai": 5.2.3 - "@vitest/spy": 4.1.1 - "@vitest/utils": 4.1.1 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.1 + '@vitest/utils': 4.1.1 chai: 6.2.2 tinyrainbow: 3.1.0 - "@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))": + '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - "@vitest/spy": 4.1.1 + '@vitest/spy': 4.1.1 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - "@vitest/pretty-format@4.1.1": + '@vitest/pretty-format@4.1.1': dependencies: tinyrainbow: 3.1.0 - "@vitest/runner@4.1.1": + '@vitest/runner@4.1.1': dependencies: - "@vitest/utils": 4.1.1 + '@vitest/utils': 4.1.1 pathe: 2.0.3 - "@vitest/snapshot@4.1.1": + '@vitest/snapshot@4.1.1': dependencies: - "@vitest/pretty-format": 4.1.1 - "@vitest/utils": 4.1.1 + '@vitest/pretty-format': 4.1.1 + '@vitest/utils': 4.1.1 magic-string: 0.30.21 pathe: 2.0.3 - "@vitest/spy@4.1.1": {} + '@vitest/spy@4.1.1': {} - "@vitest/utils@4.1.1": + '@vitest/utils@4.1.1': dependencies: - "@vitest/pretty-format": 4.1.1 + '@vitest/pretty-format': 4.1.1 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -6660,7 +4451,7 @@ snapshots: argon2@0.44.0: dependencies: - "@phc/format": 1.0.0 + '@phc/format': 1.0.0 cross-env: 10.1.0 node-addon-api: 8.6.0 node-gyp-build: 4.8.4 @@ -6890,7 +4681,7 @@ snapshots: conventional-commits-parser@6.3.0: dependencies: - "@simple-libs/stream-utils": 1.2.0 + '@simple-libs/stream-utils': 1.2.0 meow: 13.2.0 conventional-recommended-bump@7.0.1: @@ -6909,7 +4700,7 @@ snapshots: cosmiconfig-typescript-loader@6.2.0(@types/node@25.5.0)(cosmiconfig@9.0.1(typescript@6.0.2))(typescript@6.0.2): dependencies: - "@types/node": 25.5.0 + '@types/node': 25.5.0 cosmiconfig: 9.0.1(typescript@6.0.2) jiti: 2.6.1 typescript: 6.0.2 @@ -6925,7 +4716,7 @@ snapshots: cross-env@10.1.0: dependencies: - "@epic-web/invariant": 1.0.0 + '@epic-web/invariant': 1.0.0 cross-spawn: 7.0.6 cross-spawn@7.0.6: @@ -6952,7 +4743,7 @@ snapshots: whatwg-mimetype: 5.0.0 whatwg-url: 16.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' dateformat@3.0.3: {} @@ -7004,8 +4795,8 @@ snapshots: drizzle-kit@0.31.10: dependencies: - "@drizzle-team/brocli": 0.10.2 - "@esbuild-kit/esm-loader": 2.6.5 + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.27.4 tsx: 4.21.0 @@ -7059,32 +4850,32 @@ snapshots: esbuild@0.27.4: optionalDependencies: - "@esbuild/aix-ppc64": 0.27.4 - "@esbuild/android-arm": 0.27.4 - "@esbuild/android-arm64": 0.27.4 - "@esbuild/android-x64": 0.27.4 - "@esbuild/darwin-arm64": 0.27.4 - "@esbuild/darwin-x64": 0.27.4 - "@esbuild/freebsd-arm64": 0.27.4 - "@esbuild/freebsd-x64": 0.27.4 - "@esbuild/linux-arm": 0.27.4 - "@esbuild/linux-arm64": 0.27.4 - "@esbuild/linux-ia32": 0.27.4 - "@esbuild/linux-loong64": 0.27.4 - "@esbuild/linux-mips64el": 0.27.4 - "@esbuild/linux-ppc64": 0.27.4 - "@esbuild/linux-riscv64": 0.27.4 - "@esbuild/linux-s390x": 0.27.4 - "@esbuild/linux-x64": 0.27.4 - "@esbuild/netbsd-arm64": 0.27.4 - "@esbuild/netbsd-x64": 0.27.4 - "@esbuild/openbsd-arm64": 0.27.4 - "@esbuild/openbsd-x64": 0.27.4 - "@esbuild/openharmony-arm64": 0.27.4 - "@esbuild/sunos-x64": 0.27.4 - "@esbuild/win32-arm64": 0.27.4 - "@esbuild/win32-ia32": 0.27.4 - "@esbuild/win32-x64": 0.27.4 + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 escalade@3.2.0: {} @@ -7096,7 +4887,7 @@ snapshots: estree-walker@3.0.3: dependencies: - "@types/estree": 1.0.8 + '@types/estree': 1.0.8 expect-type@1.3.0: {} @@ -7108,8 +4899,8 @@ snapshots: fast-glob@3.3.3: dependencies: - "@nodelib/fs.stat": 2.0.5 - "@nodelib/fs.walk": 1.2.8 + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.8 @@ -7181,7 +4972,7 @@ snapshots: get-pkg-repo@4.2.1: dependencies: - "@hutson/parse-repository-url": 3.0.2 + '@hutson/parse-repository-url': 3.0.2 hosted-git-info: 4.1.0 through2: 2.0.5 yargs: 16.2.0 @@ -7202,7 +4993,7 @@ snapshots: git-raw-commits@5.0.1(conventional-commits-parser@6.3.0): dependencies: - "@conventional-changelog/git-client": 2.6.0(conventional-commits-parser@6.3.0) + '@conventional-changelog/git-client': 2.6.0(conventional-commits-parser@6.3.0) meow: 13.2.0 transitivePeerDependencies: - conventional-commits-filter @@ -7251,9 +5042,9 @@ snapshots: hast-util-to-jsx-runtime@2.3.6: dependencies: - "@types/estree": 1.0.8 - "@types/hast": 3.0.4 - "@types/unist": 3.0.3 + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 devlop: 1.1.0 estree-util-is-identifier-name: 3.0.0 @@ -7271,7 +5062,7 @@ snapshots: hast-util-whitespace@3.0.0: dependencies: - "@types/hast": 3.0.4 + '@types/hast': 3.0.4 help-me@5.0.0: {} @@ -7283,9 +5074,9 @@ snapshots: html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: - "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' html-url-attributes@3.0.1: {} @@ -7374,11 +5165,11 @@ snapshots: jsdom@29.0.1(@noble/hashes@2.0.1): dependencies: - "@asamuzakjp/css-color": 5.0.1 - "@asamuzakjp/dom-selector": 7.0.4 - "@bramus/specificity": 2.4.2 - "@csstools/css-syntax-patches-for-csstree": 1.1.1(css-tree@3.2.1) - "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) + '@asamuzakjp/css-color': 5.0.1 + '@asamuzakjp/dom-selector': 7.0.4 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.0.1) decimal.js: 10.6.0 @@ -7396,7 +5187,7 @@ snapshots: whatwg-url: 16.0.1(@noble/hashes@2.0.1) xml-name-validator: 5.0.0 transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' json-parse-better-errors@1.0.2: {} @@ -7410,9 +5201,9 @@ snapshots: kind-of@6.0.3: {} - knip@6.0.5: + knip@6.0.6: dependencies: - "@nodelib/fs.walk": 1.2.8 + '@nodelib/fs.walk': 1.2.8 fast-glob: 3.3.3 formatly: 0.3.0 get-tsconfig: 4.13.7 @@ -7421,8 +5212,8 @@ snapshots: oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.3 - smol-toml: 1.6.0 + picomatch: 4.0.4 + smol-toml: 1.6.1 strip-json-comments: 5.0.3 unbash: 2.2.0 yaml: 2.8.3 @@ -7530,7 +5321,7 @@ snapshots: magic-string@0.30.21: dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 + '@jridgewell/sourcemap-codec': 1.5.5 map-obj@1.0.1: {} @@ -7540,15 +5331,15 @@ snapshots: mdast-util-find-and-replace@3.0.2: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 mdast-util-from-markdown@2.0.3: dependencies: - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 @@ -7564,7 +5355,7 @@ snapshots: mdast-util-gfm-autolink-literal@2.0.1: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.2 @@ -7572,7 +5363,7 @@ snapshots: mdast-util-gfm-footnote@2.1.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -7582,7 +5373,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: @@ -7590,7 +5381,7 @@ snapshots: mdast-util-gfm-table@2.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 mdast-util-from-markdown: 2.0.3 @@ -7600,7 +5391,7 @@ snapshots: mdast-util-gfm-task-list-item@2.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -7621,9 +5412,9 @@ snapshots: mdast-util-mdx-expression@2.0.1: dependencies: - "@types/estree-jsx": 1.0.5 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -7632,10 +5423,10 @@ snapshots: mdast-util-mdx-jsx@3.2.0: dependencies: - "@types/estree-jsx": 1.0.5 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 @@ -7649,9 +5440,9 @@ snapshots: mdast-util-mdxjs-esm@2.0.1: dependencies: - "@types/estree-jsx": 1.0.5 - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 devlop: 1.1.0 mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 @@ -7660,14 +5451,14 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 unist-util-is: 6.0.1 mdast-util-to-hast@13.2.1: dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 - "@ungap/structured-clone": 1.3.0 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -7677,8 +5468,8 @@ snapshots: mdast-util-to-markdown@2.1.2: dependencies: - "@types/mdast": 4.0.4 - "@types/unist": 3.0.3 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 longest-streak: 3.1.0 mdast-util-phrasing: 4.1.0 mdast-util-to-string: 4.0.0 @@ -7689,7 +5480,7 @@ snapshots: mdast-util-to-string@4.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdn-data@2.27.1: {} @@ -7697,7 +5488,7 @@ snapshots: meow@8.1.2: dependencies: - "@types/minimist": 1.2.5 + '@types/minimist': 1.2.5 camelcase-keys: 6.2.2 decamelize-keys: 1.1.1 hard-rejection: 2.1.0 @@ -7882,7 +5673,7 @@ snapshots: micromark@4.0.2: dependencies: - "@types/debug": 4.1.12 + '@types/debug': 4.1.12 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -7931,8 +5722,8 @@ snapshots: next@16.2.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - "@next/env": 16.2.1 - "@swc/helpers": 0.5.15 + '@next/env': 16.2.1 + '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.9 caniuse-lite: 1.0.30001780 postcss: 8.4.31 @@ -7940,17 +5731,17 @@ snapshots: react-dom: 19.2.4(react@19.2.4) styled-jsx: 5.1.6(react@19.2.4) optionalDependencies: - "@next/swc-darwin-arm64": 16.2.1 - "@next/swc-darwin-x64": 16.2.1 - "@next/swc-linux-arm64-gnu": 16.2.1 - "@next/swc-linux-arm64-musl": 16.2.1 - "@next/swc-linux-x64-gnu": 16.2.1 - "@next/swc-linux-x64-musl": 16.2.1 - "@next/swc-win32-arm64-msvc": 16.2.1 - "@next/swc-win32-x64-msvc": 16.2.1 + '@next/swc-darwin-arm64': 16.2.1 + '@next/swc-darwin-x64': 16.2.1 + '@next/swc-linux-arm64-gnu': 16.2.1 + '@next/swc-linux-arm64-musl': 16.2.1 + '@next/swc-linux-x64-gnu': 16.2.1 + '@next/swc-linux-x64-musl': 16.2.1 + '@next/swc-win32-arm64-msvc': 16.2.1 + '@next/swc-win32-x64-msvc': 16.2.1 sharp: 0.34.5 transitivePeerDependencies: - - "@babel/core" + - '@babel/core' - babel-plugin-macros node-addon-api@8.6.0: {} @@ -7983,55 +5774,55 @@ snapshots: otpauth@9.5.0: dependencies: - "@noble/hashes": 2.0.1 + '@noble/hashes': 2.0.1 oxc-parser@0.120.0: dependencies: - "@oxc-project/types": 0.120.0 + '@oxc-project/types': 0.120.0 optionalDependencies: - "@oxc-parser/binding-android-arm-eabi": 0.120.0 - "@oxc-parser/binding-android-arm64": 0.120.0 - "@oxc-parser/binding-darwin-arm64": 0.120.0 - "@oxc-parser/binding-darwin-x64": 0.120.0 - "@oxc-parser/binding-freebsd-x64": 0.120.0 - "@oxc-parser/binding-linux-arm-gnueabihf": 0.120.0 - "@oxc-parser/binding-linux-arm-musleabihf": 0.120.0 - "@oxc-parser/binding-linux-arm64-gnu": 0.120.0 - "@oxc-parser/binding-linux-arm64-musl": 0.120.0 - "@oxc-parser/binding-linux-ppc64-gnu": 0.120.0 - "@oxc-parser/binding-linux-riscv64-gnu": 0.120.0 - "@oxc-parser/binding-linux-riscv64-musl": 0.120.0 - "@oxc-parser/binding-linux-s390x-gnu": 0.120.0 - "@oxc-parser/binding-linux-x64-gnu": 0.120.0 - "@oxc-parser/binding-linux-x64-musl": 0.120.0 - "@oxc-parser/binding-openharmony-arm64": 0.120.0 - "@oxc-parser/binding-wasm32-wasi": 0.120.0 - "@oxc-parser/binding-win32-arm64-msvc": 0.120.0 - "@oxc-parser/binding-win32-ia32-msvc": 0.120.0 - "@oxc-parser/binding-win32-x64-msvc": 0.120.0 + '@oxc-parser/binding-android-arm-eabi': 0.120.0 + '@oxc-parser/binding-android-arm64': 0.120.0 + '@oxc-parser/binding-darwin-arm64': 0.120.0 + '@oxc-parser/binding-darwin-x64': 0.120.0 + '@oxc-parser/binding-freebsd-x64': 0.120.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.120.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.120.0 + '@oxc-parser/binding-linux-arm64-musl': 0.120.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.120.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.120.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-gnu': 0.120.0 + '@oxc-parser/binding-linux-x64-musl': 0.120.0 + '@oxc-parser/binding-openharmony-arm64': 0.120.0 + '@oxc-parser/binding-wasm32-wasi': 0.120.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.120.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.120.0 + '@oxc-parser/binding-win32-x64-msvc': 0.120.0 oxc-resolver@11.19.1: optionalDependencies: - "@oxc-resolver/binding-android-arm-eabi": 11.19.1 - "@oxc-resolver/binding-android-arm64": 11.19.1 - "@oxc-resolver/binding-darwin-arm64": 11.19.1 - "@oxc-resolver/binding-darwin-x64": 11.19.1 - "@oxc-resolver/binding-freebsd-x64": 11.19.1 - "@oxc-resolver/binding-linux-arm-gnueabihf": 11.19.1 - "@oxc-resolver/binding-linux-arm-musleabihf": 11.19.1 - "@oxc-resolver/binding-linux-arm64-gnu": 11.19.1 - "@oxc-resolver/binding-linux-arm64-musl": 11.19.1 - "@oxc-resolver/binding-linux-ppc64-gnu": 11.19.1 - "@oxc-resolver/binding-linux-riscv64-gnu": 11.19.1 - "@oxc-resolver/binding-linux-riscv64-musl": 11.19.1 - "@oxc-resolver/binding-linux-s390x-gnu": 11.19.1 - "@oxc-resolver/binding-linux-x64-gnu": 11.19.1 - "@oxc-resolver/binding-linux-x64-musl": 11.19.1 - "@oxc-resolver/binding-openharmony-arm64": 11.19.1 - "@oxc-resolver/binding-wasm32-wasi": 11.19.1 - "@oxc-resolver/binding-win32-arm64-msvc": 11.19.1 - "@oxc-resolver/binding-win32-ia32-msvc": 11.19.1 - "@oxc-resolver/binding-win32-x64-msvc": 11.19.1 + '@oxc-resolver/binding-android-arm-eabi': 11.19.1 + '@oxc-resolver/binding-android-arm64': 11.19.1 + '@oxc-resolver/binding-darwin-arm64': 11.19.1 + '@oxc-resolver/binding-darwin-x64': 11.19.1 + '@oxc-resolver/binding-freebsd-x64': 11.19.1 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.19.1 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.19.1 + '@oxc-resolver/binding-linux-arm64-gnu': 11.19.1 + '@oxc-resolver/binding-linux-arm64-musl': 11.19.1 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.19.1 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.19.1 + '@oxc-resolver/binding-linux-riscv64-musl': 11.19.1 + '@oxc-resolver/binding-linux-s390x-gnu': 11.19.1 + '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 + '@oxc-resolver/binding-linux-x64-musl': 11.19.1 + '@oxc-resolver/binding-openharmony-arm64': 11.19.1 + '@oxc-resolver/binding-wasm32-wasi': 11.19.1 + '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 + '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 + '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 p-limit@1.3.0: dependencies: @@ -8071,7 +5862,7 @@ snapshots: parse-entities@4.0.2: dependencies: - "@types/unist": 2.0.11 + '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.3.0 @@ -8086,7 +5877,7 @@ snapshots: parse-json@5.2.0: dependencies: - "@babel/code-frame": 7.29.0 + '@babel/code-frame': 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -8115,8 +5906,6 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.3: {} - picomatch@4.0.4: {} pify@2.3.0: {} @@ -8147,7 +5936,7 @@ snapshots: pino@10.3.1: dependencies: - "@pinojs/redact": 0.4.0 + '@pinojs/redact': 0.4.0 atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 3.0.0 @@ -8223,9 +6012,9 @@ snapshots: react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 - "@types/react": 19.2.14 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.14 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -8260,7 +6049,7 @@ snapshots: read-pkg@5.2.0: dependencies: - "@types/normalize-package-data": 2.4.4 + '@types/normalize-package-data': 2.4.4 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 @@ -8290,7 +6079,7 @@ snapshots: remark-gfm@4.0.1: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-gfm: 3.1.0 micromark-extension-gfm: 3.0.0 remark-parse: 11.0.0 @@ -8301,7 +6090,7 @@ snapshots: remark-parse@11.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.3 micromark-util-types: 2.0.2 unified: 11.0.5 @@ -8310,15 +6099,15 @@ snapshots: remark-rehype@11.1.2: dependencies: - "@types/hast": 3.0.4 - "@types/mdast": 4.0.4 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 mdast-util-to-hast: 13.2.1 unified: 11.0.5 vfile: 6.0.3 remark-stringify@11.0.0: dependencies: - "@types/mdast": 4.0.4 + '@types/mdast': 4.0.4 mdast-util-to-markdown: 2.1.2 unified: 11.0.5 @@ -8342,33 +6131,33 @@ snapshots: rollup@4.60.0: dependencies: - "@types/estree": 1.0.8 + '@types/estree': 1.0.8 optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.60.0 - "@rollup/rollup-android-arm64": 4.60.0 - "@rollup/rollup-darwin-arm64": 4.60.0 - "@rollup/rollup-darwin-x64": 4.60.0 - "@rollup/rollup-freebsd-arm64": 4.60.0 - "@rollup/rollup-freebsd-x64": 4.60.0 - "@rollup/rollup-linux-arm-gnueabihf": 4.60.0 - "@rollup/rollup-linux-arm-musleabihf": 4.60.0 - "@rollup/rollup-linux-arm64-gnu": 4.60.0 - "@rollup/rollup-linux-arm64-musl": 4.60.0 - "@rollup/rollup-linux-loong64-gnu": 4.60.0 - "@rollup/rollup-linux-loong64-musl": 4.60.0 - "@rollup/rollup-linux-ppc64-gnu": 4.60.0 - "@rollup/rollup-linux-ppc64-musl": 4.60.0 - "@rollup/rollup-linux-riscv64-gnu": 4.60.0 - "@rollup/rollup-linux-riscv64-musl": 4.60.0 - "@rollup/rollup-linux-s390x-gnu": 4.60.0 - "@rollup/rollup-linux-x64-gnu": 4.60.0 - "@rollup/rollup-linux-x64-musl": 4.60.0 - "@rollup/rollup-openbsd-x64": 4.60.0 - "@rollup/rollup-openharmony-arm64": 4.60.0 - "@rollup/rollup-win32-arm64-msvc": 4.60.0 - "@rollup/rollup-win32-ia32-msvc": 4.60.0 - "@rollup/rollup-win32-x64-gnu": 4.60.0 - "@rollup/rollup-win32-x64-msvc": 4.60.0 + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -8395,34 +6184,34 @@ snapshots: sharp@0.34.5: dependencies: - "@img/colour": 1.1.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.7.4 optionalDependencies: - "@img/sharp-darwin-arm64": 0.34.5 - "@img/sharp-darwin-x64": 0.34.5 - "@img/sharp-libvips-darwin-arm64": 1.2.4 - "@img/sharp-libvips-darwin-x64": 1.2.4 - "@img/sharp-libvips-linux-arm": 1.2.4 - "@img/sharp-libvips-linux-arm64": 1.2.4 - "@img/sharp-libvips-linux-ppc64": 1.2.4 - "@img/sharp-libvips-linux-riscv64": 1.2.4 - "@img/sharp-libvips-linux-s390x": 1.2.4 - "@img/sharp-libvips-linux-x64": 1.2.4 - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 - "@img/sharp-linux-arm": 0.34.5 - "@img/sharp-linux-arm64": 0.34.5 - "@img/sharp-linux-ppc64": 0.34.5 - "@img/sharp-linux-riscv64": 0.34.5 - "@img/sharp-linux-s390x": 0.34.5 - "@img/sharp-linux-x64": 0.34.5 - "@img/sharp-linuxmusl-arm64": 0.34.5 - "@img/sharp-linuxmusl-x64": 0.34.5 - "@img/sharp-wasm32": 0.34.5 - "@img/sharp-win32-arm64": 0.34.5 - "@img/sharp-win32-ia32": 0.34.5 - "@img/sharp-win32-x64": 0.34.5 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -8437,7 +6226,7 @@ snapshots: smart-buffer@4.2.0: {} - smol-toml@1.6.0: {} + smol-toml@1.6.1: {} socks-proxy-agent@9.0.0: dependencies: @@ -8633,7 +6422,7 @@ snapshots: unified@11.0.5: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 bail: 2.0.2 devlop: 1.1.0 extend: 3.0.2 @@ -8643,24 +6432,24 @@ snapshots: unist-util-is@6.0.1: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-position@5.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-stringify-position@4.0.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-visit-parents@6.0.2: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-is: 6.0.1 unist-util-visit@5.1.0: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 @@ -8673,12 +6462,12 @@ snapshots: vfile-message@4.0.3: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 vfile@6.0.3: dependencies: - "@types/unist": 3.0.3 + '@types/unist': 3.0.3 vfile-message: 4.0.3 vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3): @@ -8690,7 +6479,7 @@ snapshots: rollup: 4.60.0 tinyglobby: 0.2.15 optionalDependencies: - "@types/node": 25.5.0 + '@types/node': 25.5.0 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 @@ -8699,13 +6488,13 @@ snapshots: vitest@4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - "@vitest/expect": 4.1.1 - "@vitest/mocker": 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - "@vitest/pretty-format": 4.1.1 - "@vitest/runner": 4.1.1 - "@vitest/snapshot": 4.1.1 - "@vitest/spy": 4.1.1 - "@vitest/utils": 4.1.1 + '@vitest/expect': 4.1.1 + '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.1 + '@vitest/runner': 4.1.1 + '@vitest/snapshot': 4.1.1 + '@vitest/spy': 4.1.1 + '@vitest/utils': 4.1.1 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -8720,7 +6509,7 @@ snapshots: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: - "@types/node": 25.5.0 + '@types/node': 25.5.0 jsdom: 29.0.1(@noble/hashes@2.0.1) transitivePeerDependencies: - msw @@ -8737,11 +6526,11 @@ snapshots: whatwg-url@16.0.1(@noble/hashes@2.0.1): dependencies: - "@exodus/bytes": 1.15.0(@noble/hashes@2.0.1) + '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' which@2.0.2: dependencies: From 9f60ad6dc4713a1d942438b2f34a6ac84d76e107 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:01:51 +0000 Subject: [PATCH 27/59] chore(deps-dev): bump @biomejs/biome from 2.4.8 to 2.4.9 Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.4.8 to 2.4.9. - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.9/packages/@biomejs/biome) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.4.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package.json | 2 +- pnpm-lock.yaml | 74 +++++++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 37112130..f127d0c4 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "socks-proxy-agent": "^9.0.0" }, "devDependencies": { - "@biomejs/biome": "^2.4.8", + "@biomejs/biome": "^2.4.9", "@commitlint/cli": "^20.5.0", "@commitlint/config-conventional": "^20.5.0", "@tailwindcss/postcss": "^4.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd988e60..e0040146 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,8 +103,8 @@ importers: version: 9.0.0 devDependencies: '@biomejs/biome': - specifier: ^2.4.8 - version: 2.4.8 + specifier: ^2.4.9 + version: 2.4.9 '@commitlint/cli': specifier: ^20.5.0 version: 20.5.0(@types/node@25.5.0)(conventional-commits-parser@6.3.0)(typescript@6.0.2) @@ -201,59 +201,59 @@ packages: resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - '@biomejs/biome@2.4.8': - resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} + '@biomejs/biome@2.4.9': + resolution: {integrity: sha512-wvZW92FrwitTcacvCBT8xdAbfbxWfDLwjYMmU3djjqQTh7Ni4ZdiWIT/x5VcZ+RQuxiKzIOzi5D+dcyJDFZMsA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.4.8': - resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} + '@biomejs/cli-darwin-arm64@2.4.9': + resolution: {integrity: sha512-d5G8Gf2RpH5pYwiHLPA+UpG3G9TLQu4WM+VK6sfL7K68AmhcEQ9r+nkj/DvR/GYhYox6twsHUtmWWWIKfcfQQA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.8': - resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} + '@biomejs/cli-darwin-x64@2.4.9': + resolution: {integrity: sha512-LNCLNgqDMG7BLdc3a8aY/dwKPK7+R8/JXJoXjCvZh2gx8KseqBdFDKbhrr7HCWF8SzNhbTaALhTBoh/I6rf9lA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.8': - resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} + '@biomejs/cli-linux-arm64-musl@2.4.9': + resolution: {integrity: sha512-8RCww5xnPn2wpK4L/QDGDOW0dq80uVWfppPxHIUg6mOs9B6gRmqPp32h1Ls3T8GnW8Wo5A8u7vpTwz4fExN+sw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.8': - resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} + '@biomejs/cli-linux-arm64@2.4.9': + resolution: {integrity: sha512-4adnkAUi6K4C/emPRgYznMOcLlUqZdXWM6aIui4VP4LraE764g6Q4YguygnAUoxKjKIXIWPteKMgRbN0wsgwcg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.8': - resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} + '@biomejs/cli-linux-x64-musl@2.4.9': + resolution: {integrity: sha512-5TD+WS9v5vzXKzjetF0hgoaNFHMcpQeBUwKKVi3JbG1e9UCrFuUK3Gt185fyTzvRdwYkJJEMqglRPjmesmVv4A==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.8': - resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} + '@biomejs/cli-linux-x64@2.4.9': + resolution: {integrity: sha512-L10na7POF0Ks/cgLFNF1ZvIe+X4onLkTi5oP9hY+Rh60Q+7fWzKDDCeGyiHUFf1nGIa9dQOOUPGe2MyYg8nMSQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.8': - resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} + '@biomejs/cli-win32-arm64@2.4.9': + resolution: {integrity: sha512-aDZr0RBC3sMGJOU10BvG7eZIlWLK/i51HRIfScE2lVhfts2dQTreowLiJJd+UYg/tHKxS470IbzpuKmd0MiD6g==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.8': - resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} + '@biomejs/cli-win32-x64@2.4.9': + resolution: {integrity: sha512-NS4g/2G9SoQ4ktKtz31pvyc/rmgzlcIDCGU/zWbmHJAqx6gcRj2gj5Q/guXhoWTzCUaQZDIqiCQXHS7BcGYc0w==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -3501,39 +3501,39 @@ snapshots: '@babel/runtime@7.29.2': {} - '@biomejs/biome@2.4.8': + '@biomejs/biome@2.4.9': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.8 - '@biomejs/cli-darwin-x64': 2.4.8 - '@biomejs/cli-linux-arm64': 2.4.8 - '@biomejs/cli-linux-arm64-musl': 2.4.8 - '@biomejs/cli-linux-x64': 2.4.8 - '@biomejs/cli-linux-x64-musl': 2.4.8 - '@biomejs/cli-win32-arm64': 2.4.8 - '@biomejs/cli-win32-x64': 2.4.8 + '@biomejs/cli-darwin-arm64': 2.4.9 + '@biomejs/cli-darwin-x64': 2.4.9 + '@biomejs/cli-linux-arm64': 2.4.9 + '@biomejs/cli-linux-arm64-musl': 2.4.9 + '@biomejs/cli-linux-x64': 2.4.9 + '@biomejs/cli-linux-x64-musl': 2.4.9 + '@biomejs/cli-win32-arm64': 2.4.9 + '@biomejs/cli-win32-x64': 2.4.9 - '@biomejs/cli-darwin-arm64@2.4.8': + '@biomejs/cli-darwin-arm64@2.4.9': optional: true - '@biomejs/cli-darwin-x64@2.4.8': + '@biomejs/cli-darwin-x64@2.4.9': optional: true - '@biomejs/cli-linux-arm64-musl@2.4.8': + '@biomejs/cli-linux-arm64-musl@2.4.9': optional: true - '@biomejs/cli-linux-arm64@2.4.8': + '@biomejs/cli-linux-arm64@2.4.9': optional: true - '@biomejs/cli-linux-x64-musl@2.4.8': + '@biomejs/cli-linux-x64-musl@2.4.9': optional: true - '@biomejs/cli-linux-x64@2.4.8': + '@biomejs/cli-linux-x64@2.4.9': optional: true - '@biomejs/cli-win32-arm64@2.4.8': + '@biomejs/cli-win32-arm64@2.4.9': optional: true - '@biomejs/cli-win32-x64@2.4.8': + '@biomejs/cli-win32-x64@2.4.9': optional: true '@bramus/specificity@2.4.2': From 4238de1878f153cace44dbacba706e7671c40fbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:03:23 +0000 Subject: [PATCH 28/59] chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates Bumps the npm_and_yarn group with 2 updates in the / directory: [picomatch](https://github.com/micromatch/picomatch) and [yaml](https://github.com/eemeli/yaml). Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) Updates `yaml` from 2.8.2 to 2.8.3 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.8.2...v2.8.3) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: yaml dependency-version: 2.8.3 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- pnpm-lock.yaml | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 172ef70c..58b77b92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2791,14 +2791,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -3429,11 +3425,6 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -4580,7 +4571,7 @@ snapshots: find-up: 5.0.0 git-semver-tags: 5.0.1 semver: 7.7.4 - yaml: 2.8.2 + yaml: 2.8.3 yargs: 17.7.2 compare-func@2.0.0: @@ -4931,10 +4922,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -5220,7 +5207,7 @@ snapshots: oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 smol-toml: 1.6.1 strip-json-comments: 5.0.3 unbash: 2.2.0 @@ -5704,7 +5691,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 min-indent@1.0.1: {} @@ -5912,9 +5899,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - - picomatch@4.0.3: {} + picomatch@2.3.2: {} picomatch@4.0.4: {} @@ -6371,8 +6356,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinyrainbow@3.1.0: {} @@ -6510,7 +6495,7 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 @@ -6571,8 +6556,6 @@ snapshots: yallist@4.0.0: {} - yaml@2.8.2: {} - yaml@2.8.3: {} yargs-parser@20.2.9: {} From 829d823b6d76ae6574b08154a7bef15c4787cf52 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:03:37 -0500 Subject: [PATCH 29/59] chore(release): 2.5.0 --- CHANGELOG.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17514b4a..5bb3406a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,58 @@ # Changelog +## [2.5.0](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.5.0) (2026-03-26) + + +### Features + +- add development image to docker hub ([8785094](https://github.com/jordanlambrecht/tracker-tracker/commit/87850942f118ccfd23c0a04d537928cd3db82976)) +- add fetchTrackerStats for future live transit paper data ([ed6662f](https://github.com/jordanlambrecht/tracker-tracker/commit/ed6662f0ac0c9990b6c96e8ae5616452385b6c26)) +- add GitHub Actions workflow for building and pushing development Docker image ([dca0af0](https://github.com/jordanlambrecht/tracker-tracker/commit/dca0af0a75a4ce21fbe14414d86d4ecbe4d459d3)) +- add per-tracker pause polling ([14a6c43](https://github.com/jordanlambrecht/tracker-tracker/commit/14a6c43cd6764924aba6e2fa592ef13d208b528a)) +- add system events viewer and log management ([b01ed22](https://github.com/jordanlambrecht/tracker-tracker/commit/b01ed22ec2ecfefe7f12bb448998d2726cb6b7f9)) +- **dashboard:** add Today At A Glance server logic, checkpoints, and deep poll fixes ([d3826a1](https://github.com/jordanlambrecht/tracker-tracker/commit/d3826a181c017c6b2ac8bbc6fa9d77f6dfd0edc2)) +- **dashboard:** add Today At A Glance UI ([84c5227](https://github.com/jordanlambrecht/tracker-tracker/commit/84c522750f517c95653b73e1298aa72bd204e27f)) +- remote image upload ([a480c34](https://github.com/jordanlambrecht/tracker-tracker/commit/a480c3470b7e97e44764c1d9c6d1bee356d22728)) +- **schema:** add daily checkpoint tables and TodayAtAGlance types ([636d227](https://github.com/jordanlambrecht/tracker-tracker/commit/636d22701cd43d8827f9afe49a5105820578f892)) +- **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) +- **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) + + +### Bug Fixes + +- **api:** improve session expiration error message ([5a95cd0](https://github.com/jordanlambrecht/tracker-tracker/commit/5a95cd039daf5aebb128f547acf70ae749132221)) +- **api:** orpheus was not matching seeding/leeching to response ([4569238](https://github.com/jordanlambrecht/tracker-tracker/commit/456923879b2970c46432bc9a0b604da2685bc31d)) +- **auth:** decouple cookie secure flag from node_env for self-hosted http deployments. Closes [#101](https://github.com/jordanlambrecht/tracker-tracker/issues/101) ([b2a7902](https://github.com/jordanlambrecht/tracker-tracker/commit/b2a790245ca76f1ec3ef8220c273a4ab9ca508fd)) +- **auth:** return 401 on stale session instead of misleading credential errors ([cf54c7f](https://github.com/jordanlambrecht/tracker-tracker/commit/cf54c7fbd6c2d9171b5d11b621e9a1f68abc381a)) +- better regex for splitting comparison values in timing safe check ([8c67a50](https://github.com/jordanlambrecht/tracker-tracker/commit/8c67a50cb64196831d7b021249eb76e84766e009)) +- convert bold numbered rules to markdown list items ([6e96454](https://github.com/jordanlambrecht/tracker-tracker/commit/6e964541330cca791818aca5d703e03ac2165694)) +- deploy issues ([cf45ea1](https://github.com/jordanlambrecht/tracker-tracker/commit/cf45ea19726b8f31db5080ebe93909dd0825e995)) +- **Dockerfile:** update package.json for drizzle-kit with esbuild overrides ([bce0854](https://github.com/jordanlambrecht/tracker-tracker/commit/bce0854d8ea18cf4a99488ed6d9243b25fc71658)) +- **Icons:** update DownloadArrowIcon stroke width ([d2cd450](https://github.com/jordanlambrecht/tracker-tracker/commit/d2cd450af0b6cf55bc5fc1c94ea01452c55252fb)) +- preload fleet dashboard tab ([5f08951](https://github.com/jordanlambrecht/tracker-tracker/commit/5f0895192f39a740927594ab6617f2c5c04b5708)) +- resolve biome lint warnings ([af8807d](https://github.com/jordanlambrecht/tracker-tracker/commit/af8807d72847e139be396778a096a7695fc49123)) +- **trackers:** markdown rendering ([a5fbdde](https://github.com/jordanlambrecht/tracker-tracker/commit/a5fbdde7056848bb58fdbe6f1e77a765a543842d)) +- **ui:** prevent StatCard DOM prop leak ([2d0b22a](https://github.com/jordanlambrecht/tracker-tracker/commit/2d0b22aa51b1badeb8916ace9505fdf32f526dc9)) +- update type imports for CollapsibleCard ([23979d1](https://github.com/jordanlambrecht/tracker-tracker/commit/23979d1f6323bd3fa209c9ed19172dbd7d05b6db)) +- update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) +- wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) + + +### Performance + +- add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) +- **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) + + +### Refactoring + +- **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) +- **charts:** migrate time-series charts to time axis with shared helpers and quality fixes ([596396e](https://github.com/jordanlambrecht/tracker-tracker/commit/596396e205fe387799986af7f1ff8386e8f77d13)) +- **charts:** reorganize chart support files into lib/ subfolder ([98a26d4](https://github.com/jordanlambrecht/tracker-tracker/commit/98a26d4fb4bf2fb7c3b02c4d38151a6b07fbb887)) +- **Dockerfile:** cleaned up build stages ([3b96ff9](https://github.com/jordanlambrecht/tracker-tracker/commit/3b96ff964058f33d3fe8fd65bf7a6fcde9dbcd3b)) +- **settings:** extract CollapsibleCard ([5487d19](https://github.com/jordanlambrecht/tracker-tracker/commit/5487d19d1ac2dd56c6bf2136f072edbcb7868fe5)) +- **settings:** extract SettingsSection wrapper ([ea0572c](https://github.com/jordanlambrecht/tracker-tracker/commit/ea0572c603ef191494a37cd9fe7ca64447bce1d4)) + ## [2.4.2](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.4.2) (2026-03-25) ### Features diff --git a/package.json b/package.json index f127d0c4..eb387f05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "private-tracker-tracker", - "version": "2.4.2", + "version": "2.5.0", "description": "Self-hosted dashboard for monitoring private tracker stats over time", "license": "GPL-3.0", "repository": { From 19affbff9c820045cecb8a6881d820149fadd064 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:07:02 -0500 Subject: [PATCH 30/59] chore: package bumps --- .markdownlint.json | 4 +- package.json | 2 +- pnpm-lock.yaml | 180 +++++++++++++++++++++------------------------ 3 files changed, 87 insertions(+), 99 deletions(-) diff --git a/.markdownlint.json b/.markdownlint.json index af62806d..c5b3fce7 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1,5 +1,7 @@ { "MD013": false, "MD024": false, - "MD025": false + "MD025": false, + "MD033": false, + "MD041": false } diff --git a/package.json b/package.json index eb387f05..e4e568cb 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,6 @@ "tailwindcss": "^4.2.2", "tsx": "^4.21.0", "typescript": "^6.0.2", - "vitest": "^4.1.1" + "vitest": "^4.1.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0040146..e924bb75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,8 +166,8 @@ importers: specifier: ^6.0.2 version: 6.0.2 vitest: - specifier: ^4.1.1 - version: 4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + specifier: ^4.1.2 + version: 4.1.2(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) packages: @@ -1379,8 +1379,8 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1429,11 +1429,11 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitest/expect@4.1.1': - resolution: {integrity: sha512-xAV0fqBTk44Rn6SjJReEQkHP3RrqbJo6JQ4zZ7/uVOiJZRarBtblzrOfFIZeYUrukp2YD6snZG6IBqhOoHTm+A==} + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} - '@vitest/mocker@4.1.1': - resolution: {integrity: sha512-h3BOylsfsCLPeceuCPAAJ+BvNwSENgJa4hXoXu4im0bs9Lyp4URc4JYK4pWLZ4pG/UQn7AT92K6IByi6rE6g3A==} + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1443,20 +1443,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.1': - resolution: {integrity: sha512-GM+TEQN5WhOygr1lp7skeVjdLPqqWMHsfzXrcHAqZJi/lIVh63H0kaRCY8MDhNWikx19zBUK8ceaLB7X5AH9NQ==} + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} - '@vitest/runner@4.1.1': - resolution: {integrity: sha512-f7+FPy75vN91QGWsITueq0gedwUZy1fLtHOCMeQpjs8jTekAHeKP80zfDEnhrleviLHzVSDXIWuCIOFn3D3f8A==} + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} - '@vitest/snapshot@4.1.1': - resolution: {integrity: sha512-kMVSgcegWV2FibXEx9p9WIKgje58lcTbXgnJixfcg15iK8nzCXhmalL0ZLtTWLW9PH1+1NEDShiFFedB3tEgWg==} + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} - '@vitest/spy@4.1.1': - resolution: {integrity: sha512-6Ti/KT5OVaiupdIZEuZN7l3CZcR0cxnxt70Z0//3CtwgObwA6jZhmVBA3yrXSVN3gmwjgd7oDNLlsXz526gpRA==} + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} - '@vitest/utils@4.1.1': - resolution: {integrity: sha512-cNxAlaB3sHoCdL6pj6yyUXv9Gry1NHNg0kFTXdvSIZXLHsqKH7chiWOkwJ5s5+d/oMwcoG9T0bKU38JZWKusrQ==} + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} @@ -1523,8 +1523,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.10.9: - resolution: {integrity: sha512-OZd0e2mU11ClX8+IdXe3r0dbqMEznRiT4TfbhYIbcRPZkqJ7Qwer8ij3GZAmLsRKa+II9V1v5czCkvmHH3XZBg==} + baseline-browser-mapping@2.10.11: + resolution: {integrity: sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1553,8 +1553,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001780: - resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} + caniuse-lite@1.0.30001781: + resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2026,8 +2026,8 @@ packages: fast-xml-builder@1.1.4: resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} - fast-xml-parser@5.5.7: - resolution: {integrity: sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==} + fast-xml-parser@5.5.9: + resolution: {integrity: sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==} hasBin: true fastq@1.20.1: @@ -2094,9 +2094,6 @@ packages: engines: {node: '>=6.9.0'} hasBin: true - get-tsconfig@4.13.6: - resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} - get-tsconfig@4.13.7: resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} @@ -2669,8 +2666,8 @@ packages: sass: optional: true - node-addon-api@8.6.0: - resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} + node-addon-api@8.7.0: + resolution: {integrity: sha512-9MdFxmkKaOYVTV+XVRG8ArDwwQ77XIgIPyKASB1k3JPq3M8fGQQQE3YpMOrKm6g//Ktx8ivZr8xo1Qmtqub+GA==} engines: {node: ^18 || ^20 || >= 21} node-cron@4.2.1: @@ -2770,8 +2767,8 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-expression-matcher@1.1.3: - resolution: {integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==} + path-expression-matcher@1.2.0: + resolution: {integrity: sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==} engines: {node: '>=14.0.0'} path-key@3.1.1: @@ -3117,8 +3114,8 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strnum@2.2.0: - resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} + strnum@2.2.2: + resolution: {integrity: sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==} style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} @@ -3153,8 +3150,8 @@ packages: tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + tapable@2.3.2: + resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} engines: {node: '>=6'} text-extensions@1.9.0: @@ -3258,8 +3255,8 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.24.5: - resolution: {integrity: sha512-3IWdCpjgxp15CbJnsi/Y9TCDE7HWVN19j1hmzVhoAkY/+CJx449tVxT5wZc1Gwg8J+P0LWvzlBzxYRnHJ+1i7Q==} + undici@7.24.6: + resolution: {integrity: sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==} engines: {node: '>=20.18.1'} unified@11.0.5: @@ -3332,18 +3329,18 @@ packages: yaml: optional: true - vitest@4.1.1: - resolution: {integrity: sha512-yF+o4POL41rpAzj5KVILUxm1GCjKnELvaqmU9TLLUbMfDzuN0UpUR9uaDs+mCtjPe+uYPksXDRLQGGPvj1cTmA==} + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.1 - '@vitest/browser-preview': 4.1.1 - '@vitest/browser-webdriverio': 4.1.1 - '@vitest/ui': 4.1.1 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3425,11 +3422,6 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -4334,7 +4326,7 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/debug@4.1.12': + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -4380,44 +4372,44 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitest/expect@4.1.1': + '@vitest/expect@4.1.2': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.1 - '@vitest/utils': 4.1.1 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.1 + '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.1.1': + '@vitest/pretty-format@4.1.2': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.1': + '@vitest/runner@4.1.2': dependencies: - '@vitest/utils': 4.1.1 + '@vitest/utils': 4.1.2 pathe: 2.0.3 - '@vitest/snapshot@4.1.1': + '@vitest/snapshot@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.1 - '@vitest/utils': 4.1.1 + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.1': {} + '@vitest/spy@4.1.2': {} - '@vitest/utils@4.1.1': + '@vitest/utils@4.1.2': dependencies: - '@vitest/pretty-format': 4.1.1 + '@vitest/pretty-format': 4.1.2 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -4453,7 +4445,7 @@ snapshots: dependencies: '@phc/format': 1.0.0 cross-env: 10.1.0 - node-addon-api: 8.6.0 + node-addon-api: 8.7.0 node-gyp-build: 4.8.4 argparse@2.0.1: {} @@ -4476,7 +4468,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.10.9: {} + baseline-browser-mapping@2.10.11: {} bidi-js@1.0.3: dependencies: @@ -4503,7 +4495,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001780: {} + caniuse-lite@1.0.30001781: {} ccount@2.0.1: {} @@ -4571,12 +4563,12 @@ snapshots: detect-indent: 6.1.0 detect-newline: 3.1.0 dotgitignore: 2.1.0 - fast-xml-parser: 5.5.7 + fast-xml-parser: 5.5.9 figures: 3.2.0 find-up: 5.0.0 git-semver-tags: 5.0.1 semver: 7.7.4 - yaml: 2.8.2 + yaml: 2.8.3 yargs: 17.7.2 compare-func@2.0.0: @@ -4836,7 +4828,7 @@ snapshots: enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.0 + tapable: 2.3.2 entities@6.0.1: {} @@ -4911,13 +4903,13 @@ snapshots: fast-xml-builder@1.1.4: dependencies: - path-expression-matcher: 1.1.3 + path-expression-matcher: 1.2.0 - fast-xml-parser@5.5.7: + fast-xml-parser@5.5.9: dependencies: fast-xml-builder: 1.1.4 - path-expression-matcher: 1.1.3 - strnum: 2.2.0 + path-expression-matcher: 1.2.0 + strnum: 2.2.2 fastq@1.20.1: dependencies: @@ -4977,10 +4969,6 @@ snapshots: through2: 2.0.5 yargs: 16.2.0 - get-tsconfig@4.13.6: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.13.7: dependencies: resolve-pkg-maps: 1.0.0 @@ -5180,7 +5168,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.1 - undici: 7.24.5 + undici: 7.24.6 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -5673,7 +5661,7 @@ snapshots: micromark@4.0.2: dependencies: - '@types/debug': 4.1.12 + '@types/debug': 4.1.13 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -5724,8 +5712,8 @@ snapshots: dependencies: '@next/env': 16.2.1 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.9 - caniuse-lite: 1.0.30001780 + baseline-browser-mapping: 2.10.11 + caniuse-lite: 1.0.30001781 postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -5744,7 +5732,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - node-addon-api@8.6.0: {} + node-addon-api@8.7.0: {} node-cron@4.2.1: {} @@ -5890,7 +5878,7 @@ snapshots: path-exists@4.0.0: {} - path-expression-matcher@1.1.3: {} + path-expression-matcher@1.2.0: {} path-key@3.1.1: {} @@ -6315,7 +6303,7 @@ snapshots: strip-json-comments@5.0.3: {} - strnum@2.2.0: {} + strnum@2.2.2: {} style-to-js@1.1.21: dependencies: @@ -6340,7 +6328,7 @@ snapshots: tailwindcss@4.2.2: {} - tapable@2.3.0: {} + tapable@2.3.2: {} text-extensions@1.9.0: {} @@ -6397,7 +6385,7 @@ snapshots: tsx@4.21.0: dependencies: esbuild: 0.27.4 - get-tsconfig: 4.13.6 + get-tsconfig: 4.13.7 optionalDependencies: fsevents: 2.3.3 @@ -6418,7 +6406,7 @@ snapshots: undici-types@7.18.2: {} - undici@7.24.5: {} + undici@7.24.6: {} unified@11.0.5: dependencies: @@ -6486,15 +6474,15 @@ snapshots: tsx: 4.21.0 yaml: 2.8.3 - vitest@4.1.1(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.2(@types/node@25.5.0)(jsdom@29.0.1(@noble/hashes@2.0.1))(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.1 - '@vitest/mocker': 4.1.1(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/pretty-format': 4.1.1 - '@vitest/runner': 4.1.1 - '@vitest/snapshot': 4.1.1 - '@vitest/spy': 4.1.1 - '@vitest/utils': 4.1.1 + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -6561,8 +6549,6 @@ snapshots: yallist@4.0.0: {} - yaml@2.8.2: {} - yaml@2.8.3: {} yargs-parser@20.2.9: {} From 15eb043989fa2611acc846032aae1e049fef7000 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:10:27 -0500 Subject: [PATCH 31/59] fix: error logging for BigInt conversion failures in computeTodayAtAGlance --- src/lib/today.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/today.ts b/src/lib/today.ts index 284dde01..a4fba15f 100644 --- a/src/lib/today.ts +++ b/src/lib/today.ts @@ -234,8 +234,8 @@ export async function computeTodayAtAGlance(): Promise { yesterdayFleetUpload = (yesterdayFleetUpload ?? 0n) + trackerUploadYesterday yesterdayFleetDownload = (yesterdayFleetDownload ?? 0n) + trackerDownloadYesterday yesterdayFleetBuffer = (yesterdayFleetBuffer ?? 0n) + trackerBufferYesterday - } catch { - log.debug("BigInt conversion failed for yesterday comparison, tracker %d", tracker.id) + } catch (err) { + log.debug(err, "BigInt conversion failed for yesterday comparison, tracker %d", tracker.id) } } @@ -278,8 +278,8 @@ export async function computeTodayAtAGlance(): Promise { try { uploadedToday = BigInt(torrent.uploaded) - BigInt(checkpoint.uploadedStart) downloadedToday = BigInt(torrent.downloaded) - BigInt(checkpoint.downloadedStart) - } catch { - log.debug("BigInt conversion failed for torrent %s", torrent.hash) + } catch (err) { + log.debug(err, "BigInt conversion failed for torrent %s", torrent.hash) continue } From 63acb160f6c0d8d8dd52ee2112091e5be7f82fbf Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:15:29 -0500 Subject: [PATCH 32/59] chore: package bumps --- pnpm-lock.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89196e3f..e924bb75 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2788,6 +2788,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -4919,10 +4923,6 @@ snapshots: optionalDependencies: picomatch: 4.0.4 - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -5892,6 +5892,8 @@ snapshots: picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.4: {} pify@2.3.0: {} From e91b30a79605ac73c34c39e372ad2aaca4c09c44 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:21:49 -0500 Subject: [PATCH 33/59] fix: error logging for BigInt conversion failures --- src/lib/today.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/today.ts b/src/lib/today.ts index a4fba15f..463ffc60 100644 --- a/src/lib/today.ts +++ b/src/lib/today.ts @@ -172,8 +172,8 @@ export async function computeTodayAtAGlance(): Promise { ratioChange, seedbonusChange, }) - } catch { - log.debug("BigInt conversion failed for tracker %d", tracker.id) + } catch (err) { + log.warn(err, "BigInt conversion failed for tracker %d", tracker.id) trackerDeltas.push(zeroDelta(tracker)) } } @@ -235,7 +235,7 @@ export async function computeTodayAtAGlance(): Promise { yesterdayFleetDownload = (yesterdayFleetDownload ?? 0n) + trackerDownloadYesterday yesterdayFleetBuffer = (yesterdayFleetBuffer ?? 0n) + trackerBufferYesterday } catch (err) { - log.debug(err, "BigInt conversion failed for yesterday comparison, tracker %d", tracker.id) + log.warn(err, "BigInt conversion failed for yesterday comparison, tracker %d", tracker.id) } } @@ -279,7 +279,7 @@ export async function computeTodayAtAGlance(): Promise { uploadedToday = BigInt(torrent.uploaded) - BigInt(checkpoint.uploadedStart) downloadedToday = BigInt(torrent.downloaded) - BigInt(checkpoint.downloadedStart) } catch (err) { - log.debug(err, "BigInt conversion failed for torrent %s", torrent.hash) + log.warn(err, "BigInt conversion failed for torrent %s", torrent.hash) continue } From 11d93fe59bf0aeac442a76a05457f637a4ea3a34 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:29:31 -0500 Subject: [PATCH 34/59] chore: they call me the Dead Code Detective --- src/components/charts/TorrentActivityHeatmap.tsx | 2 +- src/components/charts/TorrentRatioDistribution.tsx | 2 +- .../charts/TorrentSeedTimeDistribution.tsx | 2 +- src/components/charts/lib/index.ts | 14 -------------- src/lib/notifications/types.ts | 2 -- 5 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 src/components/charts/lib/index.ts diff --git a/src/components/charts/TorrentActivityHeatmap.tsx b/src/components/charts/TorrentActivityHeatmap.tsx index 7d270121..e079a1a4 100644 --- a/src/components/charts/TorrentActivityHeatmap.tsx +++ b/src/components/charts/TorrentActivityHeatmap.tsx @@ -110,4 +110,4 @@ function TorrentActivityHeatmap({ } export type { TorrentActivityHeatmapProps } -export { TorrentActivityHeatmap, TorrentActivityHeatmap as ActivityHeatmap } +export { TorrentActivityHeatmap } diff --git a/src/components/charts/TorrentRatioDistribution.tsx b/src/components/charts/TorrentRatioDistribution.tsx index 50034e03..40d7f9f8 100644 --- a/src/components/charts/TorrentRatioDistribution.tsx +++ b/src/components/charts/TorrentRatioDistribution.tsx @@ -45,4 +45,4 @@ function TorrentRatioDistribution({ } export type { TorrentRatioDistributionProps } -export { TorrentRatioDistribution, TorrentRatioDistribution as RatioDistribution } +export { TorrentRatioDistribution } diff --git a/src/components/charts/TorrentSeedTimeDistribution.tsx b/src/components/charts/TorrentSeedTimeDistribution.tsx index e2b68ec5..5dc3f907 100644 --- a/src/components/charts/TorrentSeedTimeDistribution.tsx +++ b/src/components/charts/TorrentSeedTimeDistribution.tsx @@ -64,4 +64,4 @@ function TorrentSeedTimeDistribution({ } export type { TorrentSeedTimeDistributionProps } -export { TorrentSeedTimeDistribution, TorrentSeedTimeDistribution as SeedTimeDistribution } +export { TorrentSeedTimeDistribution } diff --git a/src/components/charts/lib/index.ts b/src/components/charts/lib/index.ts deleted file mode 100644 index bc2a9093..00000000 --- a/src/components/charts/lib/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -// src/components/charts/lib/index.ts -// -// Barrel re-export for all chart support files. - -export type { ChartEChartsProps } from "./ChartECharts" -export { ChartECharts } from "./ChartECharts" -export type { ChartEmptyStateProps } from "./ChartEmptyState" -export { ChartEmptyState } from "./ChartEmptyState" -export * from "./chart-helpers" -export * from "./chart-transforms" -export type { LogScaleToggleProps } from "./LogScaleToggle" -export { LogScaleToggle } from "./LogScaleToggle" -export * from "./theme" -export { useLogScale } from "./useLogScale" diff --git a/src/lib/notifications/types.ts b/src/lib/notifications/types.ts index 11381032..424836b8 100644 --- a/src/lib/notifications/types.ts +++ b/src/lib/notifications/types.ts @@ -18,8 +18,6 @@ export const VALID_EVENT_TYPES = [ ] as const export type NotificationEventType = (typeof VALID_EVENT_TYPES)[number] -export type NotificationDeliveryStatus = "delivered" | "failed" | "rate_limited" - export interface NotificationThresholds { ratioDropDelta?: number // i.e 0.1 — alert when ratio falls by ≥0.1 bufferMilestoneBytes?: number //i.e 10737418240 — alert when buffer crosses 10 GiB From 303c6f5b21195a9a15a66f227dab4c022eca36b9 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:31:44 -0500 Subject: [PATCH 35/59] fix: update drizzle-kit, drizzle-orm, and postgres to specific versions in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a629cb17..d1382d81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ RUN pnpm build FROM base AS schema-deps WORKDIR /schema-sync RUN printf '{"private":true,"pnpm":{"overrides":{"esbuild":">=0.25.0"}}}\n' > package.json \ - && pnpm add drizzle-kit drizzle-orm postgres + && pnpm add drizzle-kit@0.31.10 drizzle-orm@0.45.1 postgres@3.4.8 # --------------------------------------------------------------------------- # Stage 4 — Production runner From 60f5786134f346e4ba07684d210f3806bd384468 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:36:30 -0500 Subject: [PATCH 36/59] fix: ensure backfill flag is set after successful checkpoint backfill --- src/app/api/dashboard/today/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/dashboard/today/route.ts b/src/app/api/dashboard/today/route.ts index 1ad6b186..58948fb1 100644 --- a/src/app/api/dashboard/today/route.ts +++ b/src/app/api/dashboard/today/route.ts @@ -14,9 +14,9 @@ export async function GET() { try { // One-time backfill on first request that populates checkpoint table from existing snapshots if (!g.__backfillDone) { - g.__backfillDone = true try { const filled = await backfillTrackerCheckpoints() + g.__backfillDone = true if (filled > 0) log.info(`Backfilled ${filled} tracker daily checkpoints`) } catch (err) { log.error(err, "Checkpoint backfill failed") From 0b07d40ddaca93f188eeb166e53431764cb1bb8e Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:39:07 -0500 Subject: [PATCH 37/59] fix: improve error handling for decryption failures in fetchAndMergeTorrents --- src/lib/qbt/fetch-merged.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/qbt/fetch-merged.ts b/src/lib/qbt/fetch-merged.ts index 754eb328..e7d9cb03 100644 --- a/src/lib/qbt/fetch-merged.ts +++ b/src/lib/qbt/fetch-merged.ts @@ -5,7 +5,7 @@ import "server-only" import { decryptClientCredentials } from "@/lib/client-decrypt" -import { sanitizeNetworkError } from "@/lib/error-utils" +import { isDecryptionError, sanitizeNetworkError } from "@/lib/error-utils" import { getTorrents, parseCrossSeedTags, @@ -125,9 +125,9 @@ export async function fetchAndMergeTorrents( crossSeedClients.push({ crossSeedTags: result.value.crossSeedTags }) } else { const clientName = clients[i].name - const raw = result.reason instanceof Error ? result.reason.message : "Unknown error" - const isDecrypt = /decrypt|crypt|EVP_/i.test(raw) + const isDecrypt = isDecryptionError(result.reason) if (isDecrypt) decryptionFailureCount++ + const raw = result.reason instanceof Error ? result.reason.message : "Unknown error" const message = isDecrypt ? "Credential decryption failed" : sanitizeNetworkError(raw) clientErrors.push(`${clientName}: ${message}`) } From 90285d69b1dd709d0f77682ecb797a20fe3fea1c Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:40:34 -0500 Subject: [PATCH 38/59] fix: optimize torrent checkpoint insertion by batching database writes --- src/lib/client-scheduler.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/lib/client-scheduler.ts b/src/lib/client-scheduler.ts index 31f2ce0d..5063ee48 100644 --- a/src/lib/client-scheduler.ts +++ b/src/lib/client-scheduler.ts @@ -212,20 +212,23 @@ export async function deepPollClient(clientId: number, encryptionKey: Buffer): P (t) => t.uploaded != null && t.downloaded != null && t.hash && t.name ) if (checkpointable.length > 0) { + const CHUNK = 500 try { - await db - .insert(torrentDailyCheckpoints) - .values( - checkpointable.map((t) => ({ - clientId, - hash: t.hash, - name: t.name, - checkpointDate, - uploadedStart: BigInt(t.uploaded), - downloadedStart: BigInt(t.downloaded), - })) - ) - .onConflictDoNothing() + for (let i = 0; i < checkpointable.length; i += CHUNK) { + await db + .insert(torrentDailyCheckpoints) + .values( + checkpointable.slice(i, i + CHUNK).map((t) => ({ + clientId, + hash: t.hash, + name: t.name, + checkpointDate, + uploadedStart: BigInt(t.uploaded), + downloadedStart: BigInt(t.downloaded), + })) + ) + .onConflictDoNothing() + } } catch (err) { log.warn( `Torrent checkpoint insert failed for client ${clientId}: ${err instanceof Error ? err.message : "Unknown"}` From 762988f2228218cf27b2d12c138bb0e2dfa1c5b1 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:46:39 -0500 Subject: [PATCH 39/59] fix: normalize tracker tags to lowercase --- src/lib/today.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/today.ts b/src/lib/today.ts index 463ffc60..6d6f5981 100644 --- a/src/lib/today.ts +++ b/src/lib/today.ts @@ -248,7 +248,7 @@ export async function computeTodayAtAGlance(): Promise { const trackerTagToColor = new Map() for (const tracker of allTrackers) { if (tracker.qbtTag) { - trackerTagToColor.set(tracker.qbtTag, tracker.color ?? null) + trackerTagToColor.set(tracker.qbtTag.toLowerCase(), tracker.color ?? null) } } @@ -287,7 +287,7 @@ export async function computeTodayAtAGlance(): Promise { let matchedTag: string | null = null let matchedColor: string | null = null if (torrent.tags) { - const torrentTags = parseTorrentTags(torrent.tags, false) + const torrentTags = parseTorrentTags(torrent.tags) for (const tag of torrentTags) { if (trackerTagToColor.has(tag)) { matchedTag = tag From 0b465b6c139333ebcb8650fcf8224d5a70076bee Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 10:47:59 -0500 Subject: [PATCH 40/59] fix: use localDateStr for cutoff date in pruneOldCheckpoints function --- src/lib/scheduler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index bb302f35..829d8a16 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -373,9 +373,7 @@ export async function pruneOldSnapshots(retentionDays: number): Promise } export async function pruneOldCheckpoints(retentionDays: number): Promise { - const cutoffDate = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000) - .toISOString() - .slice(0, 10) + const cutoffDate = localDateStr(Date.now() - retentionDays * 24 * 60 * 60 * 1000) const deletedTracker = await db .delete(trackerDailyCheckpoints) From d00700e3145c6ecb6067839b68da916aa52b0946 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 11:04:15 -0500 Subject: [PATCH 41/59] chore(ci): add copilot instructions for code reviews --- .github/copilot-instructions.md | 207 ++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..93e73ea1 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,207 @@ +# Copilot Instructions — Tracker Tracker + +## Project + +Self-hosted single-user dashboard for monitoring private tracker stats. Next.js 16 App Router, PostgreSQL, Drizzle ORM, Tailwind v4, Vitest. + +## Package Manager + +pnpm only. Never use npm or yarn. Node.js >= 22 required. + +## Linting and Formatting + +- Biome for linting (`pnpm lint` runs `pnpm tsc && biome check .`). No ESLint. +- Prettier for formatting (`pnpm format`). Biome's formatter is disabled. +- TypeScript strict mode. Check with `pnpm tsc`. + +## File Conventions + +- First line of every JS/TS file: `// relative/path/to/file.tsx` +- If a file has 5+ functions, add a comment block listing them after the filepath comment +- Client components need explicit `"use client"` directive +- Server-only modules use `import "server-only"` at the top + +## Imports + +- Do not import the entire React package. Destructure: `import { useState, useEffect } from "react"` not `import React from "react"` +- Use `import type { ... }` for type-only imports. +- Path aliases: `@/` maps to `src/`, `@typography` maps to Typography components. +- Import order is enforced by Biome's `organizeImports` rule. Components before lib before types. + +## Database (Drizzle ORM) + +- ALWAYS use Drizzle's schema-first approach. NEVER use raw SQL migrations. +- Schema lives in `src/lib/db/schema.ts`. Use `drizzle-kit push` to sync schema to DB. +- `DISTINCT ON` is supported natively via `db.selectDistinctOn()`. Do not use `db.execute(sql\`...\`)` for this. +- Use explicit column projections in `.select({ ... })` instead of `.select()` (which fetches all columns). +- BigInt columns use `{ mode: "bigint" }` and return native `bigint` values from queries. +- Upserts use `.onConflictDoUpdate()` or `.onConflictDoNothing()`. + +## BigInt Handling + +- Tracker upload/download/buffer values are stored as `bigint` in the database. +- Always wrap BigInt conversions in try/catch when processing external data. +- `Number.MAX_SAFE_INTEGER` is ~9 PiB. Daily deltas will never approach this. Do not over-engineer BigInt→Number conversions for percentage calculations on daily deltas. +- Serialize bigints as `.toString()` in API responses (JSON cannot represent BigInt). + +## Timezone + +- All date strings for checkpoint tables and daily comparisons use `localDateStr()` from `src/lib/formatters.ts`, which respects the `TZ` environment variable. +- Do NOT use `.toISOString().slice(0, 10)` for checkpoint dates — that returns UTC, not the user's configured timezone. +- When `TZ` is not set, `localDateStr()` falls back to UTC (same behavior as `toISOString`). + +## Error Handling + +- All catch blocks must either log the error (`log.warn`, `log.error` from `@/lib/logger`) or re-throw. Empty catch blocks trigger the security audit. +- Use `log` from `@/lib/logger` (pino), not `console.log` / `console.error`. +- Decryption errors (stale session keys) should return HTTP 401, not 422 or 500. Use `isDecryptionError()` from `@/lib/error-utils`. +- Scheduler and poll operations wrap checkpoint writes in try/catch so failures don't break the poll cycle. + +## API Routes + +- Every route handler must call `authenticate()` from `@/lib/api-helpers` and check the result before proceeding. +- Use `NextResponse.json()` for responses. +- Sensitive data (encrypted tokens, password hashes, encryption salts) must never appear in API responses. Column projections exclude them structurally. + +## Authentication + +Single-user app with master password auth. No third-party providers. + +- **Three-layer defense in depth:** `proxy.ts` (cookie presence check) → `getSession()` (JWE decrypt) → route handler (`authenticate()` + `decodeKey()`) +- **Session:** JWE (encrypted JWT via `EncryptJWT`, not `SignJWT`), stored in `tt_session` httpOnly/secure/sameSite=strict cookie. Contains `{ ek: encryptionKeyHex }`. +- **Key derivation:** master password → `scrypt(password, salt, 32)` → AES-256-GCM key. This key decrypts all API tokens in the DB. +- **JWE key:** derived from `SESSION_SECRET` env via HKDF-SHA256 with domain-separated info labels. +- **Sliding session refresh:** `proxy.ts` re-sets cookie expiry on every request without decrypting the JWE. +- **Lockout:** atomic SQL counter (`failedLoginAttempts + 1`), configurable threshold, returns 429 with Retry-After. Both login and TOTP verify paths increment. +- **TOTP 2FA:** optional, uses `otpauth` library. Backup codes are hex `XXXX-XXXX`, compared with `timingSafeEqual`. +- **Stale sessions:** when the encryption key doesn't match (i.e., password changed), AES-GCM auth tag fails. `isDecryptionError()` catches this → return 401, not 422/500. +- **Key zeroing:** `stopScheduler()` zero-fills the key buffer on logout. +- **Public routes:** defined in `proxy.ts` as `PUBLIC_EXACT` and `PUBLIC_PREFIX` arrays. All other routes require the cookie. + +Route handler pattern: +```ts +const auth = await authenticate() +if (auth instanceof NextResponse) return auth // 401 +const key = decodeKey(auth) // Buffer +// use key to decrypt DB fields +``` + +## Security (CI Audit) + +- A static security audit runs in CI (`scripts/security-audit.ts`). It checks for: missing auth on routes, hardcoded secrets, bare catch blocks, console.log in routes, raw SQL in routes, unvalidated JSON parse, unsafe redirects, missing fetch timeouts, and more. +- Suppress findings with `// security-audit-ignore: ` on the line above. A bare suppression without a reason is itself a critical failure. +- URL inputs are validated against SSRF via `isUnsafeNetworkHost()` in `src/lib/network.ts`. + +## Design System + +- Dark neumorphic aesthetic. No hex values in component files — use Tailwind semantic tokens. +- Depth: `nm-raised`, `nm-raised-sm` for raised elements. `nm-inset`, `nm-inset-sm` for recessed elements. +- Colors: `text-upload` (cyan), `text-download` (amber), `text-positive` (lime), `text-negative` (red). +- Per-tracker colors are the only allowed inline `style={{ backgroundColor }}` values. +- Chart colors come from `CHART_THEME` in `src/components/charts/lib/theme.ts`. Never hardcode hex in chart files. +- Typography: `font-mono` (JetBrains Mono) for data values. `font-sans` (Archivo) for UI text. Use `tabular-nums` on numeric data for column alignment. +- Chart axis labels use `textTertiary` (not `textMuted`). Chart tooltip HTML must escape user data with `escHtml()`. +- Border radius tokens: `rounded-nm-sm` (8px), `rounded-nm-md` (12px), `rounded-nm-lg` (20px), `rounded-nm-pill` (9999px). +- No shadcn/ui. This project uses a fully custom component library with `cva` (class-variance-authority) for variants and `clsx` for class merging. + +## Component Patterns + +- `StatCard` has 3 variants (basic, stacked, ring) — they render different JSX structures, not just different classes. +- `ProgressBar` in `src/components/ui/ProgressBar.tsx` — reusable neumorphic progress bar matching the Toggle track/knob depth pattern. +- `MarqueeText` for long torrent/tracker names instead of CSS `truncate`. +- `Tooltip` accepts `ReactNode` content, not just strings. Use JSX with `flex-col` for multi-line tooltips. +- Never use native `` — use the `` component. +- Never use native `` component. +- Component anatomy: internal sub-components are unexported functions within the file. Single public export at the bottom. Types exported with `export type { ... }`. +- Use React 19's ref-as-prop pattern (direct `ref` prop), not `forwardRef`. +- SVG path calculations with floating-point geometry should round to 2 decimal places for SSR hydration safety. + +## HMR Safety + +All module-level singletons (cron tasks, DB client, qBT SID cache) must be stored on `globalThis` to survive webpack HMR reloads in development. Without this, hot-reloads orphan the old instance while creating a new one. + +## Testing + +- Framework: Vitest. +- Test mocks must match real API response shapes. Do not add fields (i.e., `isPrivate: true`) that the real API does not return — this masks bugs. +- Do not add test-only methods to production classes. Put test utilities in test files. +- qBittorrent's API returns snake_case fields (`is_private`, `added_on`). The `QbtTorrent` TypeScript type uses camelCase for some fields but there is no automatic mapping — raw API responses have snake_case. + +## Commit Messages + +- Never mention AI, Claude, or LLMs in commit messages. +- Use conventional commits: `feat(scope):`, `fix(scope):`, `style(scope):`, `chore(scope):`. +- One-line commit messages only. + +## Code Organization + +- Page files (`page.tsx`) should be thin wrappers — mostly orchestration and render logic. Extract business logic, UI sections, and data fetching into components and hooks. +- Repeated code should be extracted to helper functions or reusable components. Do not duplicate logic across files. +- Centralized constants belong in `src/lib/constants.ts`. Avoid magic strings and magic numbers scattered throughout the codebase. + +## SSR / Hydration + +- NEVER read `localStorage` in `useState` initializers — causes hydration mismatch. Initialize with a server-safe default, then hydrate in `useEffect`. +- Page components (`page.tsx`) should be thin wrappers. Business logic, UI sections, and data fetching belong in extracted components and hooks. + +## Writing Style + +- Always use "i.e." never "e.g." in comments, documentation, and UI text. +- Do not use em dashes, en dashes, curly quotes, or other non-ASCII punctuation in comments, strings, or error messages. Stick to plain ASCII: commas, periods, hyphens, straight quotes. IDEs and terminals choke on fancy Unicode punctuation. + +## Adapter Pattern + +- Tracker platforms use an adapter pattern: `TrackerAdapter` interface with `getAdapter()` factory. UNIT3D, Gazelle, and GGn are implemented. Adding a new platform means adding a new adapter file in `src/lib/adapters/`, not modifying existing ones. + +## qBittorrent Integration + +- The qBT Web API returns snake_case field names (`is_private`, `added_on`). The `QbtTorrent` TypeScript interface uses camelCase for some fields but `getTorrents()` returns raw JSON with NO field mapping. Do not assume camelCase fields exist on raw API responses. +- Tag matching must be case-insensitive. The aggregator lowercases map keys. `parseTorrentTags()` lowercases by default. +- The heartbeat loop (5s) must NOT update `lastPolledAt` — only the deep poll (5min) writes it. Otherwise the deep poll's overdue check never triggers. + +## Precision Concerns + +- Daily upload/download deltas are computed as BigInt subtraction. Converting daily deltas to Number for percentage calculations is safe — `Number.MAX_SAFE_INTEGER` (~9 PiB) vastly exceeds any realistic daily transfer. Do not over-engineer BigInt arithmetic for percentage computations on daily deltas. + +## External HTTP Requests + +- All `fetch()` calls must include a timeout: `fetch(url, { signal: AbortSignal.timeout(15_000) })`. This is a CI-blocking security audit check. +- URLs from user input must be validated with `validateHttpUrl()` before use in `fetch()`. +- Error messages must not contain full URLs (which may include API tokens). Use `sanitizeNetworkError()`. + +## Validation + +- Use centralized helpers from `src/lib/api-helpers.ts`: `authenticate()`, `parseJsonBody()`, `parseRouteId()`, `validateHttpUrl()`, `validateHexColor()`, `validatePort()`, `validateJoinedAt()`. +- All validators return `NextResponse | null` — check with `if (err) return err`. +- String inputs: check type, trim, enforce length limits (i.e., name <= 100 chars, URL <= 500 chars). + +## Encryption + +- AES-256-GCM with random 12-byte IV. Format: `base64(iv + authTag + ciphertext)`. +- Key derivation: `scrypt(password, salt, 32, { N: 16384, r: 8, p: 1 })`. +- Use `encrypt()`/`decrypt()` from `src/lib/crypto.ts`. Keys are always `Buffer`. +- Encryption keys are zeroed in memory on logout via `stopScheduler()`. + +## Accessibility + +- `useId()` for stable label/input ID association in form controls. +- `aria-invalid` and `aria-describedby` on inputs with errors. +- `aria-hidden="true"` on decorative icons/SVGs. +- `aria-label` on icon-only buttons. + +## Things NOT to Do + +- Do not use the terms "Production-Ready" or "Enterprise" anywhere. +- Do not use ESLint. It has been deprecated for this project. +- Do not use `npm` or `yarn` — `pnpm` only. +- Do not create raw SQL migration files. Use `drizzle-kit push`. +- Do not use `.toISOString().slice(0, 10)` for checkpoint/daily dates — use `localDateStr()`. +- Do not pass unknown props through to DOM elements via `{...rest}` spreads without filtering component-only props first. +- Do not use dynamic code execution or inject raw HTML into the DOM. See `scripts/security-audit.ts` for the full list of banned functions. +- Do not use `===` for secret comparisons — use `timingSafeEqual`. +- Do not use `Number()` or `parseInt()` directly on BigInt database columns. +- Do not commit `.env` files. +- Do not use `JSON.parse()` without a try/catch wrapper. +- Do not use `console.log` in API routes — use the `log` instance from `@/lib/logger`. +- Do not use `db.execute()` in API routes (raw SQL is banned except for the health check `SELECT 1`). +- Do not use function names like "enhanced", "wrapper", or "util" — be specific and descriptive. From 5e6d58e361fbcfa6323e414b3702768d895d85d7 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 11:04:53 -0500 Subject: [PATCH 42/59] fix(backups): enforce maximum length for backup password to 128 characters --- src/app/api/settings/backup/export/route.ts | 6 ++++++ src/app/api/settings/backup/restore/route.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/app/api/settings/backup/export/route.ts b/src/app/api/settings/backup/export/route.ts index 67248150..960f5653 100644 --- a/src/app/api/settings/backup/export/route.ts +++ b/src/app/api/settings/backup/export/route.ts @@ -26,6 +26,12 @@ export async function POST(request: Request) { // Resolve backup password: explicit form value > stored encrypted password let backupPassword: string | null = null if (formPassword && typeof formPassword === "string" && formPassword.length > 0) { + if (formPassword.length > 128) { + return NextResponse.json( + { error: "Backup password must be 128 characters or fewer" }, + { status: 400 } + ) + } backupPassword = formPassword } else if (settings?.backupEncryptionEnabled && settings.encryptedBackupPassword) { try { diff --git a/src/app/api/settings/backup/restore/route.ts b/src/app/api/settings/backup/restore/route.ts index 60fc75e4..73d21fbe 100644 --- a/src/app/api/settings/backup/restore/route.ts +++ b/src/app/api/settings/backup/restore/route.ts @@ -134,6 +134,12 @@ export async function POST(request: Request) { { status: 400 } ) } + if (backupPassword.length > 128) { + return NextResponse.json( + { error: "Backup password must be 128 characters or fewer" }, + { status: 400 } + ) + } const backupEnvelope = envelope as unknown as EncryptedBackupEnvelope if (!backupEnvelope.encryptionSalt || typeof backupEnvelope.encryptionSalt !== "string") { From 9080e20a6bf04799b0051eced0e415a1a17df2b7 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 11:14:20 -0500 Subject: [PATCH 43/59] chore(ci): whoops wrong spot --- .github/copilot-instructions.md | 207 -------------------------------- 1 file changed, 207 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 93e73ea1..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,207 +0,0 @@ -# Copilot Instructions — Tracker Tracker - -## Project - -Self-hosted single-user dashboard for monitoring private tracker stats. Next.js 16 App Router, PostgreSQL, Drizzle ORM, Tailwind v4, Vitest. - -## Package Manager - -pnpm only. Never use npm or yarn. Node.js >= 22 required. - -## Linting and Formatting - -- Biome for linting (`pnpm lint` runs `pnpm tsc && biome check .`). No ESLint. -- Prettier for formatting (`pnpm format`). Biome's formatter is disabled. -- TypeScript strict mode. Check with `pnpm tsc`. - -## File Conventions - -- First line of every JS/TS file: `// relative/path/to/file.tsx` -- If a file has 5+ functions, add a comment block listing them after the filepath comment -- Client components need explicit `"use client"` directive -- Server-only modules use `import "server-only"` at the top - -## Imports - -- Do not import the entire React package. Destructure: `import { useState, useEffect } from "react"` not `import React from "react"` -- Use `import type { ... }` for type-only imports. -- Path aliases: `@/` maps to `src/`, `@typography` maps to Typography components. -- Import order is enforced by Biome's `organizeImports` rule. Components before lib before types. - -## Database (Drizzle ORM) - -- ALWAYS use Drizzle's schema-first approach. NEVER use raw SQL migrations. -- Schema lives in `src/lib/db/schema.ts`. Use `drizzle-kit push` to sync schema to DB. -- `DISTINCT ON` is supported natively via `db.selectDistinctOn()`. Do not use `db.execute(sql\`...\`)` for this. -- Use explicit column projections in `.select({ ... })` instead of `.select()` (which fetches all columns). -- BigInt columns use `{ mode: "bigint" }` and return native `bigint` values from queries. -- Upserts use `.onConflictDoUpdate()` or `.onConflictDoNothing()`. - -## BigInt Handling - -- Tracker upload/download/buffer values are stored as `bigint` in the database. -- Always wrap BigInt conversions in try/catch when processing external data. -- `Number.MAX_SAFE_INTEGER` is ~9 PiB. Daily deltas will never approach this. Do not over-engineer BigInt→Number conversions for percentage calculations on daily deltas. -- Serialize bigints as `.toString()` in API responses (JSON cannot represent BigInt). - -## Timezone - -- All date strings for checkpoint tables and daily comparisons use `localDateStr()` from `src/lib/formatters.ts`, which respects the `TZ` environment variable. -- Do NOT use `.toISOString().slice(0, 10)` for checkpoint dates — that returns UTC, not the user's configured timezone. -- When `TZ` is not set, `localDateStr()` falls back to UTC (same behavior as `toISOString`). - -## Error Handling - -- All catch blocks must either log the error (`log.warn`, `log.error` from `@/lib/logger`) or re-throw. Empty catch blocks trigger the security audit. -- Use `log` from `@/lib/logger` (pino), not `console.log` / `console.error`. -- Decryption errors (stale session keys) should return HTTP 401, not 422 or 500. Use `isDecryptionError()` from `@/lib/error-utils`. -- Scheduler and poll operations wrap checkpoint writes in try/catch so failures don't break the poll cycle. - -## API Routes - -- Every route handler must call `authenticate()` from `@/lib/api-helpers` and check the result before proceeding. -- Use `NextResponse.json()` for responses. -- Sensitive data (encrypted tokens, password hashes, encryption salts) must never appear in API responses. Column projections exclude them structurally. - -## Authentication - -Single-user app with master password auth. No third-party providers. - -- **Three-layer defense in depth:** `proxy.ts` (cookie presence check) → `getSession()` (JWE decrypt) → route handler (`authenticate()` + `decodeKey()`) -- **Session:** JWE (encrypted JWT via `EncryptJWT`, not `SignJWT`), stored in `tt_session` httpOnly/secure/sameSite=strict cookie. Contains `{ ek: encryptionKeyHex }`. -- **Key derivation:** master password → `scrypt(password, salt, 32)` → AES-256-GCM key. This key decrypts all API tokens in the DB. -- **JWE key:** derived from `SESSION_SECRET` env via HKDF-SHA256 with domain-separated info labels. -- **Sliding session refresh:** `proxy.ts` re-sets cookie expiry on every request without decrypting the JWE. -- **Lockout:** atomic SQL counter (`failedLoginAttempts + 1`), configurable threshold, returns 429 with Retry-After. Both login and TOTP verify paths increment. -- **TOTP 2FA:** optional, uses `otpauth` library. Backup codes are hex `XXXX-XXXX`, compared with `timingSafeEqual`. -- **Stale sessions:** when the encryption key doesn't match (i.e., password changed), AES-GCM auth tag fails. `isDecryptionError()` catches this → return 401, not 422/500. -- **Key zeroing:** `stopScheduler()` zero-fills the key buffer on logout. -- **Public routes:** defined in `proxy.ts` as `PUBLIC_EXACT` and `PUBLIC_PREFIX` arrays. All other routes require the cookie. - -Route handler pattern: -```ts -const auth = await authenticate() -if (auth instanceof NextResponse) return auth // 401 -const key = decodeKey(auth) // Buffer -// use key to decrypt DB fields -``` - -## Security (CI Audit) - -- A static security audit runs in CI (`scripts/security-audit.ts`). It checks for: missing auth on routes, hardcoded secrets, bare catch blocks, console.log in routes, raw SQL in routes, unvalidated JSON parse, unsafe redirects, missing fetch timeouts, and more. -- Suppress findings with `// security-audit-ignore: ` on the line above. A bare suppression without a reason is itself a critical failure. -- URL inputs are validated against SSRF via `isUnsafeNetworkHost()` in `src/lib/network.ts`. - -## Design System - -- Dark neumorphic aesthetic. No hex values in component files — use Tailwind semantic tokens. -- Depth: `nm-raised`, `nm-raised-sm` for raised elements. `nm-inset`, `nm-inset-sm` for recessed elements. -- Colors: `text-upload` (cyan), `text-download` (amber), `text-positive` (lime), `text-negative` (red). -- Per-tracker colors are the only allowed inline `style={{ backgroundColor }}` values. -- Chart colors come from `CHART_THEME` in `src/components/charts/lib/theme.ts`. Never hardcode hex in chart files. -- Typography: `font-mono` (JetBrains Mono) for data values. `font-sans` (Archivo) for UI text. Use `tabular-nums` on numeric data for column alignment. -- Chart axis labels use `textTertiary` (not `textMuted`). Chart tooltip HTML must escape user data with `escHtml()`. -- Border radius tokens: `rounded-nm-sm` (8px), `rounded-nm-md` (12px), `rounded-nm-lg` (20px), `rounded-nm-pill` (9999px). -- No shadcn/ui. This project uses a fully custom component library with `cva` (class-variance-authority) for variants and `clsx` for class merging. - -## Component Patterns - -- `StatCard` has 3 variants (basic, stacked, ring) — they render different JSX structures, not just different classes. -- `ProgressBar` in `src/components/ui/ProgressBar.tsx` — reusable neumorphic progress bar matching the Toggle track/knob depth pattern. -- `MarqueeText` for long torrent/tracker names instead of CSS `truncate`. -- `Tooltip` accepts `ReactNode` content, not just strings. Use JSX with `flex-col` for multi-line tooltips. -- Never use native `` — use the `` component. -- Never use native `` component. -- Component anatomy: internal sub-components are unexported functions within the file. Single public export at the bottom. Types exported with `export type { ... }`. -- Use React 19's ref-as-prop pattern (direct `ref` prop), not `forwardRef`. -- SVG path calculations with floating-point geometry should round to 2 decimal places for SSR hydration safety. - -## HMR Safety - -All module-level singletons (cron tasks, DB client, qBT SID cache) must be stored on `globalThis` to survive webpack HMR reloads in development. Without this, hot-reloads orphan the old instance while creating a new one. - -## Testing - -- Framework: Vitest. -- Test mocks must match real API response shapes. Do not add fields (i.e., `isPrivate: true`) that the real API does not return — this masks bugs. -- Do not add test-only methods to production classes. Put test utilities in test files. -- qBittorrent's API returns snake_case fields (`is_private`, `added_on`). The `QbtTorrent` TypeScript type uses camelCase for some fields but there is no automatic mapping — raw API responses have snake_case. - -## Commit Messages - -- Never mention AI, Claude, or LLMs in commit messages. -- Use conventional commits: `feat(scope):`, `fix(scope):`, `style(scope):`, `chore(scope):`. -- One-line commit messages only. - -## Code Organization - -- Page files (`page.tsx`) should be thin wrappers — mostly orchestration and render logic. Extract business logic, UI sections, and data fetching into components and hooks. -- Repeated code should be extracted to helper functions or reusable components. Do not duplicate logic across files. -- Centralized constants belong in `src/lib/constants.ts`. Avoid magic strings and magic numbers scattered throughout the codebase. - -## SSR / Hydration - -- NEVER read `localStorage` in `useState` initializers — causes hydration mismatch. Initialize with a server-safe default, then hydrate in `useEffect`. -- Page components (`page.tsx`) should be thin wrappers. Business logic, UI sections, and data fetching belong in extracted components and hooks. - -## Writing Style - -- Always use "i.e." never "e.g." in comments, documentation, and UI text. -- Do not use em dashes, en dashes, curly quotes, or other non-ASCII punctuation in comments, strings, or error messages. Stick to plain ASCII: commas, periods, hyphens, straight quotes. IDEs and terminals choke on fancy Unicode punctuation. - -## Adapter Pattern - -- Tracker platforms use an adapter pattern: `TrackerAdapter` interface with `getAdapter()` factory. UNIT3D, Gazelle, and GGn are implemented. Adding a new platform means adding a new adapter file in `src/lib/adapters/`, not modifying existing ones. - -## qBittorrent Integration - -- The qBT Web API returns snake_case field names (`is_private`, `added_on`). The `QbtTorrent` TypeScript interface uses camelCase for some fields but `getTorrents()` returns raw JSON with NO field mapping. Do not assume camelCase fields exist on raw API responses. -- Tag matching must be case-insensitive. The aggregator lowercases map keys. `parseTorrentTags()` lowercases by default. -- The heartbeat loop (5s) must NOT update `lastPolledAt` — only the deep poll (5min) writes it. Otherwise the deep poll's overdue check never triggers. - -## Precision Concerns - -- Daily upload/download deltas are computed as BigInt subtraction. Converting daily deltas to Number for percentage calculations is safe — `Number.MAX_SAFE_INTEGER` (~9 PiB) vastly exceeds any realistic daily transfer. Do not over-engineer BigInt arithmetic for percentage computations on daily deltas. - -## External HTTP Requests - -- All `fetch()` calls must include a timeout: `fetch(url, { signal: AbortSignal.timeout(15_000) })`. This is a CI-blocking security audit check. -- URLs from user input must be validated with `validateHttpUrl()` before use in `fetch()`. -- Error messages must not contain full URLs (which may include API tokens). Use `sanitizeNetworkError()`. - -## Validation - -- Use centralized helpers from `src/lib/api-helpers.ts`: `authenticate()`, `parseJsonBody()`, `parseRouteId()`, `validateHttpUrl()`, `validateHexColor()`, `validatePort()`, `validateJoinedAt()`. -- All validators return `NextResponse | null` — check with `if (err) return err`. -- String inputs: check type, trim, enforce length limits (i.e., name <= 100 chars, URL <= 500 chars). - -## Encryption - -- AES-256-GCM with random 12-byte IV. Format: `base64(iv + authTag + ciphertext)`. -- Key derivation: `scrypt(password, salt, 32, { N: 16384, r: 8, p: 1 })`. -- Use `encrypt()`/`decrypt()` from `src/lib/crypto.ts`. Keys are always `Buffer`. -- Encryption keys are zeroed in memory on logout via `stopScheduler()`. - -## Accessibility - -- `useId()` for stable label/input ID association in form controls. -- `aria-invalid` and `aria-describedby` on inputs with errors. -- `aria-hidden="true"` on decorative icons/SVGs. -- `aria-label` on icon-only buttons. - -## Things NOT to Do - -- Do not use the terms "Production-Ready" or "Enterprise" anywhere. -- Do not use ESLint. It has been deprecated for this project. -- Do not use `npm` or `yarn` — `pnpm` only. -- Do not create raw SQL migration files. Use `drizzle-kit push`. -- Do not use `.toISOString().slice(0, 10)` for checkpoint/daily dates — use `localDateStr()`. -- Do not pass unknown props through to DOM elements via `{...rest}` spreads without filtering component-only props first. -- Do not use dynamic code execution or inject raw HTML into the DOM. See `scripts/security-audit.ts` for the full list of banned functions. -- Do not use `===` for secret comparisons — use `timingSafeEqual`. -- Do not use `Number()` or `parseInt()` directly on BigInt database columns. -- Do not commit `.env` files. -- Do not use `JSON.parse()` without a try/catch wrapper. -- Do not use `console.log` in API routes — use the `log` instance from `@/lib/logger`. -- Do not use `db.execute()` in API routes (raw SQL is banned except for the health check `SELECT 1`). -- Do not use function names like "enhanced", "wrapper", or "util" — be specific and descriptive. From 67ff4961e5d6ba50655ed0a9229ba807a26e1bbe Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 11:57:26 -0500 Subject: [PATCH 44/59] feat(settings): display database size --- src/app/(auth)/settings/SettingsClient.tsx | 8 +++++++- src/app/(auth)/settings/page.tsx | 10 +++++++--- src/app/api/clients/[id]/torrents/route.ts | 2 +- .../settings/SecurityPoliciesSection.tsx | 7 +++++++ src/lib/client-scheduler.ts | 4 ++-- src/lib/scheduler.ts | 4 ++-- src/lib/server-data.ts | 14 +++++++++++++- 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/app/(auth)/settings/SettingsClient.tsx b/src/app/(auth)/settings/SettingsClient.tsx index db830938..0a8b1cbf 100644 --- a/src/app/(auth)/settings/SettingsClient.tsx +++ b/src/app/(auth)/settings/SettingsClient.tsx @@ -64,9 +64,14 @@ export interface SettingsData { export interface SettingsClientProps { initialSettings: SettingsData initialProxyTrackers: { id: number; name: string; color: string }[] + databaseSize?: string } -export function SettingsClient({ initialSettings, initialProxyTrackers }: SettingsClientProps) { +export function SettingsClient({ + initialSettings, + initialProxyTrackers, + databaseSize, +}: SettingsClientProps) { const [activeTab, setActiveTab] = useState("general") return ( @@ -102,6 +107,7 @@ export function SettingsClient({ initialSettings, initialProxyTrackers }: Settin }} initialSnapshotRetentionDays={initialSettings.snapshotRetentionDays} initialSessionTimeoutMinutes={initialSettings.sessionTimeoutMinutes} + databaseSize={databaseSize} /> + return } diff --git a/src/app/api/clients/[id]/torrents/route.ts b/src/app/api/clients/[id]/torrents/route.ts index 6c37767c..fcb1f7bd 100644 --- a/src/app/api/clients/[id]/torrents/route.ts +++ b/src/app/api/clients/[id]/torrents/route.ts @@ -21,7 +21,7 @@ export async function GET(request: Request, props: { params: Promise<{ id: strin const url = new URL(request.url) const tag = url.searchParams.get("tag") - if (!tag || !tag.trim()) { + if (!tag?.trim()) { return NextResponse.json({ error: "tag query parameter is required" }, { status: 400 }) } diff --git a/src/components/settings/SecurityPoliciesSection.tsx b/src/components/settings/SecurityPoliciesSection.tsx index 94a3de5b..abbe573c 100644 --- a/src/components/settings/SecurityPoliciesSection.tsx +++ b/src/components/settings/SecurityPoliciesSection.tsx @@ -22,12 +22,14 @@ export interface SecurityPoliciesSectionProps { initialLockout: LockoutConfig initialSnapshotRetentionDays: number | null initialSessionTimeoutMinutes: number | null + databaseSize?: string } export function SecurityPoliciesSection({ initialLockout, initialSnapshotRetentionDays, initialSessionTimeoutMinutes, + databaseSize, }: SecurityPoliciesSectionProps) { // ── Auto-lockout ─────────────────────────────────────────────────── const [lockoutEnabled, setLockoutEnabled] = useState(initialLockout.enabled) @@ -258,6 +260,11 @@ export function SecurityPoliciesSection({ Automatically prunes historical snapshot data older than the configured period. Reduces what's stored on disk. + {databaseSize && ( +

+ Current database size: {databaseSize} +

+ )} {retentionError && (

{retentionError} diff --git a/src/lib/client-scheduler.ts b/src/lib/client-scheduler.ts index 5063ee48..349c9823 100644 --- a/src/lib/client-scheduler.ts +++ b/src/lib/client-scheduler.ts @@ -98,7 +98,7 @@ async function heartbeatClient(clientId: number, encryptionKey: Buffer): Promise .where(eq(downloadClients.id, clientId)) .limit(1) - if (!client || !client.enabled) return + if (!client?.enabled) return if (!client.encryptedUsername || !client.encryptedPassword) return try { @@ -158,7 +158,7 @@ export async function deepPollClient(clientId: number, encryptionKey: Buffer): P .where(eq(downloadClients.id, clientId)) .limit(1) - if (!client || !client.enabled) return + if (!client?.enabled) return if (!client.encryptedUsername || !client.encryptedPassword) return try { diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index 829d8a16..9cc45b23 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -76,7 +76,7 @@ export async function fetchTrackerStats( proxyAgent?: HttpAgent ): Promise<{ stats: TrackerStats; tracker: typeof trackers.$inferSelect }> { const [tracker] = await db.select().from(trackers).where(eq(trackers.id, trackerId)).limit(1) - if (!tracker || !tracker.isActive) throw new Error("Tracker not found or inactive") + if (!tracker?.isActive) throw new Error("Tracker not found or inactive") let apiToken: string try { @@ -132,7 +132,7 @@ export async function pollTracker( ): Promise { const [tracker] = await db.select().from(trackers).where(eq(trackers.id, trackerId)).limit(1) - if (!tracker || !tracker.isActive) return + if (!tracker?.isActive) return const timestamp = batchTimestamp ?? new Date() diff --git a/src/lib/server-data.ts b/src/lib/server-data.ts index 049dce53..755d2554 100644 --- a/src/lib/server-data.ts +++ b/src/lib/server-data.ts @@ -2,7 +2,7 @@ // // Functions: fetchSettings, serializeSettingsResponse, getSettingsForClient, // getTrackerListForDashboard, getTrackerForClient, getSnapshotsForTracker, -// getTagGroupsWithMembers, getProxyTrackers +// getTagGroupsWithMembers, getProxyTrackers, getDatabaseSize // Constants: settingsColumns, trackerColumns // // Server-side data fetchers: single source of truth for safe DB queries @@ -335,3 +335,15 @@ export async function getProxyTrackers(): Promise<{ id: number; name: string; co color: r.color ?? "#00d4ff", })) } + +// --------------------------------------------------------------------------- +// Database size +// --------------------------------------------------------------------------- + +// security-audit-ignore: read-only PostgreSQL system function, no user input +export async function getDatabaseSize(): Promise { + const rows = await db.execute( + sql`SELECT pg_size_pretty(pg_database_size(current_database())) as size` + ) + return (rows as unknown as Array<{ size: string }>)[0]?.size ?? "Unknown" +} From 23b4cae6420d17a1e1f86e9b925841726e28ccfe Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 12:12:15 -0500 Subject: [PATCH 45/59] feat(security): enhance security audit checks and improve vulnerability reporting --- .github/workflows/security-audit.yml | 25 +- scripts/security-audit.ts | 536 ++++++++++++++++++++++++++- 2 files changed, 543 insertions(+), 18 deletions(-) diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index d8119db8..c4c4f9a6 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -29,6 +29,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Check for known dependency vulnerabilities + run: pnpm audit --audit-level=high || echo "::warning::pnpm audit found high-severity vulnerabilities" + - name: Verify security test count run: | COUNT=$(pnpm test:run -- src/lib/__tests__/security.test.ts 2>&1 \ @@ -37,8 +40,8 @@ jobs: | grep -oE '[0-9]+ test' \ | grep -oE '[0-9]+') echo "Security tests: $COUNT" - if [ "$COUNT" -lt 98 ]; then - echo "::error::Security test count dropped below expected minimum (98). Was a test removed?" + if [ "$COUNT" -lt 106 ]; then + echo "::error::Security test count dropped below expected minimum (106). Was a test removed?" exit 1 fi @@ -46,10 +49,22 @@ jobs: id: security-audit continue-on-error: true run: | - pnpm exec tsx scripts/security-audit.ts > /tmp/security-results.json - echo "exit_code=$?" >> "$GITHUB_OUTPUT" + pnpm exec tsx scripts/security-audit.ts > /tmp/security-results.json; echo "audit_exit=$?" >> "$GITHUB_OUTPUT" cat /tmp/security-results.json + - name: Verify audit check count + run: | + if [ ! -f /tmp/security-results.json ]; then + echo "::error::Security audit produced no output" + exit 1 + fi + TOTAL=$(python3 -c "import json; print(json.load(open('/tmp/security-results.json'))['summary']['total'])") + echo "Audit checks: $TOTAL" + if [ "$TOTAL" -lt 36 ]; then + echo "::error::Audit check count dropped below expected minimum (36). Was a check removed?" + exit 1 + fi + - name: Comment on PR with security audit results if: github.event_name == 'pull_request' uses: actions/github-script@v8 @@ -214,5 +229,5 @@ jobs: } - name: Fail if security audit has critical findings - if: steps.security-audit.outcome == 'failure' + if: steps.security-audit.outputs.audit_exit != '0' run: exit 1 diff --git a/scripts/security-audit.ts b/scripts/security-audit.ts index c28f84f2..ee9bbf60 100644 --- a/scripts/security-audit.ts +++ b/scripts/security-audit.ts @@ -3,21 +3,26 @@ // Static security analysis for CI. // Outputs JSON to stdout: { results: CheckResult[], summary: { ... } } // -// Functions: walkFiles, routePathFromFile, getCachedLines, filterIgnoredFindings, +// Functions: walkFiles, relativePath, findAllLineNumbers, routePathFromFile, isTestFile, +// getCachedLines, filterIgnoredFindings, extractHandlerBodies, // checkAuthEnforcement, checkDangerousFunctions, checkHardcodedSecrets, // checkSecurityHeaders, checkCookieSecurity, checkSensitiveFieldExposure, // checkEnvFiles, checkConsoleLogInRoutes, checkTodoInSecurityFiles, // checkRawSqlInRoutes, checkUnvalidatedJsonParse, checkBareCatchBlocks, // checkUnsafeRedirectFetch, checkRequestBodySize, checkTimingSafeComparison, // checkNoRawMigrations, checkFetchTimeout, checkDockerfileNonRoot, -// checkProxyAllowlistSync, checkBigIntSafety, checkPathTraversalDefense, +// normalizeRoute, checkProxyAllowlistSync, checkBigIntSafety, checkPathTraversalDefense, // checkArgon2Hashing, checkEncryptedColumnWrites, checkTotpFlowIntegrity, // checkLockdownFlowIntegrity, checkNukeFlowIntegrity, -// checkBackupRestoreIntegrity, checkLoginFlowIntegrity, runAudit +// checkBackupRestoreIntegrity, checkLoginFlowIntegrity, checkAuthResultGating, +// checkBackupPasswordBounds, checkWebhookRedirectPolicy, +// checkSessionSecretLengthGuard, checkNotificationSsrfValidation, +// checkErrorMessageDisclosure, checkDockerCopySensitiveFiles, +// checkClientEnvLeak, runAudit // // Usage: npx tsx scripts/security-audit.ts [--changed-only file1 file2 ...] // If --changed-only is provided, only those files are scanned for -// file-level checks (2/3/8/9/11/12/14/20). Auth enforcement and headers always +// file-level checks (2/3/8/9/11/12/14/20/34). Auth enforcement and headers always // run a full scan regardless. // // Exit code: 1 if any critical check fails, 0 otherwise. @@ -261,29 +266,89 @@ function filterIgnoredFindings(results: CheckResult[]): CheckResult[] { return filtered } -// ── Check 1: Auth enforcement ─────────────────────────────────────────── +// ── Check 1: Auth enforcement (per-handler) ───────────────────────────── + +/** + * Extract the body of each exported handler function. + * Returns an array of { method, startLine, body } for each handler. + * + * Handles typed parameters like `(request: Request, props: { params: Promise<{ id: string }> })` + * by first skipping past the parameter list via paren-counting, then finding the + * function body's opening brace. + */ +function extractHandlerBodies( + content: string +): Array<{ method: string; startLine: number; body: string }> { + const HANDLER_RE = /export\s+(?:async\s+)?function\s+(GET|POST|PATCH|PUT|DELETE)\b/g + const results: Array<{ method: string; startLine: number; body: string }> = [] + let handlerMatch: RegExpExecArray | null + + while ((handlerMatch = HANDLER_RE.exec(content)) !== null) { + const method = handlerMatch[1] + const startLine = content.slice(0, handlerMatch.index).split("\n").length + + // Step 1: find opening paren of parameter list + const openParen = content.indexOf("(", handlerMatch.index + handlerMatch[0].length) + if (openParen === -1) continue + + // Step 2: count parens to find the closing paren of the parameter list + let parenDepth = 0 + let closeParen = openParen + for (let i = openParen; i < content.length; i++) { + if (content[i] === "(") parenDepth++ + if (content[i] === ")") parenDepth-- + if (parenDepth === 0) { + closeParen = i + break + } + } + + // Step 3: find the function body's opening brace AFTER the parameter list + const openBrace = content.indexOf("{", closeParen + 1) + if (openBrace === -1) continue + + // Step 4: count braces to find the matching close + let braceDepth = 0 + let end = openBrace + for (let i = openBrace; i < content.length; i++) { + if (content[i] === "{") braceDepth++ + if (content[i] === "}") braceDepth-- + if (braceDepth === 0) { + end = i + break + } + } + + results.push({ + method, + startLine, + body: content.slice(openBrace, end + 1), + }) + } + + return results +} function checkAuthEnforcement(): CheckResult { const findings: Finding[] = [] const routeFiles = walkFiles(API_DIR, "route.ts") - const EXPORTED_HANDLERS = /export\s+(?:async\s+)?function\s+(GET|POST|PATCH|PUT|DELETE)\b/g for (const file of routeFiles) { const routePath = routePathFromFile(file) if (NO_AUTH_ROUTES.has(routePath)) continue const content = fs.readFileSync(file, "utf8") - const handlers = [...content.matchAll(EXPORTED_HANDLERS)].map((m) => m[1]) + const handlers = extractHandlerBodies(content) if (handlers.length === 0) continue - // Accept any of the valid auth patterns - const hasAuth = AUTH_PATTERNS.some((re) => re.test(content)) + for (const { method, startLine, body } of handlers) { + const hasAuth = AUTH_PATTERNS.some((re) => re.test(body)) - if (!hasAuth) { - for (const method of handlers) { + if (!hasAuth) { findings.push({ file: relativePath(file), - detail: `${method} /${routePath} — no auth check (missing authenticate/getSession/requireAuth)`, + line: startLine, + detail: `${method} /${routePath} — no auth check in handler body (missing authenticate/getSession/requireAuth)`, }) } } @@ -291,7 +356,7 @@ function checkAuthEnforcement(): CheckResult { return { id: "auth-enforcement", - name: "Auth enforcement on protected routes", + name: "Auth enforcement on protected routes (per-handler)", severity: "critical", status: findings.length === 0 ? "pass" : "fail", findings, @@ -1889,6 +1954,443 @@ function checkLoginFlowIntegrity(): CheckResult { } } +// ── Check 29: Auth result must be checked (critical) ────────────────────── + +function checkAuthResultGating(): CheckResult { + const findings: Finding[] = [] + const routeFiles = walkFiles(API_DIR, "route.ts") + + for (const file of routeFiles) { + const routePath = routePathFromFile(file) + if (NO_AUTH_ROUTES.has(routePath)) continue + + const content = fs.readFileSync(file, "utf8") + const handlers = extractHandlerBodies(content) + + for (const { method, startLine, body } of handlers) { + // Only check handlers that use any recognized auth pattern + const hasAuth = AUTH_PATTERNS.some((re) => re.test(body)) + if (!hasAuth) continue + + // Strip comment lines before checking for the gate, + // so a commented-out guard doesn't satisfy the check + const bodyNoComments = body + .split("\n") + .filter((l) => { + const t = l.trim() + return !t.startsWith("//") && !t.startsWith("*") + }) + .join("\n") + + // authenticate() returns NextResponse | { encryptionKey }, so gate with instanceof + // getSession() returns { encryptionKey } | null, so gate with !session or === null + const hasInstanceofGate = + /instanceof\s+NextResponse/.test(bodyNoComments) || + /instanceof\s+Response/.test(bodyNoComments) + const hasNullGate = /!\s*session\b|session\s*===?\s*null/.test(bodyNoComments) + + if (!hasInstanceofGate && !hasNullGate) { + findings.push({ + file: relativePath(file), + line: startLine, + detail: `${method} /${routePath} — calls auth function but never checks the result (missing instanceof NextResponse or null check)`, + }) + } + } + } + + return { + id: "auth-result-gating", + name: "Auth result checked before proceeding", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 30: Backup password inputs bounded before deriveKey (critical) ─ + +function checkBackupPasswordBounds(): CheckResult { + const findings: Finding[] = [] + + const exportPath = path.join(API_DIR, "settings/backup/export/route.ts") + const restorePath = path.join(API_DIR, "settings/backup/restore/route.ts") + + for (const absPath of [exportPath, restorePath]) { + if (!fs.existsSync(absPath)) continue + const content = fs.readFileSync(absPath, "utf8") + const rel = relativePath(absPath) + + // Check: any route that reads backupPassword from formData and passes it + // to deriveKey/encryptBackupPayload must enforce an upper-bound length check + const readsBackupPw = + /formData\.get\s*\(\s*["']backupPassword["']\s*\)/.test(content) || + /\bbackupPassword\b/.test(content) + const callsDeriveOrEncrypt = + /\bderiveKey\s*\(/.test(content) || /\bencryptBackupPayload\s*\(/.test(content) + + if (!readsBackupPw || !callsDeriveOrEncrypt) continue + + // Must have an upper-bound length check (e.g., .length > 128) + const hasUpperBound = + /backupPassword\.length\s*>\s*\d+/.test(content) || + /formPassword\.length\s*>\s*\d+/.test(content) + + if (!hasUpperBound) { + findings.push({ + file: rel, + detail: + "Backup password input lacks upper-bound length check before deriveKey() — scrypt DoS vector", + }) + } + } + + return { + id: "backup-password-bounds", + name: "Backup password inputs bounded before key derivation", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 31: Webhook delivery fetch uses redirect:"error" (critical) ──── + +function checkWebhookRedirectPolicy(): CheckResult { + const findings: Finding[] = [] + const deliverPath = path.join(SRC_DIR, "lib/notifications/deliver.ts") + + if (!fs.existsSync(deliverPath)) { + return { + id: "webhook-redirect-policy", + name: 'Webhook delivery fetch uses redirect: "error"', + severity: "critical", + status: "pass", + findings, + } + } + + const content = fs.readFileSync(deliverPath, "utf8") + const rel = relativePath(deliverPath) + const lines = content.split("\n") + + const FETCH_RE = /\bfetch\s*\(/ + + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trim() + if (trimmed.startsWith("//") || trimmed.startsWith("*")) continue + if (!FETCH_RE.test(lines[i])) continue + + // Check within 8 lines for redirect: "error" + const windowEnd = Math.min(i + 8, lines.length - 1) + const fetchWindow = lines.slice(i, windowEnd + 1).join("\n") + + if (!/redirect\s*:\s*["']error["']/.test(fetchWindow)) { + findings.push({ + file: rel, + line: i + 1, + detail: + 'fetch() in webhook delivery missing redirect: "error" — SSRF via open redirect possible', + }) + } + } + + return { + id: "webhook-redirect-policy", + name: 'Webhook delivery fetch uses redirect: "error"', + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 32: SESSION_SECRET minimum-length guard (critical) ────────────── + +function checkSessionSecretLengthGuard(): CheckResult { + const findings: Finding[] = [] + + const checks: Array<{ file: string; fnName: string }> = [ + { file: "src/lib/auth.ts", fnName: "getSessionKey" }, + { file: "src/lib/crypto.ts", fnName: "deriveWrappingKey" }, + ] + + for (const { file, fnName } of checks) { + const absPath = path.join(ROOT, file) + if (!fs.existsSync(absPath)) { + findings.push({ + file, + detail: `${file} not found — cannot verify SESSION_SECRET length guard`, + }) + continue + } + + const content = fs.readFileSync(absPath, "utf8") + + const hasFn = new RegExp(`function\\s+${fnName}\\b`).test(content) + if (!hasFn) { + findings.push({ + file, + detail: `Expected function ${fnName}() not found`, + }) + continue + } + + // Extract the function body (rough: from function declaration to next top-level function/export) + const fnStart = content.indexOf(`function ${fnName}`) + const fnSlice = content.slice(fnStart, fnStart + 500) + + const hasLengthCheck = + /secret\.length\s*<\s*32/.test(fnSlice) || /\.length\s*<\s*32/.test(fnSlice) + + if (!hasLengthCheck) { + findings.push({ + file, + detail: `${fnName}() missing SESSION_SECRET minimum-length guard (secret.length < 32)`, + }) + } + } + + return { + id: "session-secret-length-guard", + name: "SESSION_SECRET minimum-length guard in auth/crypto modules", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 33: Notification type validators must include SSRF check (critical) ── + +function checkNotificationSsrfValidation(): CheckResult { + const findings: Finding[] = [] + const validatePath = path.join(SRC_DIR, "lib/notifications/validate.ts") + + if (!fs.existsSync(validatePath)) { + return { + id: "notification-ssrf-validation", + name: "Notification URL validators include SSRF protection", + severity: "critical", + status: "pass", + findings, + } + } + + const content = fs.readFileSync(validatePath, "utf8") + const rel = relativePath(validatePath) + + // Find case blocks for notification types that are NOT "not yet supported" + const CASE_RE = /case\s+["'](\w+)["']\s*:\s*\{/g + let caseMatch: RegExpExecArray | null + + while ((caseMatch = CASE_RE.exec(content)) !== null) { + const typeName = caseMatch[1] + const caseStart = caseMatch.index + + // Find the closing brace by counting depth + let depth = 0 + let caseEnd = caseStart + for (let i = content.indexOf("{", caseStart); i < content.length; i++) { + if (content[i] === "{") depth++ + if (content[i] === "}") depth-- + if (depth === 0) { + caseEnd = i + break + } + } + + const caseBody = content.slice(caseStart, caseEnd + 1) + + // Skip types that are "not yet supported" — no URL handling to validate + if (/not yet supported/.test(caseBody)) continue + + // Active type — must call isUnsafeNetworkHost + if (!/isUnsafeNetworkHost/.test(caseBody)) { + const lineNum = content.slice(0, caseStart).split("\n").length + findings.push({ + file: rel, + line: lineNum, + detail: `Notification type "${typeName}" has active validation but does not call isUnsafeNetworkHost() — SSRF risk`, + }) + } + } + + return { + id: "notification-ssrf-validation", + name: "Notification URL validators include SSRF protection", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 34: Error message information disclosure (warning) ────────────── + +function checkErrorMessageDisclosure(files?: string[]): CheckResult { + const findings: Finding[] = [] + const targetFiles = files + ? files.filter((f) => f.includes("app/api") && f.endsWith("route.ts")) + : walkFiles(API_DIR, "route.ts") + + // Detect: err.message captured into a variable, then that variable (or the + // expression directly) interpolated into a JSON response within a few lines. + const ERR_CAPTURE_RE = + /(?:const|let)\s+(\w+)\s*=\s*(?:err|error)\s+instanceof\s+Error\s*\?\s*(?:err|error)\.message/ + const DIRECT_ERR_MSG_RE = /(?:err|error)\.message/ + + for (const file of targetFiles) { + if (isTestFile(file)) continue + const content = fs.readFileSync(file, "utf8") + const lines = content.split("\n") + + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trim() + if (trimmed.startsWith("//") || trimmed.startsWith("*")) continue + + // Check for err.message capture: `const message = err instanceof Error ? err.message : ...` + const captureMatch = ERR_CAPTURE_RE.exec(lines[i]) + if (!captureMatch && !DIRECT_ERR_MSG_RE.test(lines[i])) continue + + // Look within 5 lines for a JSON response that includes the error + const windowEnd = Math.min(i + 5, lines.length - 1) + const window = lines.slice(i, windowEnd + 1).join("\n") + + const hasJsonResponse = + /NextResponse\.json\s*\(/.test(window) || + /return\s+new\s+Response/.test(window) + + if (!hasJsonResponse) continue + + // Check: is the captured variable or err.message in the response? + const varName = captureMatch?.[1] + const varInResponse = varName + ? new RegExp(`\\b${varName}\\b`).test(window) && + /(?:error|message)\s*:/.test(window) + : /(?:err|error)\.message/.test(window) + + if (varInResponse) { + findings.push({ + file: relativePath(file), + line: i + 1, + detail: + "Raw error message passed into API response — may leak internal paths or library details", + }) + } + } + } + + return { + id: "error-message-disclosure", + name: "No raw error messages in API responses", + severity: "warning", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 35: Docker COPY of sensitive files (critical) ─────────────────── + +function checkDockerCopySensitiveFiles(): CheckResult { + const findings: Finding[] = [] + const dockerfilePath = path.join(ROOT, "Dockerfile") + + if (!fs.existsSync(dockerfilePath)) { + return { + id: "docker-copy-sensitive", + name: "Dockerfile does not COPY sensitive files", + severity: "critical", + status: "pass", + findings, + } + } + + const content = fs.readFileSync(dockerfilePath, "utf8") + const lines = content.split("\n") + + const SENSITIVE_FILE_PATTERNS = [ + /\.env\b(?!\.example)/, + /\.env\.local\b/, + /\.env\.production\b/, + /credentials\.json/, + /\.pem\b/, + /id_rsa\b/, + /\.key\b/, + ] + + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trim() + if (trimmed.startsWith("#")) continue + if (!trimmed.startsWith("COPY") && !trimmed.startsWith("ADD")) continue + + for (const pattern of SENSITIVE_FILE_PATTERNS) { + if (pattern.test(trimmed)) { + findings.push({ + file: "Dockerfile", + line: i + 1, + detail: `COPY/ADD of potentially sensitive file: ${trimmed.slice(0, 100)}`, + }) + } + } + } + + return { + id: "docker-copy-sensitive", + name: "Dockerfile does not COPY sensitive files", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + +// ── Check 36: No secret env vars in client components (critical) ────────── + +const SECRET_ENV_VARS = [ + "SESSION_SECRET", + "DATABASE_URL", + "POSTGRES_URL", + "REDIS_URL", + "ENCRYPTION_KEY", +] + +function checkClientEnvLeak(): CheckResult { + const findings: Finding[] = [] + const clientFiles = [ + ...walkFiles(SRC_DIR, ".tsx"), + ...walkFiles(SRC_DIR, ".ts"), + ].filter((f) => !isTestFile(f)) + + for (const file of clientFiles) { + const content = fs.readFileSync(file, "utf8") + + // Only check files marked as client components + if (!content.includes("'use client'") && !content.includes('"use client"')) continue + + const lines = content.split("\n") + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + const trimmed = line.trim() + if (trimmed.startsWith("//") || trimmed.startsWith("*")) continue + + for (const envVar of SECRET_ENV_VARS) { + if (line.includes(`process.env.${envVar}`)) { + findings.push({ + file: relativePath(file), + line: i + 1, + detail: `Client component references process.env.${envVar} — secret will be undefined but indicates confused boundary`, + }) + } + } + } + } + + return { + id: "client-env-leak", + name: "No secret env vars in client components", + severity: "critical", + status: findings.length === 0 ? "pass" : "fail", + findings, + } +} + // ── Run all checks ────────────────────────────────────────────────────── function runAudit(changedFiles?: string[]): AuditOutput { @@ -1918,6 +2420,13 @@ function runAudit(changedFiles?: string[]): AuditOutput { checkNukeFlowIntegrity(), checkBackupRestoreIntegrity(), checkLoginFlowIntegrity(), + checkAuthResultGating(), + checkBackupPasswordBounds(), + checkWebhookRedirectPolicy(), + checkSessionSecretLengthGuard(), + checkNotificationSsrfValidation(), + checkDockerCopySensitiveFiles(), + checkClientEnvLeak(), // Warning — flag but don't fail checkConsoleLogInRoutes(absChangedFiles), checkTodoInSecurityFiles(), @@ -1925,6 +2434,7 @@ function runAudit(changedFiles?: string[]): AuditOutput { checkBareCatchBlocks(absChangedFiles), checkRequestBodySize(absChangedFiles), checkBigIntSafety(absChangedFiles), + checkErrorMessageDisclosure(absChangedFiles), ] const finalResults = filterIgnoredFindings(results) From 7f7b202e2557d25cd011c8bd96e14332f3565bb1 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 13:02:17 -0500 Subject: [PATCH 46/59] fix(errors): improve error handling and logging for backup and tracker operations --- src/app/api/settings/backup/export/route.ts | 5 ++--- src/app/api/settings/backup/restore/route.ts | 6 ++---- src/app/api/trackers/test/route.ts | 11 ++++++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/app/api/settings/backup/export/route.ts b/src/app/api/settings/backup/export/route.ts index 960f5653..93e80162 100644 --- a/src/app/api/settings/backup/export/route.ts +++ b/src/app/api/settings/backup/export/route.ts @@ -101,14 +101,13 @@ export async function POST(request: Request) { }, }) } catch (err) { - const message = err instanceof Error ? err.message : "Unknown error" log.error( { route: "POST /api/settings/backup/export", - error: err instanceof Error ? err.message : "unknown", + error: err instanceof Error ? err.message : String(err), }, "backup export failed" ) - return NextResponse.json({ error: `Backup export failed: ${message}` }, { status: 500 }) + return NextResponse.json({ error: "Backup export failed" }, { status: 500 }) } } diff --git a/src/app/api/settings/backup/restore/route.ts b/src/app/api/settings/backup/restore/route.ts index 73d21fbe..b6463353 100644 --- a/src/app/api/settings/backup/restore/route.ts +++ b/src/app/api/settings/backup/restore/route.ts @@ -672,19 +672,17 @@ export async function POST(request: Request) { .where(eq(appSettings.id, currentSettingsId)) }) } catch (err) { - const message = err instanceof Error ? err.message : "Unknown error" - log.error( { event: "restore_failed", - error: message, + error: err instanceof Error ? err.message : String(err), fileNameHash: hashFileName(fileName), }, "Restore operation failed" ) return NextResponse.json( - { error: "Restore failed. Check server logs for details." }, + { error: "Backup restore failed" }, { status: 409 } ) } finally { diff --git a/src/app/api/trackers/test/route.ts b/src/app/api/trackers/test/route.ts index 89e861a6..39acf4ee 100644 --- a/src/app/api/trackers/test/route.ts +++ b/src/app/api/trackers/test/route.ts @@ -61,8 +61,13 @@ export async function POST(request: Request) { group: stats.group, }) } catch (error) { - const message = error instanceof Error ? error.message : "Connection failed" - log.warn({ route: "POST /api/trackers/test", error: message }, "tracker connection test failed") - return NextResponse.json({ error: message }, { status: 422 }) + log.warn( + { + route: "POST /api/trackers/test", + error: error instanceof Error ? error.message : String(error), + }, + "tracker connection test failed" + ) + return NextResponse.json({ error: "Tracker test failed" }, { status: 422 }) } } From 7e5f470d3314632a6e3d4a34d312e1428f963817 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 19:00:44 +0000 Subject: [PATCH 47/59] chore(deps): bump the npm_and_yarn group across 1 directory with 2 updates Bumps the npm_and_yarn group with 2 updates in the / directory: [picomatch](https://github.com/micromatch/picomatch) and [yaml](https://github.com/eemeli/yaml). Updates `picomatch` from 2.3.1 to 2.3.2 - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2) Updates `yaml` from 2.8.2 to 2.8.3 - [Release notes](https://github.com/eemeli/yaml/releases) - [Commits](https://github.com/eemeli/yaml/compare/v2.8.2...v2.8.3) --- updated-dependencies: - dependency-name: picomatch dependency-version: 2.3.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: yaml dependency-version: 2.8.3 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- pnpm-lock.yaml | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 172ef70c..58b77b92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2791,14 +2791,10 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -3429,11 +3425,6 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -4580,7 +4571,7 @@ snapshots: find-up: 5.0.0 git-semver-tags: 5.0.1 semver: 7.7.4 - yaml: 2.8.2 + yaml: 2.8.3 yargs: 17.7.2 compare-func@2.0.0: @@ -4931,10 +4922,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -5220,7 +5207,7 @@ snapshots: oxc-parser: 0.120.0 oxc-resolver: 11.19.1 picocolors: 1.1.1 - picomatch: 4.0.3 + picomatch: 4.0.4 smol-toml: 1.6.1 strip-json-comments: 5.0.3 unbash: 2.2.0 @@ -5704,7 +5691,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 min-indent@1.0.1: {} @@ -5912,9 +5899,7 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} - - picomatch@4.0.3: {} + picomatch@2.3.2: {} picomatch@4.0.4: {} @@ -6371,8 +6356,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinyrainbow@3.1.0: {} @@ -6510,7 +6495,7 @@ snapshots: magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.4 @@ -6571,8 +6556,6 @@ snapshots: yallist@4.0.0: {} - yaml@2.8.2: {} - yaml@2.8.3: {} yargs-parser@20.2.9: {} From 692d3124cd648a3f1278b38881df5083f7febdc1 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:30:47 -0500 Subject: [PATCH 48/59] feat(mam): add MyAnonaMouse adapter --- public/tracker-logos/myanonamouse_logo.png | Bin 0 -> 2852 bytes src/data/__tests__/tracker-registry.test.ts | 2 +- src/data/tracker-registry.test.ts | 4 +- src/data/tracker-registry.ts | 2 +- src/data/trackers/myanonamouse.ts | 33 +- src/lib/adapters/constants.ts | 5 +- src/lib/adapters/index.ts | 2 + src/lib/adapters/mam.test.ts | 326 ++++++++++++++++++++ src/lib/adapters/mam.ts | 166 ++++++++++ src/lib/adapters/types.ts | 18 +- 10 files changed, 545 insertions(+), 13 deletions(-) create mode 100644 public/tracker-logos/myanonamouse_logo.png create mode 100644 src/lib/adapters/mam.test.ts create mode 100644 src/lib/adapters/mam.ts diff --git a/public/tracker-logos/myanonamouse_logo.png b/public/tracker-logos/myanonamouse_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a6bd09f0c23b02c4dc6ed88c84a01b213b982dd1 GIT binary patch literal 2852 zcmZ`*c{tSV8vf0U*%z~qr8Jh1bu9T9lHCjqiDJl_ZS4EL6%w*Fwvr{HC_xDj3IG5sn`LoGgk8T2 z0TFc+>6@qnVgaTcQvm2FLT`FQMVx+&b%+B1Ns0iFkp%$jA}M1Y0Ad*cu;>K<%t`>D zMV#+xBk^L?Iy56%B`h7H-qRkD`_6Vk31V zFc@GN!UK<>x=M+;kyw+xu$I$=5B*da6m+wS*j7xqQ9QEOpGRzH)LxVcb5H4il=~9q{V;HCy@ZchO zw1vzvf}82Vu9Xn3ml8D;q5E`T{6rj?0Djp{IT4KIvXQr{Nt2xv0uH=eM(lkbwI~kf zegL^w4O(*oFC3yhY9e3E!DsNWZnnsM+Av28M4S)iY?}DsdD6QZR6G_uc9m?+gzuDq zr1)dr^icQf!bUETwD&-6*39| zdUA=}l#3rYM?9A<9W;oi9kMtcSCW7$PZoEwLg=VKiQ-^21+i#P zbpJV`2M3uEguR?kNDaV9kinmC(eBoh&`3~WEbjGnimWuGGEE!;1{g|EDiM4#9Basc zsVa!wZy;$YLq85s<#$5PB;a0kP<$LvtwjU`98{hnt|BkSJAz{GgI_5iIJ1y4k`N_X zF$Ed1>@e&eIk8WJGzW782@h^BA;fxPFep$}hPa<2YL^s*$$)j25s?T`Rw%YKN&Hj< zcDkGDdk8gDP0EkPNmC&Khf(Q)m>ZSEXU$|g6U0F?L~jMLE(@O)h$%}Jw={si?WH#4 z;DcOHR|^S;tq>0z$q`4>?d&>o|)?BxO(dFnw^QyNb&8b^1~a-6f-RoJVQZe`oQos zW!O^oQKl}L(mkT1j^=RGp5clP3_MxdmVc8NAe=O)iI2PQY+U<&Lzvr@H;$JJn6kj=#ovZwX%TicA^+gFxweuix+e_UWp-x zqRr}Es~rNSsJ=0Mf1U0ZOwb#uJlAEXq%153HU%@hWc>KZ^64bOH+sFQ<`yGVTrO!< zF!)=}saUd|&ODWNGe&bt#^N7gBYf-|{R6=xi7qnvBrHtoPsu6VUCl&GS-54LO7K{B z5ZM$~1WS6c=|=Z$8!SY}`taU+cFU0-OdcYXKs;l(I$=LXm6e0Od)eG&$&$!xtPOE| zf*5K4>L{Xkjjt<&E{+IeZQS}zeRfPHDTI=UR^Gf0;kr-cHi{wl&N>(^Lrpx`iW*u&x?(~etL!NaB=pxz;JyE| zB0zI-C2xBFT1;Y8y+?t|(fW8h&kJTLw|K%=#jwI`X27Q$^;{R%BW@au&?i{oPU32ErvuQuZtOC+BZp&hvehPd881 zrS;K%M3{9~=$QV?YNkcwXQT3Kr;j?sGRG^wMglc={LxJ}xLr4&u-f~t^{dZt7IvEl z%Q|DjXB*gx{&r~0-k#Z~D>%ih#LgZVO-6;RL9eI@b?J}__$=Wc^wA-4Ul&IovEbqt z0(ZI#%{MEy`Wg-^8(o<_X2>%9>siJLWXj^hgBJ-?1`gch;)p=x*=QQ8ulL8BlVcah z+im>5y8n0>*17wU7^~aLxL4nT*JW*>DJlC@^*fQhoJo#;pR7STbJd!|uis2dnFSt+ z8A~3)JZ~*{5Gr5#*8)5n5>8@0hnBSn`3>fAR;<2s_Wb(0*Qkm@@ZpvZd|UP6xSOi9 zNrfE=;u@(4rv*lB%3UyzyeGZ3HkJIqeb)9%0hHqUIKaN$OzO+(V{#K}_1Hn)?5<7y zTKB+Q-6YQ@ynI%LX`}aur2uKXadEHUQ>So~S&QEWl(%Fr+n`2`{R(s!m7-a+JG(%Q z`(c#faF;6eD66&?hjMhxeq_)TQ!$4cU`j&n?gHZy<|#BNz*|t)0sZdqb3mP6{$-F! zJ5`gzz6K{h9O+iu20(|4JH^01%&|xPE%o#?+oFE zwr&wKZyPvrBcg8=bkpt>v37l)Y8>{f(}lTiyJFHH{IC|Z+~w3x>Tj=II zcVSQesOsMmmB_w(kwS{>Op&VQ^yHRV^F!gN3Y8dp$@ZQOPr5g%YGChCLf%;9=vz~_ z9~#xdJ;M1V($r`yv? z%r5;^Txe8wPB4_qZP@KSR^{)OTQD9|qjvT+`;TWY$Kocq9L+8FGZXat9}CZE_!HuF zdzUR=*{t>C!s%|oA&o&@pR<3Nm(OqLtQ7Gs{(Zh}P&}S57fbh0%N?+I-RH12T{2g1 z_js=1ijBe2wY$Tm)xj^9vC9j8@)zAZgygSE_l6EL1BE9`RVJ+Mf8xH( z#o4y6DeMYWO^wro8b)^+!cw)vFK>ZuQ@RHKQFh#ZTc;YTxvx#HnHe1)n9t+KIRtk) z*t>D~+RyAAeqCs7%HF7Sj_&cEv68TjB+txs_1Vi@r_AX_Crz%L+v|H$Q}0J-&-os2 zd`Bu~=;pNPw8NvJodu-^OR!M3WZSmacKYM<9fa@5_ijnfCrTy%XJx@M$3OqvC4E87 zle54w6D`!;F_tbdzTPo@%wti0A^@}*TKeh?Ep=@JCkBJ5tiRY-j_T8 literal 0 HcmV?d00001 diff --git a/src/data/__tests__/tracker-registry.test.ts b/src/data/__tests__/tracker-registry.test.ts index 3793af12..704e2573 100644 --- a/src/data/__tests__/tracker-registry.test.ts +++ b/src/data/__tests__/tracker-registry.test.ts @@ -15,7 +15,7 @@ import type { TrackerRegistryEntry } from "@/data/tracker-registry" import { ALL_TRACKERS } from "@/data/trackers" import { DEFAULT_API_PATHS } from "@/lib/adapters" -const VALID_PLATFORMS = ["unit3d", "gazelle", "ggn", "nebulance", "custom"] as const +const VALID_PLATFORMS = ["unit3d", "gazelle", "ggn", "nebulance", "mam", "custom"] as const const HEX_COLOR_RE = /^#[0-9a-fA-F]{6}$/ const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/ const LOGO_NAME_RE = /^\/tracker-logos\/[a-z0-9_]+_logo\.(svg|png)$/ diff --git a/src/data/tracker-registry.test.ts b/src/data/tracker-registry.test.ts index a0e05faa..8c94ad80 100644 --- a/src/data/tracker-registry.test.ts +++ b/src/data/tracker-registry.test.ts @@ -12,13 +12,15 @@ describe("tracker registry", () => { expect(tracker.slug).toBeTruthy() expect(tracker.name).toBeTruthy() expect(tracker.url).toMatch(/^https:\/\//) - expect(["unit3d", "gazelle", "ggn", "nebulance"]).toContain(tracker.platform) + expect(["unit3d", "gazelle", "ggn", "nebulance", "mam"]).toContain(tracker.platform) if (tracker.platform === "unit3d") { expect(tracker.apiPath).toBe("/api/user") } else if (tracker.platform === "gazelle") { expect(tracker.apiPath).toBe("/ajax.php") } else if (tracker.platform === "ggn" || tracker.platform === "nebulance") { expect(tracker.apiPath).toBe("/api.php") + } else if (tracker.platform === "mam") { + expect(tracker.apiPath).toBe("/jsonLoad.php") } expect(tracker.color).toMatch(/^#[0-9a-f]{6}$/i) } diff --git a/src/data/tracker-registry.ts b/src/data/tracker-registry.ts index cfe07515..dd7739bf 100644 --- a/src/data/tracker-registry.ts +++ b/src/data/tracker-registry.ts @@ -50,7 +50,7 @@ export interface TrackerRegistryEntry { abbreviation?: string url: string description: string - platform: "unit3d" | "gazelle" | "ggn" | "nebulance" | "custom" + platform: "unit3d" | "gazelle" | "ggn" | "nebulance" | "mam" | "custom" apiPath: string specialty: string contentCategories: string[] diff --git a/src/data/trackers/myanonamouse.ts b/src/data/trackers/myanonamouse.ts index 3a633940..949097a4 100644 --- a/src/data/trackers/myanonamouse.ts +++ b/src/data/trackers/myanonamouse.ts @@ -7,13 +7,13 @@ export const myanonamouse: TrackerRegistryEntry = { slug: "myanonamouse", name: "MyAnonaMouse", abbreviation: "MAM", - url: "https://myanonamouse.net", + url: "https://www.myanonamouse.net", description: "Book, audiobook, and comics tracker with an open interview for anyone who wants to join and an extremely friendly community.", // ── Platform & API ────────────────────────────────────────────────── - platform: "custom", - apiPath: "/api/user", + platform: "mam", + apiPath: "/jsonLoad.php", // ── Content ───────────────────────────────────────────────────────── specialty: "Books / Audiobooks", @@ -22,23 +22,40 @@ export const myanonamouse: TrackerRegistryEntry = { // ── Visual ────────────────────────────────────────────────────────── color: "#ec407a", - logo: "", + logo: "/tracker-logos/myanonamouse_logo.png", // ── External Links ────────────────────────────────────────────────── trackerHubSlug: "my-anona-mouse", statusPageUrl: "https://status.myanonamouse.net/", // ── Community ─────────────────────────────────────────────────────── - userClasses: [], + userClasses: [ + { name: "Mouse", requirements: "Ratio below 1.0 (auto-demotion)" }, + { name: "User", requirements: "Ratio above 1.0" }, + { name: "Power User", requirements: "4 weeks membership, 25 GB uploaded, 2.0 ratio" }, + { name: "Star", requirements: "Donor" }, + { name: "VIP", requirements: "Bonus points or donation (requires Power User first)" }, + { name: "Elite VIP", requirements: "Staff-selected for community contribution" }, + { name: "Elite", requirements: "Staff-selected, immune to auto-demotion" }, + { name: "Supporter", requirements: "Continuous supporter" }, + { name: "Mouseketeer", requirements: "Retired staff" }, + { name: "Uploader", requirements: "Staff-selected for consistent monthly uploads" }, + ], + stats: { + userCount: 114812, + torrentCount: 1128625, + statsUpdatedAt: "2026-03-26", + }, releaseGroups: [], bannedGroups: [], notableMembers: [], // ── Rules ─────────────────────────────────────────────────────────── rules: { - minimumRatio: 0, - seedTimeHours: 0, + minimumRatio: 1.0, + seedTimeHours: 72, loginIntervalDays: 0, + fulfillmentPeriodHours: 720, }, // ── Status ────────────────────────────────────────────────────────── @@ -46,7 +63,7 @@ export const myanonamouse: TrackerRegistryEntry = { warningNote: "", // ── Flags ─────────────────────────────────────────────────────────── - draft: true, + draft: false, supportsTransitPapers: false, profileUrlPattern: "", } diff --git a/src/lib/adapters/constants.ts b/src/lib/adapters/constants.ts index bafb9238..634352d3 100644 --- a/src/lib/adapters/constants.ts +++ b/src/lib/adapters/constants.ts @@ -1,10 +1,13 @@ // src/lib/adapters/constants.ts -export const VALID_PLATFORM_TYPES = ["unit3d", "gazelle", "ggn", "nebulance"] as const +export const VALID_PLATFORM_TYPES = ["unit3d", "gazelle", "ggn", "nebulance", "mam"] as const + +export const MAM_BONUS_CAP = 99999 export const DEFAULT_API_PATHS: Record = { unit3d: "/api/user", gazelle: "/ajax.php", ggn: "/api.php", nebulance: "/api.php", + mam: "/jsonLoad.php", } diff --git a/src/lib/adapters/index.ts b/src/lib/adapters/index.ts index fe6a13f2..20f56f12 100644 --- a/src/lib/adapters/index.ts +++ b/src/lib/adapters/index.ts @@ -7,6 +7,7 @@ import type { Agent as HttpAgent } from "node:http" import { findRegistryEntry } from "@/data/tracker-registry" import { GazelleAdapter } from "./gazelle" import { GGnAdapter } from "./ggn" +import { MamAdapter } from "./mam" import { NebulanceAdapter } from "./nebulance" import type { FetchOptions, TrackerAdapter } from "./types" import { Unit3dAdapter } from "./unit3d" @@ -16,6 +17,7 @@ export { DEFAULT_API_PATHS, VALID_PLATFORM_TYPES } from "./constants" const adapters: Record = { gazelle: new GazelleAdapter(), ggn: new GGnAdapter(), + mam: new MamAdapter(), nebulance: new NebulanceAdapter(), unit3d: new Unit3dAdapter(), } diff --git a/src/lib/adapters/mam.test.ts b/src/lib/adapters/mam.test.ts new file mode 100644 index 00000000..fceb420f --- /dev/null +++ b/src/lib/adapters/mam.test.ts @@ -0,0 +1,326 @@ +// src/lib/adapters/mam.test.ts +// +// Functions: mockMamResponse, mockFetch, describe(MamAdapter - parsing), +// describe(MamAdapter - auth), describe(MamAdapter - error handling), +// describe(MamAdapter - fetchRaw) + +import { beforeEach, describe, expect, it, vi } from "vitest" +import { MamAdapter } from "./mam" + +function mockMamResponse(overrides?: Partial>) { + return { + username: "trackerfan", + uid: 12345, + classname: "VIP", + ratio: 2.47, + uploaded: "500.25 GiB", + downloaded: "202.57 GiB", + uploaded_bytes: 537062408601, + downloaded_bytes: 217524183040, + seedbonus: 98765, + wedges: 3, + vip_until: "2027-01-01", + connectable: "Yes", + recently_deleted: 0, + leeching: { name: "Leeching", count: 2, red: false, size: null }, + sSat: { name: "Seeding Satisfied", count: 10, red: false, size: null }, + seedHnr: { name: "Seeding HnR", count: 1, red: true, size: null }, + seedUnsat: { name: "Seeding Unsatisfied", count: 3, red: true, size: null }, + upAct: { name: "Upload Active", count: 4, red: false, size: null }, + upInact: { name: "Upload Inactive", count: 0, red: false, size: null }, + inactHnr: { name: "Inactive HnR", count: 5, red: true, size: null }, + inactSat: { name: "Inactive Satisfied", count: 8, red: false, size: null }, + inactUnsat: { name: "Inactive Unsatisfied", count: 2, red: true, size: null }, + unsat: { name: "Unsatisfied", count: 2, red: true, size: null, limit: 10 }, + duplicates: { name: "Duplicates", count: 0, red: false, size: null }, + reseed: { name: "Reseed", count: 0, inactive: 0, red: false }, + ite: { name: "Tracker Errors", count: 1, latest: 1711000000 }, + notifs: { pms: 3, aboutToDropClient: 0, tickets: 1, waiting_tickets: 0, requests: 2, topics: 5 }, + ...overrides, + } +} + +function mockFetch(overrides?: Partial>) { + return vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse(overrides), + } as Response) +} + +describe("MamAdapter - parsing", () => { + const adapter = new MamAdapter() + + beforeEach(() => { + vi.restoreAllMocks() + }) + + it("parses a valid response into TrackerStats", async () => { + mockFetch() + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "fake-session-id", + "/jsonLoad.php" + ) + + expect(stats.username).toBe("trackerfan") + expect(stats.group).toBe("VIP") + expect(stats.remoteUserId).toBe(12345) + expect(stats.uploadedBytes).toBe(BigInt(537062408601)) + expect(stats.downloadedBytes).toBe(BigInt(217524183040)) + expect(stats.ratio).toBeCloseTo(2.47) + expect(stats.bufferBytes).toBe(BigInt(537062408601) - BigInt(217524183040)) + expect(stats.seedbonus).toBe(98765) + expect(stats.freeleechTokens).toBe(3) + expect(stats.hitAndRuns).toBe(5) + expect(stats.requiredRatio).toBeNull() + expect(stats.warned).toBeNull() + }) + + it("aggregates seedingCount from sSat + seedHnr + seedUnsat + upAct", async () => { + mockFetch() + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "fake-session-id", + "/jsonLoad.php" + ) + + // 10 + 1 + 3 + 4 = 18 + expect(stats.seedingCount).toBe(18) + }) + + it("reads leechingCount directly from leeching.count", async () => { + mockFetch() + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "fake-session-id", + "/jsonLoad.php" + ) + + expect(stats.leechingCount).toBe(2) + }) + + it("returns zero bufferBytes when downloaded exceeds uploaded", async () => { + mockFetch({ + uploaded_bytes: 100_000_000, + downloaded_bytes: 500_000_000, + }) + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + expect(stats.bufferBytes).toBe(BigInt(0)) + }) + + it("handles missing snatch_summary fields and returns seedingCount of 0", async () => { + mockFetch({ + sSat: undefined, + seedHnr: undefined, + seedUnsat: undefined, + upAct: undefined, + leeching: undefined, + inactHnr: undefined, + }) + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + expect(stats.seedingCount).toBe(0) + expect(stats.leechingCount).toBe(0) + expect(stats.hitAndRuns).toBeNull() + }) + + it("maps wedges to freeleechTokens", async () => { + mockFetch({ wedges: 7 }) + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + expect(stats.freeleechTokens).toBe(7) + }) + + it("maps inactHnr.count to hitAndRuns", async () => { + mockFetch({ inactHnr: { name: "Inactive HnR", count: 12, red: true, size: null } }) + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + expect(stats.hitAndRuns).toBe(12) + }) + + it("populates MamPlatformMeta with all expected fields", async () => { + mockFetch() + + const stats = await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + const meta = stats.platformMeta as { + vipUntil?: string + connectable?: string + unsatisfiedCount?: number + unsatisfiedLimit?: number + inactiveSatisfiedCount?: number + seedingHnrCount?: number + trackerErrorCount?: number + recentlyDeleted?: number + unreadPMs?: number + openTickets?: number + pendingRequests?: number + unreadTopics?: number + } + + expect(meta).toBeDefined() + expect(meta.vipUntil).toBe("2027-01-01") + expect(meta.connectable).toBe("Yes") + expect(meta.unsatisfiedCount).toBe(2) + expect(meta.unsatisfiedLimit).toBe(10) + expect(meta.inactiveSatisfiedCount).toBe(8) + expect(meta.seedingHnrCount).toBe(1) + expect(meta.trackerErrorCount).toBe(1) + expect(meta.recentlyDeleted).toBe(0) + expect(meta.unreadPMs).toBe(3) + expect(meta.openTickets).toBe(1) + expect(meta.pendingRequests).toBe(2) + expect(meta.unreadTopics).toBe(5) + }) +}) + +describe("MamAdapter - auth", () => { + const adapter = new MamAdapter() + + beforeEach(() => { + vi.restoreAllMocks() + }) + + it("sends mam_id as Cookie header", async () => { + const fetchSpy = vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse(), + } as Response) + + await adapter.fetchStats( + "https://www.myanonamouse.net", + "my-session-cookie", + "/jsonLoad.php" + ) + + const callOpts = fetchSpy.mock.calls[0][1] as RequestInit + const headers = callOpts.headers as Record + expect(headers.Cookie).toBe("mam_id=my-session-cookie") + }) + + it("includes snatch_summary in the request URL", async () => { + const fetchSpy = vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse(), + } as Response) + + await adapter.fetchStats( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + const calledUrl = fetchSpy.mock.calls[0][0] as string + expect(calledUrl).toContain("snatch_summary") + expect(calledUrl).toContain("notif") + expect(calledUrl).toContain("jsonLoad.php") + }) +}) + +describe("MamAdapter - error handling", () => { + const adapter = new MamAdapter() + + beforeEach(() => { + vi.restoreAllMocks() + }) + + it("throws when username is missing from response", async () => { + vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse({ username: "" }), + } as Response) + + await expect( + adapter.fetchStats("https://www.myanonamouse.net", "session-id", "/jsonLoad.php") + ).rejects.toThrow("missing username") + }) + + it("does not leak the session cookie in error messages", async () => { + const secretToken = "ultra-secret-mam-session-abc123" + + vi.spyOn(global, "fetch").mockRejectedValueOnce(new Error("fetch failed")) + + await expect( + adapter.fetchStats("https://www.myanonamouse.net", secretToken, "/jsonLoad.php") + ).rejects.toSatisfy((err: Error) => { + expect(err.message).not.toContain(secretToken) + expect(err.message).toContain("www.myanonamouse.net") + return true + }) + }) + + it("throws a timeout-specific message on TimeoutError", async () => { + const timeoutError = new DOMException("signal timed out", "TimeoutError") + vi.spyOn(global, "fetch").mockRejectedValueOnce(timeoutError) + + await expect( + adapter.fetchStats("https://www.myanonamouse.net", "session-id", "/jsonLoad.php") + ).rejects.toThrow("Request to www.myanonamouse.net timed out") + }) + + it("passes an AbortSignal to fetch for timeout protection", async () => { + const fetchSpy = vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse(), + } as Response) + + await adapter.fetchStats("https://www.myanonamouse.net", "session-id", "/jsonLoad.php") + + const callOpts = fetchSpy.mock.calls[0][1] as RequestInit + expect(callOpts.signal).toBeDefined() + }) +}) + +describe("MamAdapter - fetchRaw", () => { + const adapter = new MamAdapter() + + beforeEach(() => { + vi.restoreAllMocks() + }) + + it("returns a single debug call with label 'User Stats'", async () => { + vi.spyOn(global, "fetch").mockResolvedValueOnce({ + ok: true, + json: async () => mockMamResponse(), + } as Response) + + const calls = await adapter.fetchRaw( + "https://www.myanonamouse.net", + "session-id", + "/jsonLoad.php" + ) + + expect(calls).toHaveLength(1) + expect(calls[0].label).toBe("User Stats") + expect(calls[0].error).toBeNull() + expect(calls[0].data).toBeDefined() + }) +}) diff --git a/src/lib/adapters/mam.ts b/src/lib/adapters/mam.ts new file mode 100644 index 00000000..57ef4de3 --- /dev/null +++ b/src/lib/adapters/mam.ts @@ -0,0 +1,166 @@ +// src/lib/adapters/mam.ts +// +// Functions: MamAdapter, MamAdapter.fetchStats, MamAdapter.fetchRaw + +import { adapterFetch } from "./adapter-fetch" +import type { + DebugApiCall, + FetchOptions, + MamPlatformMeta, + TrackerAdapter, + TrackerStats, +} from "./types" + +interface MamSnatchCategory { + name: string + count: number + red: boolean + size: number | null +} + +interface MamJsonLoadResponse { + username: string + uid: number + classname: string + ratio: number + uploaded: string + downloaded: string + uploaded_bytes: number + downloaded_bytes: number + seedbonus: number + wedges: number + vip_until?: string | null + connectable?: string + country_code?: string + country_name?: string + created?: number + update?: number + ipv6_mac?: boolean + v6_connectable?: string | null + partial?: boolean + recently_deleted?: number + + // snatch_summary categories (present when ?snatch_summary is set) + leeching?: MamSnatchCategory + sSat?: MamSnatchCategory + seedHnr?: MamSnatchCategory + seedUnsat?: MamSnatchCategory + upAct?: MamSnatchCategory + upInact?: MamSnatchCategory + inactHnr?: MamSnatchCategory + inactSat?: MamSnatchCategory + inactUnsat?: MamSnatchCategory + unsat?: MamSnatchCategory & { limit: number } + duplicates?: MamSnatchCategory + reseed?: { name: string; count: number; inactive: number; red: boolean } + ite?: { name: string; count: number; latest: number } + + // notif (present when ?notif is set) + notifs?: { + pms: number + aboutToDropClient: number + tickets: number + waiting_tickets: number + requests: number + topics: number + } + + // clientStats (present when ?clientStats is set) + clientStats?: unknown[] +} + +export class MamAdapter implements TrackerAdapter { + async fetchStats( + baseUrl: string, + apiToken: string, + apiPath: string, + options?: FetchOptions + ): Promise { + const hostname = new URL(baseUrl).hostname + const url = new URL(apiPath, baseUrl) + url.searchParams.set("snatch_summary", "") + url.searchParams.set("notif", "") + + const data = await adapterFetch(url.toString(), hostname, options, { + Cookie: `mam_id=${apiToken}`, + }) + + if (!data.username) { + throw new Error(`Unexpected response from ${hostname}: missing username`) + } + + const uploaded = BigInt(Math.floor(data.uploaded_bytes ?? 0)) + const downloaded = BigInt(Math.floor(data.downloaded_bytes ?? 0)) + + const seedingCount = + (data.sSat?.count ?? 0) + + (data.seedHnr?.count ?? 0) + + (data.seedUnsat?.count ?? 0) + + (data.upAct?.count ?? 0) + + const platformMeta: MamPlatformMeta = { + vipUntil: data.vip_until ?? undefined, + connectable: data.connectable ?? undefined, + unsatisfiedCount: data.unsat?.count ?? undefined, + unsatisfiedLimit: data.unsat?.limit ?? undefined, + inactiveSatisfiedCount: data.inactSat?.count ?? undefined, + seedingHnrCount: data.seedHnr?.count ?? undefined, + inactiveUnsatisfiedCount: data.inactUnsat?.count ?? undefined, + trackerErrorCount: data.ite?.count ?? undefined, + recentlyDeleted: data.recently_deleted ?? undefined, + unreadPMs: data.notifs?.pms ?? undefined, + openTickets: data.notifs?.tickets ?? undefined, + pendingRequests: data.notifs?.requests ?? undefined, + unreadTopics: data.notifs?.topics ?? undefined, + } + + return { + username: data.username, + group: data.classname ?? "Unknown", + remoteUserId: data.uid, + uploadedBytes: uploaded, + downloadedBytes: downloaded, + ratio: typeof data.ratio === "number" ? data.ratio : parseFloat(String(data.ratio)) || 0, + bufferBytes: uploaded > downloaded ? uploaded - downloaded : BigInt(0), + seedingCount, + leechingCount: data.leeching?.count ?? 0, + seedbonus: data.seedbonus ?? null, + hitAndRuns: data.inactHnr?.count ?? null, + requiredRatio: null, + warned: null, + freeleechTokens: data.wedges ?? null, + platformMeta, + } + } + + async fetchRaw( + baseUrl: string, + apiToken: string, + apiPath: string, + options?: FetchOptions + ): Promise { + const hostname = new URL(baseUrl).hostname + const calls: DebugApiCall[] = [] + + const url = new URL(apiPath, baseUrl) + url.searchParams.set("snatch_summary", "") + url.searchParams.set("notif", "") + const endpoint = `${apiPath}?snatch_summary¬if` + + try { + const data = await adapterFetch>(url.toString(), hostname, options, { + Cookie: `mam_id=${apiToken}`, + }) + calls.push({ label: "User Stats", endpoint, data, error: null }) + } catch (err) { + calls.push({ + label: "User Stats", + endpoint, + data: null, + error: err instanceof Error ? err.message : "Request failed", + }) + } + + return calls + } +} diff --git a/src/lib/adapters/types.ts b/src/lib/adapters/types.ts index f51a6936..f9ef0e4f 100644 --- a/src/lib/adapters/types.ts +++ b/src/lib/adapters/types.ts @@ -21,7 +21,7 @@ export interface TrackerStats { lastAccessDate?: string shareScore?: number avatarUrl?: string - platformMeta?: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta + platformMeta?: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | MamPlatformMeta } export interface GGnPlatformMeta { @@ -96,6 +96,22 @@ export interface NebulancePlatformMeta { invites?: number } +export interface MamPlatformMeta { + vipUntil?: string + connectable?: string + unsatisfiedCount?: number + unsatisfiedLimit?: number + inactiveSatisfiedCount?: number + seedingHnrCount?: number + inactiveUnsatisfiedCount?: number + trackerErrorCount?: number + recentlyDeleted?: number + unreadPMs?: number + openTickets?: number + pendingRequests?: number + unreadTopics?: number +} + export interface FetchOptions { proxyAgent?: HttpAgent remoteUserId?: number From 2b210d76542db6a7377c13d7e9a9abf51cc48752 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:36:00 -0500 Subject: [PATCH 49/59] feat(mam): add bonus cap, VIP expiry, unsatisfied limit, and active HnR notifications --- src/app/api/notifications/[id]/route.ts | 50 +++++++++- src/app/api/notifications/route.ts | 4 + src/components/NotificationTargets.tsx | 36 ++++++++ src/lib/__tests__/notifications.test.ts | 1 + src/lib/__tests__/tracker-events.test.ts | 112 +++++++++++++++++++++++ src/lib/db/schema.ts | 4 + src/lib/notifications/dispatch.ts | 50 ++++++++++ src/lib/notifications/payload.ts | 28 ++++++ src/lib/notifications/types.ts | 7 ++ src/lib/scheduler.ts | 20 +++- src/lib/tracker-events.ts | 52 ++++++++++- 11 files changed, 358 insertions(+), 6 deletions(-) diff --git a/src/app/api/notifications/[id]/route.ts b/src/app/api/notifications/[id]/route.ts index 9766adab..4e53f153 100644 --- a/src/app/api/notifications/[id]/route.ts +++ b/src/app/api/notifications/[id]/route.ts @@ -34,7 +34,7 @@ export async function PATCH(req: Request, { params }: { params: Promise<{ id: st const updates: Record = { updatedAt: new Date() } - // Key is decoded lazily — only when config update is present + // Key is decoded lazily and only when config update is present let cachedKey: Buffer | null = null const getKey = () => { if (!cachedKey) cachedKey = decodeKey(auth as Exclude) @@ -82,6 +82,10 @@ export async function PATCH(req: Request, { params }: { params: Promise<{ id: st "notifyZeroSeeding", "notifyRankChange", "notifyAnniversary", + "notifyBonusCap", + "notifyVipExpiring", + "notifyUnsatisfiedLimit", + "notifyActiveHnrs", "includeTrackerName", ] as const) { if (key in fields) { @@ -99,7 +103,13 @@ export async function PATCH(req: Request, { params }: { params: Promise<{ id: st return NextResponse.json({ error: "thresholds must be an object or null" }, { status: 400 }) } else { const t = fields.thresholds as Record - const allowed = new Set(["ratioDropDelta", "bufferMilestoneBytes"]) + const allowed = new Set([ + "ratioDropDelta", + "bufferMilestoneBytes", + "bonusCapLimit", + "vipExpiringDays", + "unsatisfiedLimitPercent", + ]) for (const key of Object.keys(t)) { if (!allowed.has(key)) { return NextResponse.json({ error: `thresholds: unknown key "${key}"` }, { status: 400 }) @@ -127,6 +137,40 @@ export async function PATCH(req: Request, { params }: { params: Promise<{ id: st { status: 400 } ) } + if ( + "bonusCapLimit" in t && + (typeof t.bonusCapLimit !== "number" || + !Number.isInteger(t.bonusCapLimit) || + t.bonusCapLimit <= 0) + ) { + return NextResponse.json( + { error: "thresholds.bonusCapLimit must be a positive integer" }, + { status: 400 } + ) + } + if ( + "vipExpiringDays" in t && + (typeof t.vipExpiringDays !== "number" || + !Number.isInteger(t.vipExpiringDays) || + t.vipExpiringDays <= 0) + ) { + return NextResponse.json( + { error: "thresholds.vipExpiringDays must be a positive integer" }, + { status: 400 } + ) + } + if ( + "unsatisfiedLimitPercent" in t && + (typeof t.unsatisfiedLimitPercent !== "number" || + !Number.isFinite(t.unsatisfiedLimitPercent) || + t.unsatisfiedLimitPercent <= 0 || + t.unsatisfiedLimitPercent > 100) + ) { + return NextResponse.json( + { error: "thresholds.unsatisfiedLimitPercent must be a number between 1 and 100" }, + { status: 400 } + ) + } updates.thresholds = fields.thresholds } } @@ -159,7 +203,7 @@ export async function DELETE(_req: Request, { params }: { params: Promise<{ id: if (id instanceof NextResponse) return id // notificationDeliveryState rows are cleaned up automatically via FK cascade - // (onDelete: "cascade" on targetId) — no manual delete needed here. + // (onDelete: "cascade" on targetId) await db.delete(notificationTargets).where(eq(notificationTargets.id, id)) log.info({ route: "DELETE /api/notifications/[id]", targetId: id }, "notification target deleted") diff --git a/src/app/api/notifications/route.ts b/src/app/api/notifications/route.ts index 7a243cbe..2f46fc9a 100644 --- a/src/app/api/notifications/route.ts +++ b/src/app/api/notifications/route.ts @@ -32,6 +32,10 @@ export async function GET() { notifyZeroSeeding: t.notifyZeroSeeding, notifyRankChange: t.notifyRankChange, notifyAnniversary: t.notifyAnniversary, + notifyBonusCap: t.notifyBonusCap, + notifyVipExpiring: t.notifyVipExpiring, + notifyUnsatisfiedLimit: t.notifyUnsatisfiedLimit, + notifyActiveHnrs: t.notifyActiveHnrs, thresholds: t.thresholds, includeTrackerName: t.includeTrackerName, scope: t.scope, diff --git a/src/components/NotificationTargets.tsx b/src/components/NotificationTargets.tsx index 70de2148..a4a5ae19 100644 --- a/src/components/NotificationTargets.tsx +++ b/src/components/NotificationTargets.tsx @@ -35,6 +35,10 @@ interface NotificationTarget { notifyZeroSeeding: boolean notifyRankChange: boolean notifyAnniversary: boolean + notifyBonusCap: boolean + notifyVipExpiring: boolean + notifyUnsatisfiedLimit: boolean + notifyActiveHnrs: boolean thresholds: { ratioDropDelta?: number; bufferMilestoneBytes?: number } | null includeTrackerName: boolean scope: number[] | null @@ -83,6 +87,10 @@ const DRAFT_KEYS: (keyof NotificationTarget)[] = [ "notifyZeroSeeding", "notifyRankChange", "notifyAnniversary", + "notifyBonusCap", + "notifyVipExpiring", + "notifyUnsatisfiedLimit", + "notifyActiveHnrs", "thresholds", "includeTrackerName", "scope", @@ -408,6 +416,34 @@ function NotificationCard({ target, onSaved, onRemove }: NotificationCardProps) checked={draft.notifyAnniversary} onChange={(v) => updateDraft({ notifyAnniversary: v })} /> + + updateDraft({ notifyBonusCap: v })} + description="Alert when seedbonus hits the cap (99,999 on MAM)" + /> + + updateDraft({ notifyVipExpiring: v })} + description="Alert when VIP status is about to expire" + /> + + updateDraft({ notifyUnsatisfiedLimit: v })} + description="Alert when approaching the unsatisfied torrent limit" + /> + + updateDraft({ notifyActiveHnrs: v })} + description="Alert when inactive Hit & Runs are detected" + />

diff --git a/src/lib/__tests__/notifications.test.ts b/src/lib/__tests__/notifications.test.ts index 2e1ee30f..b5427903 100644 --- a/src/lib/__tests__/notifications.test.ts +++ b/src/lib/__tests__/notifications.test.ts @@ -77,6 +77,7 @@ function makeContext(overrides: Partial = {}): SnapshotContext trackerPausedAt: null, trackerJoinedAt: "2020-01-01", minimumRatio: 0.6, + mamContext: undefined, ...overrides, } } diff --git a/src/lib/__tests__/tracker-events.test.ts b/src/lib/__tests__/tracker-events.test.ts index 0eb92711..ad4f35dd 100644 --- a/src/lib/__tests__/tracker-events.test.ts +++ b/src/lib/__tests__/tracker-events.test.ts @@ -4,7 +4,9 @@ import { afterEach, describe, expect, it, vi } from "vitest" import type { NotificationEventType } from "@/lib/notifications/types" import { VALID_EVENT_TYPES } from "@/lib/notifications/types" import { + checkActiveHnrs, checkAnniversaryMilestone, + checkBonusCapReached, checkBufferMilestoneCrossed, checkHnrIncrease, checkRankChange, @@ -12,6 +14,8 @@ import { checkRatioBelowMinimumTransition, checkRatioDelta, checkTrackerError, + checkUnsatisfiedLimitApproaching, + checkVipExpiringSoon, checkWarned, checkWarnedTransition, checkZeroSeeding, @@ -325,3 +329,111 @@ describe("EVENT_SNOOZE_MS", () => { expect(EVENT_SNOOZE_MS.anniversary).toBe(sevenDays) }) }) + +describe("checkBonusCapReached", () => { + it("returns true when current >= capLimit and previous was below cap (transition)", () => { + expect(checkBonusCapReached(1000, 800, 1000)).toBe(true) + }) + it("returns true when current exceeds capLimit and previous was below cap", () => { + expect(checkBonusCapReached(1200, 800, 1000)).toBe(true) + }) + it("returns false when current is below capLimit", () => { + expect(checkBonusCapReached(900, 800, 1000)).toBe(false) + }) + it("returns false when previous was already at or above capLimit (already notified)", () => { + expect(checkBonusCapReached(1100, 1000, 1000)).toBe(false) + }) + it("returns false when currentBonus is null", () => { + expect(checkBonusCapReached(null, 800, 1000)).toBe(false) + }) + it("returns false when currentBonus is undefined", () => { + expect(checkBonusCapReached(undefined, 800, 1000)).toBe(false) + }) + it("returns true when previousBonus is null and current >= capLimit (first poll at cap fires)", () => { + expect(checkBonusCapReached(1000, null, 1000)).toBe(true) + }) +}) + +describe("checkVipExpiringSoon", () => { + afterEach(() => { + vi.useRealTimers() + }) + + it("returns true when expiry is within threshold days", () => { + vi.useFakeTimers() + vi.setSystemTime(new Date("2026-03-26T00:00:00Z")) + // Expires in 3 days, threshold is 7 + expect(checkVipExpiringSoon("2026-03-29T00:00:00Z", 7)).toBe(true) + }) + it("returns false when expiry is beyond threshold days", () => { + vi.useFakeTimers() + vi.setSystemTime(new Date("2026-03-26T00:00:00Z")) + // Expires in 30 days, threshold is 7 + expect(checkVipExpiringSoon("2026-04-25T00:00:00Z", 7)).toBe(false) + }) + it("returns false when vipUntil is null", () => { + expect(checkVipExpiringSoon(null, 7)).toBe(false) + }) + it("returns false when vipUntil is undefined", () => { + expect(checkVipExpiringSoon(undefined, 7)).toBe(false) + }) + it("returns false when vipUntil is an invalid date string", () => { + expect(checkVipExpiringSoon("not-a-date", 7)).toBe(false) + }) + it("returns false when VIP has already expired (date in the past)", () => { + vi.useFakeTimers() + vi.setSystemTime(new Date("2026-03-26T00:00:00Z")) + expect(checkVipExpiringSoon("2026-03-20T00:00:00Z", 7)).toBe(false) + }) +}) + +describe("checkUnsatisfiedLimitApproaching", () => { + it("returns true when count/limit ratio meets the percent threshold", () => { + // 80/100 = 80% >= 80% + expect(checkUnsatisfiedLimitApproaching(80, 100, 80)).toBe(true) + }) + it("returns true when count/limit ratio exceeds the percent threshold", () => { + // 90/100 = 90% >= 80% + expect(checkUnsatisfiedLimitApproaching(90, 100, 80)).toBe(true) + }) + it("returns false when count/limit ratio is below threshold", () => { + // 50/100 = 50% < 80% + expect(checkUnsatisfiedLimitApproaching(50, 100, 80)).toBe(false) + }) + it("returns false when count is null", () => { + expect(checkUnsatisfiedLimitApproaching(null, 100, 80)).toBe(false) + }) + it("returns false when count is undefined", () => { + expect(checkUnsatisfiedLimitApproaching(undefined, 100, 80)).toBe(false) + }) + it("returns false when limit is null", () => { + expect(checkUnsatisfiedLimitApproaching(80, null, 80)).toBe(false) + }) + it("returns false when limit is 0 (division by zero guard)", () => { + expect(checkUnsatisfiedLimitApproaching(80, 0, 80)).toBe(false) + }) +}) + +describe("checkActiveHnrs", () => { + it("returns true when count increased from 0 to 2 (transition)", () => { + expect(checkActiveHnrs(2, 0)).toBe(true) + }) + it("returns true when count increased from a prior positive value", () => { + expect(checkActiveHnrs(5, 3)).toBe(true) + }) + it("returns false when count is 0", () => { + expect(checkActiveHnrs(0, 0)).toBe(false) + }) + it("returns false when count is null", () => { + expect(checkActiveHnrs(null, 0)).toBe(false) + }) + it("returns false when count is undefined", () => { + expect(checkActiveHnrs(undefined, 0)).toBe(false) + }) + it("returns false when count stayed the same (already notified)", () => { + expect(checkActiveHnrs(3, 3)).toBe(false) + }) + it("returns false when count decreased (resolving HnRs)", () => { + expect(checkActiveHnrs(2, 5)).toBe(false) + }) +}) diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index c31390ea..a4169c4a 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -282,6 +282,10 @@ export const notificationTargets = pgTable("notification_targets", { notifyZeroSeeding: boolean("notify_zero_seeding").default(false).notNull(), notifyRankChange: boolean("notify_rank_change").default(false).notNull(), notifyAnniversary: boolean("notify_anniversary").default(false).notNull(), + notifyBonusCap: boolean("notify_bonus_cap").default(false).notNull(), + notifyVipExpiring: boolean("notify_vip_expiring").default(false).notNull(), + notifyUnsatisfiedLimit: boolean("notify_unsatisfied_limit").default(false).notNull(), + notifyActiveHnrs: boolean("notify_active_hnrs").default(false).notNull(), // Event thresholds — nullable JSONB; null means use application defaults. // Shape: { ratioDropDelta?: number, bufferMilestoneBytes?: number } diff --git a/src/lib/notifications/dispatch.ts b/src/lib/notifications/dispatch.ts index f6b4112d..1ecdbc31 100644 --- a/src/lib/notifications/dispatch.ts +++ b/src/lib/notifications/dispatch.ts @@ -3,6 +3,7 @@ // Functions: dispatchNotifications, detectEvents, buildEventData import { eq } from "drizzle-orm" +import { MAM_BONUS_CAP } from "@/lib/adapters/constants" import { db } from "@/lib/db" import { notificationDeliveryState, notificationTargets } from "@/lib/db/schema" import { log } from "@/lib/logger" @@ -15,12 +16,16 @@ import type { NotificationThresholds, } from "@/lib/notifications/types" import { + checkActiveHnrs, checkAnniversaryMilestone, + checkBonusCapReached, checkBufferMilestoneCrossed, checkHnrIncrease, checkRankChange, checkRatioBelowMinimumTransition, checkRatioDelta, + checkUnsatisfiedLimitApproaching, + checkVipExpiringSoon, checkWarnedTransition, checkZeroSeeding, EVENT_SNOOZE_MS, @@ -47,6 +52,16 @@ export interface SnapshotContext { trackerPausedAt: string | null trackerJoinedAt: string | null minimumRatio: number | undefined + // MAM-specific fields grouped into sub-object; undefined for non-MAM trackers + mamContext?: { + currentSeedbonus: number | null + previousSeedbonus: number | null + vipUntil: string | null + unsatisfiedCount: number | null + unsatisfiedLimit: number | null + inactiveHnrCount: number | null + previousInactiveHnrCount: number | null + } } export async function dispatchNotifications( @@ -257,6 +272,33 @@ export function detectEvents( events.push("anniversary") } + if (target.notifyBonusCap) { + const capLimit = (thresholds?.bonusCapLimit as number | undefined) ?? MAM_BONUS_CAP + if (checkBonusCapReached(ctx.mamContext?.currentSeedbonus ?? null, ctx.mamContext?.previousSeedbonus ?? null, capLimit)) { + events.push("bonus_cap") + } + } + + if (target.notifyVipExpiring) { + const days = (thresholds?.vipExpiringDays as number | undefined) ?? 7 + if (checkVipExpiringSoon(ctx.mamContext?.vipUntil ?? null, days)) { + events.push("vip_expiring") + } + } + + if (target.notifyUnsatisfiedLimit) { + const pct = (thresholds?.unsatisfiedLimitPercent as number | undefined) ?? 80 + if (checkUnsatisfiedLimitApproaching(ctx.mamContext?.unsatisfiedCount ?? null, ctx.mamContext?.unsatisfiedLimit ?? null, pct)) { + events.push("unsatisfied_limit") + } + } + + if (target.notifyActiveHnrs) { + if (checkActiveHnrs(ctx.mamContext?.inactiveHnrCount ?? null, ctx.mamContext?.previousInactiveHnrCount ?? null)) { + events.push("active_hnrs") + } + } + return events } @@ -284,6 +326,14 @@ export function buildEventData( return { newGroup: ctx.currentGroup, previousGroup: ctx.previousGroup } case "anniversary": return { label: anniversaryLabel ?? "Anniversary" } + case "bonus_cap": + return { currentBonus: ctx.mamContext?.currentSeedbonus ?? null, capLimit: MAM_BONUS_CAP } + case "vip_expiring": + return { vipUntil: ctx.mamContext?.vipUntil ?? null } + case "unsatisfied_limit": + return { count: ctx.mamContext?.unsatisfiedCount ?? null, limit: ctx.mamContext?.unsatisfiedLimit ?? null } + case "active_hnrs": + return { count: ctx.mamContext?.inactiveHnrCount ?? null } default: return {} } diff --git a/src/lib/notifications/payload.ts b/src/lib/notifications/payload.ts index fba863a6..a0ac77f8 100644 --- a/src/lib/notifications/payload.ts +++ b/src/lib/notifications/payload.ts @@ -37,6 +37,10 @@ const EVENT_COLORS: Record = { zero_seeding: hexToInt(CHART_THEME.warn), rank_change: hexToInt(CHART_THEME.accent), anniversary: hexToInt(CHART_THEME.accent), + bonus_cap: hexToInt(CHART_THEME.warn), + vip_expiring: hexToInt(CHART_THEME.warn), + unsatisfied_limit: hexToInt(CHART_THEME.danger), + active_hnrs: hexToInt(CHART_THEME.danger), } const EVENT_TITLES: Record = { @@ -49,6 +53,10 @@ const EVENT_TITLES: Record = { zero_seeding: "Zero Active Seeds", rank_change: "Rank Change", anniversary: "Membership Anniversary", + bonus_cap: "Bonus Cap Reached", + vip_expiring: "VIP Expiring Soon", + unsatisfied_limit: "Unsatisfied Limit Approaching", + active_hnrs: "New Inactive Hit & Run", } export function buildDiscordEmbed(input: EmbedInput): DiscordEmbed { @@ -107,6 +115,26 @@ function buildDescription( const label = data.label as string | undefined return label ? `${source} — ${label}` : `${source} membership anniversary` } + case "bonus_cap": { + const current = Number(data.currentBonus ?? 0).toLocaleString() + const cap = Number(data.capLimit ?? 0).toLocaleString() + return `${source} bonus points at **${current}** (cap: ${cap}). Spend them before they're wasted!` + } + case "vip_expiring": { + const expiry = data.vipUntil ? new Date(String(data.vipUntil)) : null + const days = expiry ? Math.ceil((expiry.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : 0 + return `${source} VIP status expires in **${days} day${days !== 1 ? "s" : ""}**` + } + case "unsatisfied_limit": { + const count = Number(data.count ?? 0) + const limit = Number(data.limit ?? 0) + const pct = limit > 0 ? Math.round((count / limit) * 100) : 0 + return `${source} unsatisfied torrents at **${count}/${limit}** (${pct}%). Download capacity running low.` + } + case "active_hnrs": { + const count = Number(data.count ?? 0) + return `${source} has **${count}** active Hit & Run${count !== 1 ? "s" : ""}. Seed them to avoid penalties.` + } default: return `${source} triggered a notification` } diff --git a/src/lib/notifications/types.ts b/src/lib/notifications/types.ts index 424836b8..62a80872 100644 --- a/src/lib/notifications/types.ts +++ b/src/lib/notifications/types.ts @@ -15,12 +15,19 @@ export const VALID_EVENT_TYPES = [ "zero_seeding", "rank_change", "anniversary", + "bonus_cap", + "vip_expiring", + "unsatisfied_limit", + "active_hnrs", ] as const export type NotificationEventType = (typeof VALID_EVENT_TYPES)[number] export interface NotificationThresholds { ratioDropDelta?: number // i.e 0.1 — alert when ratio falls by ≥0.1 bufferMilestoneBytes?: number //i.e 10737418240 — alert when buffer crosses 10 GiB + bonusCapLimit?: number // default 99999 — fires when seedbonus hits or exceeds cap + vipExpiringDays?: number // default 7 — fires when VIP expiry is within N days + unsatisfiedLimitPercent?: number // default 80 — fires at 80% of unsatisfied limit } // Per-type config shapes (decrypted form — never stored plaintext) diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index 9cc45b23..c02008c0 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -7,7 +7,7 @@ import { and, desc, eq, isNotNull, lt, notInArray, sql } from "drizzle-orm" import cron, { type ScheduledTask } from "node-cron" import { findRegistryEntry } from "@/data/tracker-registry" import { buildFetchOptions, getAdapter } from "@/lib/adapters" -import type { TrackerStats } from "@/lib/adapters/types" +import type { MamPlatformMeta, TrackerStats } from "@/lib/adapters/types" import { pruneDismissedAlerts } from "@/lib/alert-pruning" import { startBackupScheduler, stopBackupScheduler } from "@/lib/backup-scheduler" import { @@ -192,6 +192,7 @@ export async function pollTracker( warned: trackerSnapshots.warned, group: trackerSnapshots.group, seedingCount: trackerSnapshots.seedingCount, + seedbonus: trackerSnapshots.seedbonus, }) .from(trackerSnapshots) .where(eq(trackerSnapshots.trackerId, tracker.id)) @@ -250,6 +251,11 @@ export async function pollTracker( } try { + const mamMeta = + tracker.platformType === "mam" + ? (stats.platformMeta as MamPlatformMeta | undefined) + : undefined + await dispatchNotifications( { trackerId: tracker.id, @@ -259,7 +265,7 @@ export async function pollTracker( previousRatio: previousSnapshot?.ratio ?? null, currentHnrs: stats.hitAndRuns, previousHnrs: previousSnapshot?.hitAndRuns ?? null, - currentBufferBytes: stats.bufferBytes !== null ? stats.bufferBytes : null, + currentBufferBytes: stats.bufferBytes, previousBufferBytes: previousSnapshot?.bufferBytes ?? null, trackerDown: false, trackerError: null, @@ -272,6 +278,15 @@ export async function pollTracker( trackerPausedAt: null, trackerJoinedAt: tracker.joinedAt ?? null, minimumRatio: findRegistryEntry(tracker.baseUrl)?.rules?.minimumRatio, + mamContext: tracker.platformType === "mam" ? { + currentSeedbonus: stats.seedbonus ?? null, + previousSeedbonus: previousSnapshot?.seedbonus ?? null, + vipUntil: mamMeta?.vipUntil ?? null, + unsatisfiedCount: mamMeta?.unsatisfiedCount ?? null, + unsatisfiedLimit: mamMeta?.unsatisfiedLimit ?? null, + inactiveHnrCount: stats.hitAndRuns ?? null, + previousInactiveHnrCount: previousSnapshot?.hitAndRuns ?? null, + } : undefined, }, encryptionKey, enabledTargets @@ -345,6 +360,7 @@ export async function pollTracker( trackerPausedAt: tracker?.pausedAt?.toISOString() ?? null, trackerJoinedAt: tracker?.joinedAt ?? null, minimumRatio: undefined, + mamContext: undefined, }, encryptionKey, enabledTargets diff --git a/src/lib/tracker-events.ts b/src/lib/tracker-events.ts index ebbe9a53..8388ca50 100644 --- a/src/lib/tracker-events.ts +++ b/src/lib/tracker-events.ts @@ -3,7 +3,8 @@ // Functions: checkRatioBelowMinimum, checkRatioDelta, checkRatioBelowMinimumTransition, // checkTrackerError, checkWarned, checkWarnedTransition, checkZeroSeeding, // checkHnrIncrease, checkBufferMilestoneCrossed, checkRankChange, -// checkAnniversaryMilestone, EVENT_SNOOZE_MS +// checkAnniversaryMilestone, checkBonusCapReached, checkVipExpiringSoon, +// checkUnsatisfiedLimitApproaching, checkActiveHnrs, EVENT_SNOOZE_MS // // Shared pure-function event detection checks. No framework imports, no DB imports. // Importable from both client-side dashboard code and server-side scheduler code. @@ -148,6 +149,51 @@ export function checkAnniversaryMilestone( return null } +// ─── MAM-specific events ────────────────────────────────────────────────────── + +/** Fires when seedbonus hits or exceeds the cap. Transition-based: only fires if previous was below cap. */ +export function checkBonusCapReached( + currentBonus: number | null | undefined, + previousBonus: number | null | undefined, + capLimit: number +): boolean { + if (currentBonus == null) return false + if (previousBonus != null && previousBonus >= capLimit) return false + return currentBonus >= capLimit +} + +/** Fires when VIP expiry is within N days from now. */ +export function checkVipExpiringSoon( + vipUntil: string | null | undefined, + thresholdDays: number +): boolean { + if (!vipUntil) return false + const expiry = new Date(vipUntil) + if (Number.isNaN(expiry.getTime())) return false + const daysRemaining = (expiry.getTime() - Date.now()) / (1000 * 60 * 60 * 24) + return daysRemaining > 0 && daysRemaining <= thresholdDays +} + +/** Fires when unsatisfied count reaches or exceeds the percent threshold of the limit. */ +export function checkUnsatisfiedLimitApproaching( + unsatisfiedCount: number | null | undefined, + unsatisfiedLimit: number | null | undefined, + percentThreshold: number +): boolean { + if (unsatisfiedCount == null || unsatisfiedLimit == null || unsatisfiedLimit === 0) return false + return (unsatisfiedCount / unsatisfiedLimit) * 100 >= percentThreshold +} + +/** Fires when inactive HnR count increases (transition-based). */ +export function checkActiveHnrs( + inactiveHnrCount: number | null | undefined, + previousInactiveHnrCount: number | null | undefined +): boolean { + if (inactiveHnrCount == null || inactiveHnrCount <= 0) return false + if (previousInactiveHnrCount != null && previousInactiveHnrCount >= inactiveHnrCount) return false + return true +} + // ─── Snooze durations ──────────────────────────────────────────────────────── // Per-event-type snooze duration map. Events with different urgency/frequency profiles @@ -162,4 +208,8 @@ export const EVENT_SNOOZE_MS: Record = { zero_seeding: 24 * 60 * 60 * 1000, // 24 hours — state-based, fires while at 0 seeds rank_change: 7 * 24 * 60 * 60 * 1000, // 7 days — rare event, one notification per change anniversary: 7 * 24 * 60 * 60 * 1000, // 7 days — longer than the ±3-day detection window + bonus_cap: 24 * 60 * 60 * 1000, // 24 hours + vip_expiring: 24 * 60 * 60 * 1000, // 24 hours + unsatisfied_limit: 6 * 60 * 60 * 1000, // 6 hours + active_hnrs: 6 * 60 * 60 * 1000, // 6 hours } From 3bb280e302ae4f4afeab667b912f5cea0e6f501c Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:39:20 -0500 Subject: [PATCH 50/59] feat(mam): add platform UI with health overview, badges, and FL Wedges chart --- src/components/AddTrackerDialog.tsx | 16 +- src/components/charts/MetricChart.tsx | 8 +- .../tracker-detail/AnalyticsCharts.tsx | 8 + .../tracker-detail/platform/MamBonusWaste.tsx | 33 +++ .../platform/MamHealthOverview.tsx | 201 ++++++++++++++++++ .../platform/MamSatisfactionProgress.tsx | 40 ++++ .../platform/MamTorrentHealth.tsx | 67 ++++++ .../platform/MamVipCountdown.tsx | 42 ++++ .../tracker-detail/slot-registry.ts | 159 +++++++++++++- src/lib/__tests__/slot-resolve.test.tsx | 2 + src/lib/slot-types.ts | 3 +- 11 files changed, 566 insertions(+), 13 deletions(-) create mode 100644 src/components/tracker-detail/platform/MamBonusWaste.tsx create mode 100644 src/components/tracker-detail/platform/MamHealthOverview.tsx create mode 100644 src/components/tracker-detail/platform/MamSatisfactionProgress.tsx create mode 100644 src/components/tracker-detail/platform/MamTorrentHealth.tsx create mode 100644 src/components/tracker-detail/platform/MamVipCountdown.tsx diff --git a/src/components/AddTrackerDialog.tsx b/src/components/AddTrackerDialog.tsx index 2365c22c..ad1f9ba2 100644 --- a/src/components/AddTrackerDialog.tsx +++ b/src/components/AddTrackerDialog.tsx @@ -8,9 +8,9 @@ import { H2 } from "@typography" import clsx from "clsx" import Image from "next/image" import { - type FormEvent, type KeyboardEvent, type MouseEvent, + type SyntheticEvent, useCallback, useEffect, useMemo, @@ -348,7 +348,7 @@ function AddTrackerDialog({ return next } - async function handleSubmit(e: FormEvent) { + async function handleSubmit(e: SyntheticEvent) { e.preventDefault() const validationErrors = validate() @@ -491,11 +491,13 @@ function AddTrackerDialog({ value={apiToken} onChange={(e) => setApiToken(e.target.value)} placeholder={ - selectedEntry?.platform === "ggn" - ? "Your GGn API key" - : selectedEntry?.platform === "gazelle" - ? "Your Gazelle API key" - : "Your UNIT3D API token" + selectedEntry?.platform === "mam" + ? "Your mam_id session cookie" + : selectedEntry?.platform === "ggn" + ? "Your GGn API key" + : selectedEntry?.platform === "gazelle" + ? "Your Gazelle API key" + : "Your UNIT3D API token" } error={errors.apiToken} /> diff --git a/src/components/charts/MetricChart.tsx b/src/components/charts/MetricChart.tsx index db5853ce..ba8bfddf 100644 --- a/src/components/charts/MetricChart.tsx +++ b/src/components/charts/MetricChart.tsx @@ -40,7 +40,7 @@ import { useLogScale } from "./lib/useLogScale" // ── Types ── -type Metric = "ratio" | "buffer" | "seedbonus" | "seedingCount" | "dailyDelta" | "shareScore" +type Metric = "ratio" | "buffer" | "seedbonus" | "seedingCount" | "dailyDelta" | "shareScore" | "freeleechTokens" interface MetricConfig { label: string @@ -89,6 +89,12 @@ const METRIC_CONFIGS: Record, MetricConfig> = { getValue: (s) => s.seedingCount, isInteger: true, }, + freeleechTokens: { + label: "Freeleech Tokens", + unit: "tokens", + getValue: (s) => s.freeleechTokens, + isInteger: true, + }, } const BORDER_SOFT = CHART_THEME.gridLine diff --git a/src/components/tracker-detail/AnalyticsCharts.tsx b/src/components/tracker-detail/AnalyticsCharts.tsx index a3bf6959..a7b03c5f 100644 --- a/src/components/tracker-detail/AnalyticsCharts.tsx +++ b/src/components/tracker-detail/AnalyticsCharts.tsx @@ -94,6 +94,14 @@ export function AnalyticsCharts({ )} + {/* FL Wedges (MAM only) */} + {platformType === "mam" && ( + +

FL Wedges

+ +
+ )} + {/* Gazelle Percentile Radar */} {gazelleMeta?.ranks && ( diff --git a/src/components/tracker-detail/platform/MamBonusWaste.tsx b/src/components/tracker-detail/platform/MamBonusWaste.tsx new file mode 100644 index 00000000..9d2a6d26 --- /dev/null +++ b/src/components/tracker-detail/platform/MamBonusWaste.tsx @@ -0,0 +1,33 @@ +// src/components/tracker-detail/platform/MamBonusWaste.tsx + +import { MAM_BONUS_CAP } from "@/lib/adapters/constants" + +export interface MamBonusWasteProps { + seedbonus: number + seedingCount: number +} +// Rough average — MAM's actual formula varies by torrent size, seeder count, etc. +const ESTIMATED_POINTS_PER_SEED_PER_HOUR = 0.5 + +export function MamBonusWaste({ seedbonus, seedingCount }: MamBonusWasteProps) { + if (seedbonus < MAM_BONUS_CAP) return null + + const wastePerHour = seedingCount * ESTIMATED_POINTS_PER_SEED_PER_HOUR + const wastePerDay = Math.round(wastePerHour * 24) + + return ( +
+ + Bonus Cap Reached + + + {seedbonus.toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} + + {wastePerDay > 0 && ( +

+ ~{wastePerDay.toLocaleString()} pts/day wasted +

+ )} +
+ ) +} diff --git a/src/components/tracker-detail/platform/MamHealthOverview.tsx b/src/components/tracker-detail/platform/MamHealthOverview.tsx new file mode 100644 index 00000000..6f6bceeb --- /dev/null +++ b/src/components/tracker-detail/platform/MamHealthOverview.tsx @@ -0,0 +1,201 @@ +// src/components/tracker-detail/platform/MamHealthOverview.tsx +// +// Composite layout for all MAM progress visualizations. +// Replaces the 4 individual full-width progress slots with a single +// component that handles internal composition and visual hierarchy. + +import { ProgressBar } from "@/components/ui/ProgressBar" +import { Tooltip } from "@/components/ui/Tooltip" +import { MAM_BONUS_CAP } from "@/lib/adapters/constants" +import type { MamPlatformMeta } from "@/lib/adapters/types" + +export interface MamHealthOverviewProps { + meta: MamPlatformMeta + seedingCount: number + leechingCount: number + hitAndRuns: number + seedbonus: number | null + accentColor: string + vipUntil: string | null + unsatisfiedCount: number | null + unsatisfiedLimit: number | null +} + +// ── Bonus cap waste estimate ──────────────────────────────────────────────── +const ESTIMATED_POINTS_PER_SEED_PER_HOUR = 0.5 // Rough average — MAM formula varies by torrent + +// ── Torrent health segments ───────────────────────────────────────────────── +const SEGMENTS = [ + { key: "seeding", label: "Seeding", color: "var(--color-success)" }, + { key: "seedingHnr", label: "Seeding HnR", color: "var(--color-warn)" }, + { key: "leeching", label: "Leeching", color: "var(--color-accent)" }, + { key: "inactiveHnr", label: "Inactive HnR", color: "var(--color-danger)" }, + { key: "preHnr", label: "Pre-HnR", color: "var(--color-warn)" }, + { key: "completed", label: "Completed", color: "var(--color-text-tertiary)" }, +] as const + +function formatResult(msg: string): string { + switch (msg) { + case "Completed": + return "IP updated" + case "No change": + case "No Change": + return "No change needed" + default: + return msg + } +} + +export function MamHealthOverview({ + meta, + seedingCount, + leechingCount, + hitAndRuns, + seedbonus, + accentColor, + vipUntil, + unsatisfiedCount, + unsatisfiedLimit, +}: MamHealthOverviewProps) { + // ── VIP countdown data ────────────────────────────────────────────────── + let vipDays: number | null = null + let vipPct = 0 + let vipColor = accentColor + let vipDateStr = "" + if (vipUntil) { + const expiry = new Date(vipUntil) + if (!Number.isNaN(expiry.getTime())) { + const ms = expiry.getTime() - Date.now() + if (ms > 0) { + vipDays = Math.ceil(ms / (1000 * 60 * 60 * 24)) + vipPct = (vipDays / 90) * 100 + if (vipDays <= 3) vipColor = "var(--color-danger)" + else if (vipDays <= 7) vipColor = "var(--color-warn)" + vipDateStr = expiry.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }) + } + } + } + + // ── Satisfaction data ─────────────────────────────────────────────────── + const hasCapacity = unsatisfiedLimit != null && unsatisfiedLimit > 0 + const satUsed = hasCapacity ? Math.min(unsatisfiedCount ?? 0, unsatisfiedLimit!) : 0 + const satPct = hasCapacity ? (satUsed / unsatisfiedLimit!) * 100 : 0 + const satRemaining = hasCapacity ? unsatisfiedLimit! - satUsed : 0 + let satColor = accentColor + if (satPct >= 90) satColor = "var(--color-danger)" + else if (satPct >= 70) satColor = "var(--color-warn)" + + // ── Torrent health data ──────────────────────────────────────────────── + const healthValues: Record = { + seeding: Math.max(0, seedingCount - (meta.seedingHnrCount ?? 0)), + seedingHnr: meta.seedingHnrCount ?? 0, + leeching: leechingCount, + inactiveHnr: hitAndRuns, + preHnr: meta.inactiveUnsatisfiedCount ?? 0, + completed: meta.inactiveSatisfiedCount ?? 0, + } + const healthTotal = Object.values(healthValues).reduce((a, b) => a + b, 0) + + // ── Bonus cap data ───────────────────────────────────────────────────── + const atCap = seedbonus != null && seedbonus >= MAM_BONUS_CAP + const wastePerDay = atCap + ? Math.round(seedingCount * ESTIMATED_POINTS_PER_SEED_PER_HOUR * 24) + : 0 + + const hasVip = vipDays != null + const hasHealth = healthTotal > 0 + + // Nothing to show + if (!hasVip && !hasCapacity && !hasHealth && !atCap) return null + + return ( +
+ {/* ── Top-left: VIP Countdown ──────────────────────────────────────── */} + {hasVip && ( +
+
+ VIP Expires + {vipDays}d +
+ +

{vipDateStr}

+
+ )} + + {/* ── Top-right: Download Capacity ─────────────────────────────────── */} + {hasCapacity && ( +
+
+ Download Capacity + + {satRemaining} / {unsatisfiedLimit} slots + +
+ +

+ {satUsed} unsatisfied torrent{satUsed !== 1 ? "s" : ""} +

+
+ )} + + {/* ── Bottom-left: Torrent Health ──────────────────────────────────── */} + {hasHealth && ( +
+
+ Torrent Health + + + +
+
+ {SEGMENTS.map(({ key, label, color }) => { + const pct = (healthValues[key] / healthTotal) * 100 + if (pct <= 0) return null + return ( +
+ ) + })} +
+
+ {SEGMENTS.map(({ key, label, color }) => { + if (healthValues[key] <= 0) return null + return ( + + + {label}: {healthValues[key].toLocaleString()} + + ) + })} +
+
+ )} + + {/* ── Bottom-right: Bonus Cap Warning ──────────────────────────────── */} + {atCap && ( +
+ Bonus Cap Reached + + {seedbonus!.toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} + + {wastePerDay > 0 && ( +

+ ~{wastePerDay.toLocaleString()} pts/day wasted +

+ )} +
+ )} +
+ ) +} diff --git a/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx b/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx new file mode 100644 index 00000000..5c3040e1 --- /dev/null +++ b/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx @@ -0,0 +1,40 @@ +// src/components/tracker-detail/platform/MamSatisfactionProgress.tsx + +import { ProgressBar } from "@/components/ui/ProgressBar" + +export interface MamSatisfactionProgressProps { + unsatisfiedCount: number + unsatisfiedLimit: number + accentColor: string +} + +export function MamSatisfactionProgress({ + unsatisfiedCount, + unsatisfiedLimit, + accentColor, +}: MamSatisfactionProgressProps) { + if (unsatisfiedLimit <= 0) return null + + const used = Math.min(unsatisfiedCount, unsatisfiedLimit) + const pct = (used / unsatisfiedLimit) * 100 + const remaining = unsatisfiedLimit - used + + let barColor = accentColor + if (pct >= 90) barColor = "var(--color-danger)" + else if (pct >= 70) barColor = "var(--color-warn)" + + return ( +
+
+ Download Capacity + + {remaining} / {unsatisfiedLimit} slots + +
+ +

+ {used} unsatisfied torrent{used !== 1 ? "s" : ""} +

+
+ ) +} diff --git a/src/components/tracker-detail/platform/MamTorrentHealth.tsx b/src/components/tracker-detail/platform/MamTorrentHealth.tsx new file mode 100644 index 00000000..647842d5 --- /dev/null +++ b/src/components/tracker-detail/platform/MamTorrentHealth.tsx @@ -0,0 +1,67 @@ +// src/components/tracker-detail/platform/MamTorrentHealth.tsx + +import type { MamPlatformMeta } from "@/lib/adapters/types" + +export interface MamTorrentHealthProps { + meta: MamPlatformMeta + seedingCount: number + leechingCount: number + hitAndRuns: number +} + +const SEGMENTS = [ + { key: "seeding", label: "Seeding", color: "var(--color-success)" }, + { key: "seedingHnr", label: "Seeding HnR", color: "var(--color-warn)" }, + { key: "leeching", label: "Leeching", color: "var(--color-accent)" }, + { key: "inactiveHnr", label: "Inactive HnR", color: "var(--color-danger)" }, + { key: "preHnr", label: "Pre-HnR", color: "var(--color-warn)" }, + { key: "completed", label: "Completed", color: "var(--color-text-tertiary)" }, +] as const + +export function MamTorrentHealth({ meta, seedingCount, leechingCount, hitAndRuns }: MamTorrentHealthProps) { + const values: Record = { + seeding: Math.max(0, seedingCount - (meta.seedingHnrCount ?? 0)), + seedingHnr: meta.seedingHnrCount ?? 0, + leeching: leechingCount, + inactiveHnr: hitAndRuns, + preHnr: meta.inactiveUnsatisfiedCount ?? 0, + completed: meta.inactiveSatisfiedCount ?? 0, + } + + const total = Object.values(values).reduce((a, b) => a + b, 0) + if (total === 0) return null + + return ( +
+ Torrent Health +
+ {SEGMENTS.map(({ key, label, color }) => { + const pct = (values[key] / total) * 100 + if (pct <= 0) return null + return ( +
+ ) + })} +
+
+ {SEGMENTS.map(({ key, label, color }) => { + if (values[key] <= 0) return null + return ( + + + {label}: {values[key].toLocaleString()} + + ) + })} +
+
+ ) +} diff --git a/src/components/tracker-detail/platform/MamVipCountdown.tsx b/src/components/tracker-detail/platform/MamVipCountdown.tsx new file mode 100644 index 00000000..58014c3a --- /dev/null +++ b/src/components/tracker-detail/platform/MamVipCountdown.tsx @@ -0,0 +1,42 @@ +// src/components/tracker-detail/platform/MamVipCountdown.tsx + +import { ProgressBar } from "@/components/ui/ProgressBar" + +export interface MamVipCountdownProps { + vipUntil: string + accentColor: string +} + +export function MamVipCountdown({ vipUntil, accentColor }: MamVipCountdownProps) { + const expiry = new Date(vipUntil) + if (Number.isNaN(expiry.getTime())) return null + + const msRemaining = expiry.getTime() - Date.now() + if (msRemaining <= 0) return null + + const daysRemaining = Math.ceil(msRemaining / (1000 * 60 * 60 * 24)) + // VIP duration varies by donation tier. 90 days is a reasonable default for the progress bar + const totalDays = 90 + const pct = (daysRemaining / totalDays) * 100 + + let urgencyColor = accentColor + if (daysRemaining <= 3) urgencyColor = "var(--color-danger)" + else if (daysRemaining <= 7) urgencyColor = "var(--color-warn)" + + const dateStr = expiry.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }) + + return ( +
+
+ VIP Expires + {daysRemaining}d remaining +
+ +

{dateStr}

+
+ ) +} diff --git a/src/components/tracker-detail/slot-registry.ts b/src/components/tracker-detail/slot-registry.ts index caf8ff1c..854e492d 100644 --- a/src/components/tracker-detail/slot-registry.ts +++ b/src/components/tracker-detail/slot-registry.ts @@ -4,10 +4,13 @@ // Slot IDs defined: // stat-card: login-deadline, gold, snatched-nebulance, seedbonus, ggn-share-score-card, // gazelle-tokens, perfect-flacs, snatched-gazelle, torrents-uploaded, -// requests-filled, groups-contributed, invited, gazelle-bounty, gazelle-comments +// requests-filled, groups-contributed, invited, gazelle-bounty, gazelle-comments, +// mam-wedges, mam-completed, mam-tracker-errors // badge: warned, donor, disabled, ggn-parked, ggn-invites, ggn-irc, -// gazelle-paranoia, gazelle-unread, gazelle-announcement -// progress: ggn-achievement-progress, ggn-share-score-progress, ggn-buffs +// gazelle-paranoia, gazelle-unread, gazelle-announcement, +// mam-vip, mam-connectable, mam-unread +// progress: ggn-achievement-progress, ggn-share-score-progress, ggn-buffs, +// mam-health-overview import type { ComponentType, ReactNode } from "react" import { createElement } from "react" @@ -26,7 +29,7 @@ import type { StatCardStackedProps, } from "@/components/ui/StatCard" import { StatCard } from "@/components/ui/StatCard" -import type { GazellePlatformMeta, GGnPlatformMeta } from "@/lib/adapters/types" +import type { GazellePlatformMeta, GGnPlatformMeta, MamPlatformMeta } from "@/lib/adapters/types" import { formatBytesNum } from "@/lib/formatters" import type { SlotCategory, SlotContext } from "@/lib/slot-types" import type { GgnAchievementProgressProps } from "./platform/GgnAchievementProgress" @@ -35,6 +38,8 @@ import type { GgnBuffsDisplayProps } from "./platform/GgnBuffsDisplay" import { GgnBuffsDisplay } from "./platform/GgnBuffsDisplay" import type { GgnShareScoreProgressProps } from "./platform/GgnShareScoreProgress" import { GgnShareScoreProgress } from "./platform/GgnShareScoreProgress" +import type { MamHealthOverviewProps } from "./platform/MamHealthOverview" +import { MamHealthOverview } from "./platform/MamHealthOverview" import type { SlotBadgeProps } from "./slots/SlotBadge" import { SlotBadge } from "./slots/SlotBadge" @@ -507,6 +512,7 @@ const gazelleUnreadBadgeSlot: SlotDefinition = { component: SlotBadge, priority: 31, resolve(ctx) { + if (ctx.tracker.hideUnreadBadges) return null const { meta } = ctx if (!meta || !("notifications" in meta)) return null const gazMeta = meta as GazellePlatformMeta @@ -522,6 +528,7 @@ const gazelleAnnouncementBadgeSlot: SlotDefinition = { component: SlotBadge, priority: 32, resolve(ctx) { + if (ctx.tracker.hideUnreadBadges) return null const { meta } = ctx if (!meta || !("notifications" in meta)) return null const gazMeta = meta as GazellePlatformMeta @@ -575,6 +582,140 @@ const ggnBuffsSlot: SlotDefinition = { }, } +// --------------------------------------------------------------------------- +// MAM slot definitions +// --------------------------------------------------------------------------- + +function isMamMeta(meta: unknown): meta is MamPlatformMeta { + return !!meta && typeof meta === "object" && "vipUntil" in meta +} + +function getDaysUntilVipExpiry(vipUntil: string): number | null { + const expiry = new Date(vipUntil) + if (Number.isNaN(expiry.getTime())) return null + const ms = expiry.getTime() - Date.now() + if (ms <= 0) return null + return Math.ceil(ms / (1000 * 60 * 60 * 24)) +} + +const mamWedgesSlot: SlotDefinition = { + id: "mam-wedges", + category: "stat-card", + component: StatCard as ComponentType, + priority: 14, + resolve(ctx) { + const { meta, latestSnapshot, accentColor } = ctx + if (!isMamMeta(meta)) return null + if (latestSnapshot?.freeleechTokens == null) return null + return { + label: "FL Wedges", + value: Math.floor(latestSnapshot.freeleechTokens).toLocaleString(), + accentColor, + icon: icon16(StarIcon), + } + }, +} + +const mamCompletedSlot: SlotDefinition = { + id: "mam-completed", + category: "stat-card", + component: StatCard as ComponentType, + priority: 22, + resolve(ctx) { + const { meta, accentColor } = ctx + if (!isMamMeta(meta)) return null + if (meta.inactiveSatisfiedCount == null) return null + return { + label: "Completed", + value: meta.inactiveSatisfiedCount, + accentColor, + icon: icon16(DownloadArrowIcon), + } + }, +} + +const mamTrackerErrorsSlot: SlotDefinition = { + id: "mam-tracker-errors", + category: "stat-card", + component: StatCard as ComponentType, + priority: 23, + resolve(ctx) { + const { meta, accentColor } = ctx + if (!isMamMeta(meta)) return null + if (meta.trackerErrorCount == null || meta.trackerErrorCount <= 0) return null + return { + label: "Tracker Errors", + value: meta.trackerErrorCount, + accentColor, + alert: "danger" as const, + } + }, +} + +const mamVipBadgeSlot: SlotDefinition = { + id: "mam-vip", + category: "badge", + component: SlotBadge, + priority: 10, + resolve(ctx) { + if (!isMamMeta(ctx.meta) || !ctx.meta.vipUntil) return null + const days = getDaysUntilVipExpiry(ctx.meta.vipUntil) + if (days == null) return null + return { variant: days <= 7 ? "warn" : "accent", label: `VIP (${days}d)` } + }, +} + +const mamConnectableBadgeSlot: SlotDefinition = { + id: "mam-connectable", + category: "badge", + component: SlotBadge, + priority: 20, + resolve(ctx) { + if (!isMamMeta(ctx.meta) || ctx.meta.connectable == null) return null + const lower = ctx.meta.connectable.toLowerCase() + const isOnline = lower === "yes" || lower === "true" || lower === "online" + return { + variant: isOnline ? "default" : "danger", + label: isOnline ? "Connectable" : "Not Connectable", + } + }, +} + +const mamUnreadBadgeSlot: SlotDefinition = { + id: "mam-unread", + category: "badge", + component: SlotBadge, + priority: 30, + resolve(ctx) { + if (ctx.tracker.hideUnreadBadges) return null + if (!isMamMeta(ctx.meta)) return null + const count = (ctx.meta.unreadPMs ?? 0) + (ctx.meta.unreadTopics ?? 0) + if (count <= 0) return null + return { variant: "warn", label: `${count} Unread` } + }, +} + +const mamHealthOverviewSlot: SlotDefinition = { + id: "mam-health-overview", + category: "progress", + component: MamHealthOverview as ComponentType, + priority: 10, + resolve(ctx) { + if (!isMamMeta(ctx.meta)) return null + return { + meta: ctx.meta, + seedingCount: ctx.latestSnapshot?.seedingCount ?? 0, + leechingCount: ctx.latestSnapshot?.leechingCount ?? 0, + hitAndRuns: ctx.latestSnapshot?.hitAndRuns ?? 0, + seedbonus: ctx.latestSnapshot?.seedbonus ?? null, + accentColor: ctx.accentColor, + vipUntil: ctx.meta.vipUntil ?? null, + unsatisfiedCount: ctx.meta.unsatisfiedCount ?? null, + unsatisfiedLimit: ctx.meta.unsatisfiedLimit ?? null, + } + }, +} + // --------------------------------------------------------------------------- // Exported registry // --------------------------------------------------------------------------- @@ -595,6 +736,10 @@ export const SLOT_DEFINITIONS: AnySlotDefinition[] = [ invitedSlot, gazelleBountySlot, gazelleCommentsSlot, + // MAM slots (stat-card) + mamWedgesSlot, + mamCompletedSlot, + mamTrackerErrorsSlot, // badge slots warnedBadgeSlot, donorBadgeSlot, @@ -605,10 +750,16 @@ export const SLOT_DEFINITIONS: AnySlotDefinition[] = [ gazelleParanoiaBadgeSlot, gazelleUnreadBadgeSlot, gazelleAnnouncementBadgeSlot, + // MAM slots (badge) + mamVipBadgeSlot, + mamConnectableBadgeSlot, + mamUnreadBadgeSlot, // progress slots ggnAchievementProgressSlot, ggnShareScoreProgressSlot, ggnBuffsSlot, + // MAM slots (progress) + mamHealthOverviewSlot, ] // Shared component lookup — single source for rendering resolved slots diff --git a/src/lib/__tests__/slot-resolve.test.tsx b/src/lib/__tests__/slot-resolve.test.tsx index 8b85d1ae..8483abbe 100644 --- a/src/lib/__tests__/slot-resolve.test.tsx +++ b/src/lib/__tests__/slot-resolve.test.tsx @@ -28,6 +28,8 @@ function makeTracker(overrides: Partial = {}): TrackerSummary { userPausedAt: null, color: "#00d4ff", qbtTag: null, + mouseholeUrl: null, + hideUnreadBadges: false, useProxy: false, countCrossSeedUnsatisfied: false, isFavorite: false, diff --git a/src/lib/slot-types.ts b/src/lib/slot-types.ts index 86d70813..5bb0db56 100644 --- a/src/lib/slot-types.ts +++ b/src/lib/slot-types.ts @@ -6,6 +6,7 @@ import type { TrackerRegistryEntry } from "@/data/tracker-registry" import type { GazellePlatformMeta, GGnPlatformMeta, + MamPlatformMeta, NebulancePlatformMeta, } from "@/lib/adapters/types" import type { Snapshot, TrackerSummary } from "@/types/api" @@ -16,7 +17,7 @@ export interface SlotContext { tracker: TrackerSummary latestSnapshot: Snapshot | null snapshots: Snapshot[] - meta: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | null + meta: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | MamPlatformMeta | null registry: TrackerRegistryEntry | undefined accentColor: string } From c3d90315d4ca8717f983a3d270d922cc0355de18 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:40:36 -0500 Subject: [PATCH 51/59] refactor: reuse ProgressBar component and extract slot-label utility --- .../trackers/[id]/TrackerDetailClient.tsx | 7 ++++--- src/app/globals.css | 9 +++++++++ .../platform/GgnAchievementProgress.tsx | 15 +++------------ .../platform/GgnShareScoreProgress.tsx | 19 ++++--------------- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx index 07667437..926a874d 100644 --- a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx +++ b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx @@ -207,9 +207,10 @@ export function TrackerDetailClient({ const delta = useMemo(() => computeDelta(snapshots), [snapshots]) const tc = tracker?.color || CHART_THEME.accent - const registryEntry: TrackerRegistryEntry | undefined = tracker - ? findRegistryEntry(tracker.baseUrl) - : undefined + const registryEntry = useMemo( + () => (tracker ? findRegistryEntry(tracker.baseUrl) : undefined), + [tracker?.baseUrl] + ) const { statCardSlots, badgeSlots, progressSlots } = useMemo(() => { if (!tracker) return { statCardSlots: [], badgeSlots: [], progressSlots: [] } diff --git a/src/app/globals.css b/src/app/globals.css index bf6473cf..3cff1f8b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -174,6 +174,15 @@ /* #14151b */ inset -2px -2px 5px oklch(32.94% 0.0266 278.88); /* #323443 */ } +@utility slot-label { + font-family: var(--font-sans); + font-size: 10px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--color-text-tertiary); +} + /* * Neumorphic interactive states — for clickable raised surfaces. * Three tiers matching elevation: sm (cards), base (panels), inset (dormant links). diff --git a/src/components/tracker-detail/platform/GgnAchievementProgress.tsx b/src/components/tracker-detail/platform/GgnAchievementProgress.tsx index 7c3715f4..44357fe0 100644 --- a/src/components/tracker-detail/platform/GgnAchievementProgress.tsx +++ b/src/components/tracker-detail/platform/GgnAchievementProgress.tsx @@ -1,6 +1,6 @@ // src/components/tracker-detail/platform/GgnAchievementProgress.tsx -import { hexToRgba } from "@/lib/formatters" +import { ProgressBar } from "@/components/ui/ProgressBar" import type { GGnPlatformMeta } from "@/types/api" export interface GgnAchievementProgressProps { @@ -13,7 +13,7 @@ export function GgnAchievementProgress({ ggMeta, accentColor }: GgnAchievementPr const { userLevel, nextLevel, totalPoints, pointsToNextLvl } = ggMeta.achievements const earned = totalPoints - pointsToNextLvl - const pct = totalPoints > 0 ? Math.min(100, Math.max(0, (earned / totalPoints) * 100)) : 0 + const pct = totalPoints > 0 ? (earned / totalPoints) * 100 : 0 return (
@@ -24,16 +24,7 @@ export function GgnAchievementProgress({ ggMeta, accentColor }: GgnAchievementPr {nextLevel}
-
-
-
+

{pointsToNextLvl.toLocaleString()} pts to {nextLevel}

diff --git a/src/components/tracker-detail/platform/GgnShareScoreProgress.tsx b/src/components/tracker-detail/platform/GgnShareScoreProgress.tsx index f6dea2da..561e8a6a 100644 --- a/src/components/tracker-detail/platform/GgnShareScoreProgress.tsx +++ b/src/components/tracker-detail/platform/GgnShareScoreProgress.tsx @@ -1,6 +1,6 @@ // src/components/tracker-detail/platform/GgnShareScoreProgress.tsx -import { hexToRgba } from "@/lib/formatters" +import { ProgressBar } from "@/components/ui/ProgressBar" import type { Snapshot } from "@/types/api" export interface GgnShareScoreProgressProps { @@ -13,28 +13,17 @@ export function GgnShareScoreProgress({ latestSnapshot, accentColor }: GgnShareS const score = latestSnapshot.shareScore const maxScore = 15 - const pct = Math.min(100, Math.max(0, (score / maxScore) * 100)) + const pct = (score / maxScore) * 100 return (
- - Share Score - + Share Score {score.toFixed(2)} / {maxScore}
-
-
-
+
) } From 0e28443875c1de70b1dea0f3662ae9625000da0f Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:52:32 -0500 Subject: [PATCH 52/59] feat(mam): add Mousehole integration --- public/tracker-logos/mousehole_logo.svg | 3 + src/app/api/trackers/[id]/mousehole/route.ts | 167 +++++++++ src/app/api/trackers/[id]/route.ts | 14 + src/app/api/trackers/route.ts | 30 +- src/components/AddTrackerDialog.tsx | 25 ++ src/components/TrackerHubStatus.tsx | 2 +- src/components/TrackerSettingsDialog.tsx | 37 +- .../tracker-detail/TrackerDetailHeader.tsx | 21 +- .../platform/MamMouseholeCard.tsx | 328 ++++++++++++++++++ src/hooks/__tests__/useDashboardData.test.tsx | 2 + src/lib/__tests__/dashboard.test.ts | 2 + src/lib/__tests__/server-data.test.ts | 3 + src/lib/__tests__/tracker-serializer.test.ts | 2 + src/lib/db/schema.ts | 2 + src/lib/server-data.ts | 2 + src/lib/tracker-serializer.ts | 2 + src/types/api.ts | 6 +- 17 files changed, 629 insertions(+), 19 deletions(-) create mode 100644 public/tracker-logos/mousehole_logo.svg create mode 100644 src/app/api/trackers/[id]/mousehole/route.ts create mode 100644 src/components/tracker-detail/platform/MamMouseholeCard.tsx diff --git a/public/tracker-logos/mousehole_logo.svg b/public/tracker-logos/mousehole_logo.svg new file mode 100644 index 00000000..e7d4b6d7 --- /dev/null +++ b/public/tracker-logos/mousehole_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/api/trackers/[id]/mousehole/route.ts b/src/app/api/trackers/[id]/mousehole/route.ts new file mode 100644 index 00000000..81f2decb --- /dev/null +++ b/src/app/api/trackers/[id]/mousehole/route.ts @@ -0,0 +1,167 @@ +// src/app/api/trackers/[id]/mousehole/route.ts +// +// Functions: GET, POST + +import { eq } from "drizzle-orm" +import { NextResponse } from "next/server" +import { authenticate, parseTrackerId } from "@/lib/api-helpers" +import { db } from "@/lib/db" +import { trackers } from "@/lib/db/schema" +import { log } from "@/lib/logger" + +const GET_TIMEOUT_MS = 10_000 +const POST_TIMEOUT_MS = 15_000 + +type RouteContext = { params: Promise<{ id: string }> } + +// --------------------------------------------------------------------------- +// Shared guards +// --------------------------------------------------------------------------- + +async function resolveMouseholeBase( + params: Promise<{ id: string }> +): Promise { + const trackerId = await parseTrackerId(params) + if (trackerId instanceof NextResponse) return trackerId + + const [tracker] = await db + .select({ platformType: trackers.platformType, mouseholeUrl: trackers.mouseholeUrl }) + .from(trackers) + .where(eq(trackers.id, trackerId)) + .limit(1) + + if (!tracker) { + return NextResponse.json({ error: "Tracker not found" }, { status: 404 }) + } + + if (tracker.platformType !== "mam") { + return NextResponse.json( + { error: "Mousehole is only available for MAM trackers" }, + { status: 400 } + ) + } + + if (!tracker.mouseholeUrl) { + return NextResponse.json({ error: "Mousehole URL not configured" }, { status: 400 }) + } + + const mouseholeBase = tracker.mouseholeUrl.replace(/\/+$/, "") + return { mouseholeBase } +} + +// --------------------------------------------------------------------------- +// GET /api/trackers/[id]/mousehole +// Combines GET /ok + GET /state from the Mousehole instance +// --------------------------------------------------------------------------- + +export async function GET(_request: Request, { params }: RouteContext) { + const auth = await authenticate() + if (auth instanceof NextResponse) return auth + + const resolved = await resolveMouseholeBase(params) + if (resolved instanceof NextResponse) return resolved + + const { mouseholeBase } = resolved + + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(), GET_TIMEOUT_MS) + + try { + const [okRes, stateRes] = await Promise.all([ + fetch(`${mouseholeBase}/ok`, { signal: controller.signal }), + fetch(`${mouseholeBase}/state`, { signal: controller.signal }), + ]) + + clearTimeout(timer) + + const [okJson, stateJson] = await Promise.all([ + okRes.json() as Promise<{ ok: boolean; reason: string }>, + stateRes.json() as Promise<{ + host?: { ip?: string | null; asn?: number | null; as?: string | null } + nextUpdateAt?: string | null + lastUpdate?: { at?: string | null; mamUpdated?: boolean | null } + lastMam?: { response?: { body?: { msg?: string | null } } } + }>, + ]) + + return NextResponse.json({ + ok: okJson.ok ?? false, + reason: okJson.reason ?? null, + ip: stateJson.host?.ip ?? null, + asn: stateJson.host?.asn ?? null, + asOrg: stateJson.host?.as ?? null, + nextUpdateAt: stateJson.nextUpdateAt ?? null, + lastUpdateAt: stateJson.lastUpdate?.at ?? null, + lastUpdateResult: stateJson.lastMam?.response?.body?.msg ?? null, + mamUpdated: stateJson.lastUpdate?.mamUpdated ?? null, + }) + } catch (error) { + clearTimeout(timer) + + if (error instanceof Error && error.name === "AbortError") { + log.warn({ route: "GET /api/trackers/[id]/mousehole" }, "Mousehole request timed out") + return NextResponse.json({ error: "Mousehole request timed out" }, { status: 504 }) + } + + log.warn( + { route: "GET /api/trackers/[id]/mousehole", error: String(error) }, + "Mousehole unreachable" + ) + return NextResponse.json({ error: "Mousehole unreachable" }, { status: 502 }) + } +} + +// --------------------------------------------------------------------------- +// POST /api/trackers/[id]/mousehole +// Proxies POST /update to the Mousehole instance +// --------------------------------------------------------------------------- + +export async function POST(request: Request, { params }: RouteContext) { + const auth = await authenticate() + if (auth instanceof NextResponse) return auth + + const resolved = await resolveMouseholeBase(params) + if (resolved instanceof NextResponse) return resolved + + const { mouseholeBase } = resolved + + let body: { force?: boolean } = {} + try { + const raw = await request.json() + if (raw && typeof raw === "object" && "force" in raw) { + body = { force: Boolean(raw.force) } + } + } catch (_err) { + log.debug({ route: "POST /api/trackers/[id]/mousehole" }, "no request body (optional)") + } + + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(), POST_TIMEOUT_MS) + + try { + const res = await fetch(`${mouseholeBase}/update`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + signal: controller.signal, + }) + + clearTimeout(timer) + + const data: unknown = await res.json() + return NextResponse.json(data, { status: res.status }) + } catch (error) { + clearTimeout(timer) + + if (error instanceof Error && error.name === "AbortError") { + log.warn({ route: "POST /api/trackers/[id]/mousehole" }, "Mousehole request timed out") + return NextResponse.json({ error: "Mousehole request timed out" }, { status: 504 }) + } + + log.warn( + { route: "POST /api/trackers/[id]/mousehole", error: String(error) }, + "Mousehole unreachable" + ) + return NextResponse.json({ error: "Mousehole unreachable" }, { status: 502 }) + } +} diff --git a/src/app/api/trackers/[id]/route.ts b/src/app/api/trackers/[id]/route.ts index 5068c3b1..5d448306 100644 --- a/src/app/api/trackers/[id]/route.ts +++ b/src/app/api/trackers/[id]/route.ts @@ -75,9 +75,23 @@ export async function PATCH(request: Request, props: { params: Promise<{ id: str updates.qbtTag = body.qbtTag.trim() || null } + if (typeof body.mouseholeUrl === "string") { + const trimmed = body.mouseholeUrl.trim() + if (trimmed) { + try { + new URL(trimmed) + } catch { + return NextResponse.json({ error: "Invalid Mousehole URL format" }, { status: 400 }) + } + } + updates.mouseholeUrl = trimmed || null + } + if (typeof body.useProxy === "boolean") updates.useProxy = body.useProxy if (typeof body.countCrossSeedUnsatisfied === "boolean") updates.countCrossSeedUnsatisfied = body.countCrossSeedUnsatisfied + if (typeof body.hideUnreadBadges === "boolean") + updates.hideUnreadBadges = body.hideUnreadBadges if (typeof body.isFavorite === "boolean") updates.isFavorite = body.isFavorite if (typeof body.pollingPaused === "boolean") { updates.userPausedAt = body.pollingPaused ? new Date() : null diff --git a/src/app/api/trackers/route.ts b/src/app/api/trackers/route.ts index a377447e..51257764 100644 --- a/src/app/api/trackers/route.ts +++ b/src/app/api/trackers/route.ts @@ -34,15 +34,17 @@ export async function POST(request: Request) { const body = await parseJsonBody(request) if (body instanceof NextResponse) return body - const { name, baseUrl, apiToken, platformType, color, qbtTag, joinedAt } = body as { - name?: string - baseUrl?: string - apiToken?: string - platformType?: string - color?: string - qbtTag?: string - joinedAt?: string - } + const { name, baseUrl, apiToken, platformType, color, qbtTag, mouseholeUrl, joinedAt } = + body as { + name?: string + baseUrl?: string + apiToken?: string + platformType?: string + color?: string + qbtTag?: string + mouseholeUrl?: string + joinedAt?: string + } if ( typeof name !== "string" || @@ -92,6 +94,14 @@ export async function POST(request: Request) { ) } + if (typeof mouseholeUrl === "string" && mouseholeUrl.trim()) { + try { + new URL(mouseholeUrl.trim()) + } catch { + return NextResponse.json({ error: "Invalid Mousehole URL format" }, { status: 400 }) + } + } + if (typeof joinedAt === "string" && joinedAt) { const joinedAtErr = validateJoinedAt(joinedAt) if (joinedAtErr) return joinedAtErr @@ -115,6 +125,8 @@ export async function POST(request: Request) { platformType: platform, color: (color as string) || CHART_THEME.accent, qbtTag: typeof qbtTag === "string" ? qbtTag.trim() : null, + mouseholeUrl: + typeof mouseholeUrl === "string" && mouseholeUrl.trim() ? mouseholeUrl.trim() : null, joinedAt: typeof joinedAt === "string" && joinedAt ? joinedAt : null, }) .returning() diff --git a/src/components/AddTrackerDialog.tsx b/src/components/AddTrackerDialog.tsx index ad1f9ba2..9f0dc4c0 100644 --- a/src/components/AddTrackerDialog.tsx +++ b/src/components/AddTrackerDialog.tsx @@ -247,6 +247,7 @@ function AddTrackerDialog({ const [baseUrl, setBaseUrl] = useState("") const [apiToken, setApiToken] = useState("") const [qbtTag, setQbtTag] = useState("") + const [mouseholeUrl, setMouseholeUrl] = useState("") const [color, setColor] = useState(CHART_THEME.accent) const [joinedAt, setJoinedAt] = useState("") const [errors, setErrors] = useState>({}) @@ -259,6 +260,7 @@ function AddTrackerDialog({ setBaseUrl("") setApiToken("") setQbtTag("") + setMouseholeUrl("") setColor(CHART_THEME.accent) setJoinedAt("") setErrors({}) @@ -394,6 +396,7 @@ function AddTrackerDialog({ color, qbtTag: qbtTag.trim() || undefined, joinedAt: joinedAt || undefined, + mouseholeUrl: mouseholeUrl.trim() || undefined, }), }) @@ -522,6 +525,28 @@ function AddTrackerDialog({
+ {selectedEntry?.platform === "mam" && ( +
+
+ setMouseholeUrl(e.target.value)} + placeholder="http://localhost:7001" + /> + + + +
+
+ )} + {!(selectedEntry?.gazelleEnrich || selectedEntry?.platform === "ggn") && ( diff --git a/src/components/TrackerHubStatus.tsx b/src/components/TrackerHubStatus.tsx index ef513da0..2da7cec1 100644 --- a/src/components/TrackerHubStatus.tsx +++ b/src/components/TrackerHubStatus.tsx @@ -109,7 +109,7 @@ function TrackerHubStatus({ const trackerHubUrl = `${TRACKERHUB_HISTORY}/${trackerHubSlug}` return ( -
+
{/* Header row — always visible */}
+ {tracker.platformType === "mam" && ( +
+ updateField("mouseholeUrl", e.target.value)} + placeholder="http://localhost:7001" + /> + + + +
+ )} + updateField("color", v)} /> {!(registryEntry?.gazelleEnrich || tracker.platformType === "ggn") && ( @@ -333,6 +357,15 @@ function TrackerSettingsDialog({ open, tracker, onClose, onUpdated }: TrackerSet description="Include cross-seeded torrents when calculating unsatisfied download requirements." /> + {(tracker.platformType === "mam" || tracker.platformType === "gazelle") && ( + updateField("hideUnreadBadges", v)} + label="Hide unread badges" + description="Don't show inbox/notification counts on this tracker's detail page" + /> + )} + {errors.form && (

{errors.form} diff --git a/src/components/tracker-detail/TrackerDetailHeader.tsx b/src/components/tracker-detail/TrackerDetailHeader.tsx index 8b8c79fa..93fda677 100644 --- a/src/components/tracker-detail/TrackerDetailHeader.tsx +++ b/src/components/tracker-detail/TrackerDetailHeader.tsx @@ -6,6 +6,7 @@ import { H1 } from "@typography" import clsx from "clsx" import Image from "next/image" import { TrackerHubStatus } from "@/components/TrackerHubStatus" +import { MamMouseholeCard } from "@/components/tracker-detail/platform/MamMouseholeCard" import { SlotRenderer } from "@/components/tracker-detail/SlotRenderer" import { UserProfileCard } from "@/components/tracker-detail/UserProfileCard" import { Badge } from "@/components/ui/Badge" @@ -148,12 +149,20 @@ export function TrackerDetailHeader({

- {registryEntry?.trackerHubSlug && ( -
- + {(registryEntry?.trackerHubSlug || (tracker.platformType === "mam" && tracker.mouseholeUrl)) && ( +
+ {registryEntry?.trackerHubSlug && ( + + )} + {tracker.platformType === "mam" && tracker.mouseholeUrl && ( + + )}
)} diff --git a/src/components/tracker-detail/platform/MamMouseholeCard.tsx b/src/components/tracker-detail/platform/MamMouseholeCard.tsx new file mode 100644 index 00000000..f5103380 --- /dev/null +++ b/src/components/tracker-detail/platform/MamMouseholeCard.tsx @@ -0,0 +1,328 @@ +// src/components/tracker-detail/platform/MamMouseholeCard.tsx +"use client" + +import clsx from "clsx" +import Image from "next/image" +import { useCallback, useEffect, useRef, useState } from "react" +import { Button } from "@/components/ui/Button" +import { ChevronToggle } from "@/components/ui/ChevronToggle" +import { Tooltip } from "@/components/ui/Tooltip" +import { useLocalStorage } from "@/hooks/useLocalStorage" + +interface MouseholeResponse { + ok: boolean + reason: string + ip: string | null + asn: number | null + asOrg: string | null + nextUpdateAt: string | null + lastUpdateAt: string | null + lastUpdateResult: string | null + mamUpdated: boolean | null +} + +export interface MamMouseholeCardProps { + trackerId: number + mouseholeUrl: string +} + +function parseRfc9557(dateStr: string): Date { + return new Date(dateStr.replace(/\[.*\]$/, "")) +} + +function formatCountdown(ms: number): string { + if (ms <= 0) return "00:00:00" + const totalSeconds = Math.floor(ms / 1000) + const hours = Math.floor(totalSeconds / 3600) + const minutes = Math.floor((totalSeconds % 3600) / 60) + const seconds = totalSeconds % 60 + return [hours, minutes, seconds].map((v) => String(v).padStart(2, "0")).join(":") +} + +function formatResult(msg: string): string { + switch (msg) { + case "Completed": + return "IP updated" + case "No change": + case "No Change": + return "No change needed" + default: + return msg + } +} + +export function MamMouseholeCard({ trackerId, mouseholeUrl }: MamMouseholeCardProps) { + const [data, setData] = useState(null) + const [fetchError, setFetchError] = useState(null) + const [loading, setLoading] = useState(true) + const [checking, setChecking] = useState(false) + const [countdown, setCountdown] = useState("--:--:--") + const [expanded, setExpanded] = useLocalStorage("tracker-tracker:mousehole-expanded", true) + + const nextUpdateAtRef = useRef(null) + const mountedRef = useRef(true) + const fetchStateRef = useRef<() => void>(() => {}) + + const fetchState = useCallback( + async (signal?: AbortSignal) => { + try { + const res = await fetch(`/api/trackers/${trackerId}/mousehole`, { signal }) + if (!mountedRef.current) return + if (!res.ok) { + const body = await res.json().catch(() => ({})) + setFetchError(body?.error ?? `HTTP ${res.status}`) + return + } + const json: MouseholeResponse = await res.json() + setData(json) + setFetchError(null) + nextUpdateAtRef.current = json.nextUpdateAt + } catch (err) { + if (err instanceof Error && err.name === "AbortError") return + if (!mountedRef.current) return + setFetchError(err instanceof Error ? err.message : "Failed to reach Mousehole") + } finally { + if (mountedRef.current) setLoading(false) + } + }, + [trackerId] + ) + + // Keep ref in sync so the countdown effect can call it without a dep change + fetchStateRef.current = () => { + fetchState() + } + + // Polling + useEffect(() => { + mountedRef.current = true + const controller = new AbortController() + fetchState(controller.signal) + const id = setInterval(() => fetchState(controller.signal), 60_000) + return () => { + mountedRef.current = false + controller.abort() + clearInterval(id) + } + }, [fetchState]) + + // Countdown + useEffect(() => { + let prev = "" + let refetched = false + function tick() { + const raw = nextUpdateAtRef.current + if (!raw) { + if (prev !== "--:--:--") { + setCountdown("--:--:--") + prev = "--:--:--" + } + return + } + const ms = parseRfc9557(raw).getTime() - Date.now() + const next = formatCountdown(ms) + if (next !== prev) { + setCountdown(next) + prev = next + } + if (ms <= 0 && !refetched) { + refetched = true + fetchStateRef.current() + } + if (ms > 0) { + refetched = false + } + } + tick() + const id = setInterval(tick, 1_000) + return () => clearInterval(id) + }, []) + + const handleCheckNow = useCallback(async () => { + setChecking(true) + try { + const res = await fetch(`/api/trackers/${trackerId}/mousehole`, { method: "POST" }) + if (!mountedRef.current) return + if (!res.ok) { + const body = await res.json().catch(() => ({})) + setFetchError(body?.error ?? `HTTP ${res.status}`) + return + } + await fetchState() + } catch (err) { + if (!mountedRef.current) return + setFetchError(err instanceof Error ? err.message : "Check failed") + } finally { + if (mountedRef.current) setChecking(false) + } + }, [trackerId, fetchState]) + + if (loading) { + return ( +
+
+ + + Connecting to Mousehole... + +
+
+ ) + } + + if (fetchError && !data) { + return ( +
+ +
+ Mousehole + {fetchError} +
+
+ ) + } + + if (!data) return null + + const isOk = data.ok + const isStale = !data.ok && data.reason === "needs_update" + const countdownExpired = countdown === "00:00:00" + + return ( +
+ {/* Left: Logo — clickable, links to user's Mousehole instance */} + + + + + {/* Right: Content — separated from logo by gap, not border */} +
+ {/* Header — always visible */} +
+ + +
+ {/* Countdown */} + + + {countdown} + + + + + ⓘ + + +
+
+ + {/* Expandable details */} +
+
+ {/* IP + ASN */} +
+ {data.ip ?? "No IP"} + {data.asn != null && ( + + AS{data.asn} + {data.asOrg ? `, ${data.asOrg}` : ""} + + )} +
+ + {/* Last result */} + {data.lastUpdateResult && ( +
+ Last check: {formatResult(data.lastUpdateResult)} +
+ )} + + {/* Action row */} +
+ + Next check{" "} + + {countdown} + + + +
+
+
+
+
+ ) +} diff --git a/src/hooks/__tests__/useDashboardData.test.tsx b/src/hooks/__tests__/useDashboardData.test.tsx index 03f166d3..a9a24928 100644 --- a/src/hooks/__tests__/useDashboardData.test.tsx +++ b/src/hooks/__tests__/useDashboardData.test.tsx @@ -47,6 +47,8 @@ const mockTracker: TrackerSummary = { userPausedAt: null, color: "#00d4ff", qbtTag: null, + mouseholeUrl: null, + hideUnreadBadges: false, useProxy: false, countCrossSeedUnsatisfied: false, isFavorite: false, diff --git a/src/lib/__tests__/dashboard.test.ts b/src/lib/__tests__/dashboard.test.ts index cab3e78e..61eb7ce9 100644 --- a/src/lib/__tests__/dashboard.test.ts +++ b/src/lib/__tests__/dashboard.test.ts @@ -42,6 +42,8 @@ function makeTracker(overrides: Partial = {}): TrackerSummary { userPausedAt: null, color: "#00d4ff", qbtTag: null, + mouseholeUrl: null, + hideUnreadBadges: false, sortOrder: 0, joinedAt: null, lastAccessAt: null, diff --git a/src/lib/__tests__/server-data.test.ts b/src/lib/__tests__/server-data.test.ts index 640513fa..2d22faa0 100644 --- a/src/lib/__tests__/server-data.test.ts +++ b/src/lib/__tests__/server-data.test.ts @@ -306,6 +306,8 @@ describe("serializeTrackerResponse excludes encryptedApiToken", () => { userPausedAt: null, color: "#00d4ff", qbtTag: null, + mouseholeUrl: null, + hideUnreadBadges: false, remoteUserId: null, platformMeta: null, avatarData: null, @@ -361,6 +363,7 @@ describe("trackerColumns security invariant", () => { "pausedAt", "color", "qbtTag", + "mouseholeUrl", "useProxy", "countCrossSeedUnsatisfied", "isFavorite", diff --git a/src/lib/__tests__/tracker-serializer.test.ts b/src/lib/__tests__/tracker-serializer.test.ts index a074052b..5bb6191b 100644 --- a/src/lib/__tests__/tracker-serializer.test.ts +++ b/src/lib/__tests__/tracker-serializer.test.ts @@ -17,6 +17,8 @@ const mockTracker = { userPausedAt: null, color: "#00d4ff", qbtTag: null, + mouseholeUrl: null, + hideUnreadBadges: false, remoteUserId: null, platformMeta: null, avatarData: null, diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index a4169c4a..1746fa4c 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -80,11 +80,13 @@ export const trackers = pgTable("trackers", { qbtTag: varchar("qbt_tag", { length: 100 }), remoteUserId: integer("remote_user_id"), platformMeta: text("platform_meta"), + mouseholeUrl: text("mousehole_url"), avatarData: text("avatar_data"), avatarCachedAt: timestamp("avatar_cached_at"), avatarRemoteUrl: text("avatar_remote_url"), useProxy: boolean("use_proxy").default(false).notNull(), countCrossSeedUnsatisfied: boolean("count_cross_seed_unsatisfied").default(false).notNull(), + hideUnreadBadges: boolean("hide_unread_badges").default(false).notNull(), isFavorite: boolean("is_favorite").default(false).notNull(), sortOrder: integer("sort_order"), joinedAt: date("joined_at"), diff --git a/src/lib/server-data.ts b/src/lib/server-data.ts index 755d2554..59c8eeb2 100644 --- a/src/lib/server-data.ts +++ b/src/lib/server-data.ts @@ -149,10 +149,12 @@ export const trackerColumns = { userPausedAt: trackers.userPausedAt, color: trackers.color, qbtTag: trackers.qbtTag, + mouseholeUrl: trackers.mouseholeUrl, remoteUserId: trackers.remoteUserId, platformMeta: trackers.platformMeta, useProxy: trackers.useProxy, countCrossSeedUnsatisfied: trackers.countCrossSeedUnsatisfied, + hideUnreadBadges: trackers.hideUnreadBadges, isFavorite: trackers.isFavorite, sortOrder: trackers.sortOrder, joinedAt: trackers.joinedAt, diff --git a/src/lib/tracker-serializer.ts b/src/lib/tracker-serializer.ts index 456bc164..3664403f 100644 --- a/src/lib/tracker-serializer.ts +++ b/src/lib/tracker-serializer.ts @@ -39,8 +39,10 @@ export function serializeTrackerResponse( userPausedAt: tracker.userPausedAt?.toISOString() ?? null, color: tracker.color ?? "#00d4ff", qbtTag: tracker.qbtTag, + mouseholeUrl: tracker.mouseholeUrl ?? null, useProxy: tracker.useProxy, countCrossSeedUnsatisfied: tracker.countCrossSeedUnsatisfied, + hideUnreadBadges: tracker.hideUnreadBadges, isFavorite: tracker.isFavorite, sortOrder: tracker.sortOrder, joinedAt: tracker.joinedAt, diff --git a/src/types/api.ts b/src/types/api.ts index ced0615e..1f3dafa5 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -3,6 +3,7 @@ import type { GazellePlatformMeta, GGnPlatformMeta, + MamPlatformMeta, NebulancePlatformMeta, } from "@/lib/adapters/types" @@ -10,6 +11,7 @@ export type { GazellePlatformMeta, GazelleRanks, GGnPlatformMeta, + MamPlatformMeta, NebulancePlatformMeta, } from "@/lib/adapters/types" @@ -43,14 +45,16 @@ export interface TrackerSummary { userPausedAt: string | null color: string qbtTag: string | null + mouseholeUrl: string | null useProxy: boolean countCrossSeedUnsatisfied: boolean + hideUnreadBadges: boolean isFavorite: boolean sortOrder: number | null joinedAt: string | null lastAccessAt: string | null remoteUserId: number | null - platformMeta: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | null + platformMeta: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | MamPlatformMeta | null createdAt: string latestStats: TrackerLatestStats | null } From 7faa708ca6eaf2dcca3a32cd0fbe133e47e2f413 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 16:53:30 -0500 Subject: [PATCH 53/59] docs(mam): add API response reference, platform differences, and setup guide --- .prettierignore | 1 + docs/adding-trackers.md | 221 ----------- docs/api-comparison-unit3d-gazelle.md | 243 ------------ docs/kb/docs/contributing/adding-a-tracker.md | 17 +- .../contributing/tracker-responses-mam.md | 361 ++++++++++++++++++ .../kb/docs/contributing/tracker-responses.md | 3 +- .../kb/docs/reference/platform-differences.md | 57 ++- docs/kb/mkdocs.yml | 1 + 8 files changed, 411 insertions(+), 493 deletions(-) create mode 100644 .prettierignore delete mode 100644 docs/adding-trackers.md delete mode 100644 docs/api-comparison-unit3d-gazelle.md create mode 100644 docs/kb/docs/contributing/tracker-responses-mam.md diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..e4741063 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +# docs/**/*.md diff --git a/docs/adding-trackers.md b/docs/adding-trackers.md deleted file mode 100644 index 143ab43b..00000000 --- a/docs/adding-trackers.md +++ /dev/null @@ -1,221 +0,0 @@ -# Adding a Tracker - -There are two levels of contribution: adding a **registry entry** (metadata only) or writing a **platform adapter** (code that talks to the tracker's API). - -Most trackers run on UNIT3D or Gazelle, which already have adapters. For those, you only need a registry entry. - ---- - -## Registry Entry (no code required) - -If the tracker runs on UNIT3D, Gazelle, or GGn, the existing adapter handles the API calls. You just need to describe the tracker. - -### 1. Create the tracker file - -Create `src/data/trackers/.ts`: - -```ts -// src/data/trackers/.ts - -import type { TrackerRegistryEntry } from "@/data/tracker-registry" - -export const mytracker: TrackerRegistryEntry = { - slug: "mytracker", - name: "MyTracker", - abbreviation: "MT", - url: "https://mytracker.org", - description: "Brief description of what the tracker is known for.", - platform: "unit3d", // "unit3d" | "gazelle" | "ggn" | "custom" - apiPath: "/api/user", // UNIT3D default; Gazelle uses "/ajax.php" - specialty: "General / HD", - contentCategories: ["Movies", "TV"], - userClasses: [ - { name: "User", requirements: "Default class" }, - { name: "Power User", requirements: "Upload >= 50 GiB, ratio >= 1.0, age >= 1 month" }, - ], - releaseGroups: ["GroupA", "GroupB"], - notableMembers: [], - rules: { - minimumRatio: 0.6, // 0 = no minimum - seedTimeHours: 72, // 0 = no minimum - loginIntervalDays: 90, // days before account prune/disable - }, - language: "English", - color: "#00d4ff", // hex color used as the tracker's accent throughout the UI -} -``` - -### 2. Register it - -Add your import and entry to `src/data/trackers/index.ts`: - -```ts -import { mytracker } from "./mytracker" - -export const ALL_TRACKERS: TrackerRegistryEntry[] = [ - // ... existing trackers (alphabetical) - mytracker, - // ... -] -``` - -That's it. The tracker will appear in the Add Tracker dialog and use the existing platform adapter. - -### Field reference - -| Field | Required | Description | -| ------------------- | -------- | ----------------------------------------------------------------------------- | -| `slug` | Yes | Unique lowercase identifier, used in URLs | -| `name` | Yes | Display name | -| `abbreviation` | No | Short form (i.e "RED", "OPS") | -| `url` | Yes | Tracker homepage URL | -| `description` | Yes | What the tracker is known for | -| `platform` | Yes | `"unit3d"`, `"gazelle"`, `"ggn"`, or `"custom"` | -| `apiPath` | Yes | API endpoint path. UNIT3D: `/api/user`, Gazelle: `/ajax.php`, GGn: `/api.php` | -| `specialty` | Yes | Content focus (i.e "Anime", "Music", "General / HD") | -| `contentCategories` | Yes | Array of content types | -| `userClasses` | Yes | Array of `{ name, requirements? }` — the tracker's user class ladder | -| `releaseGroups` | Yes | Array of group names or `{ name, description }` objects | -| `notableMembers` | Yes | Array of notable community members (can be empty) | -| `bannedGroups` | No | Groups banned from uploading | -| `rules` | No | See TrackerRules below | -| `stats` | No | `{ userCount?, torrentCount?, seedSize?, statsUpdatedAt? }` | -| `language` | No | Primary language | -| `color` | Yes | Hex color for the tracker's accent theme | -| `logo` | No | Path to logo file in `public/tracker-logos/` | -| `trackerHubSlug` | No | Slug on TrackerHub for status monitoring | -| `statusPageUrl` | No | External status page URL | -| `draft` | No | Set `true` if the platform adapter doesn't exist yet | - -### TrackerRules - -| Field | Type | Description | -| ------------------------ | --------- | ------------------------------------------------------- | -| `minimumRatio` | `number` | Minimum ratio before penalties. 0 = no minimum | -| `seedTimeHours` | `number` | Required seed time per torrent in hours. 0 = no minimum | -| `loginIntervalDays` | `number` | Days of inactivity before account prune/disable | -| `fulfillmentPeriodHours` | `number?` | Time allowed to complete seeding requirement | -| `hnrBanLimit` | `number?` | Number of H&R warnings before ban | -| `fullRulesMarkdown` | `string?` | Detailed rules text (shown in tracker detail page) | - ---- - -## Platform Adapter (code required) - -If the tracker doesn't run on UNIT3D, Gazelle, or GGn, you need to write an adapter that knows how to call its API and normalize the response. - -### 1. Create the adapter - -Create `src/lib/adapters/.ts`: - -```ts -// src/lib/adapters/.ts - -import type { FetchOptions, TrackerAdapter, TrackerStats } from "./types" - -export class MyPlatformAdapter implements TrackerAdapter { - async fetchStats( - baseUrl: string, - apiToken: string, - apiPath: string, - options?: FetchOptions - ): Promise { - // 1. Build the request URL - const url = new URL(apiPath, baseUrl) - - // 2. Make the API call (handle proxy if options.proxyAgent is set) - const response = await fetch(url.toString(), { - headers: { - Authorization: `Bearer ${apiToken}`, - Accept: "application/json", - }, - signal: AbortSignal.timeout(15000), - }) - - if (!response.ok) { - throw new Error(`Tracker API error: ${response.status} ${response.statusText}`) - } - - const data = await response.json() - - // 3. Map the API response to TrackerStats - return { - username: data.username, - group: data.class_name, - uploadedBytes: BigInt(data.uploaded), - downloadedBytes: BigInt(data.downloaded), - ratio: data.ratio, - bufferBytes: BigInt(data.uploaded) - BigInt(data.downloaded), - seedingCount: data.seeding ?? 0, - leechingCount: data.leeching ?? 0, - seedbonus: data.bonus ?? 0, - hitAndRuns: data.hnrs ?? 0, - requiredRatio: null, // set if the API provides it - warned: null, // set if the API provides it - freeleechTokens: null, // set if the API provides it - } - } -} -``` - -### TrackerStats fields - -Every adapter must return all 13 fields. Use `null` for fields the API doesn't provide. - -| Field | Type | Description | -| ----------------- | ----------------- | ------------------------------------ | -| `username` | `string` | Current username | -| `group` | `string` | User class / rank name | -| `uploadedBytes` | `bigint` | Total uploaded in bytes | -| `downloadedBytes` | `bigint` | Total downloaded in bytes | -| `ratio` | `number` | Upload/download ratio | -| `bufferBytes` | `bigint` | uploaded - downloaded | -| `seedingCount` | `number` | Active seeding torrents | -| `leechingCount` | `number` | Active leeching torrents | -| `seedbonus` | `number` | Bonus points / freeleech tokens | -| `hitAndRuns` | `number` | Active H&R warnings | -| `requiredRatio` | `number \| null` | Required ratio (Gazelle-specific) | -| `warned` | `boolean \| null` | Whether the user has a ratio warning | -| `freeleechTokens` | `number \| null` | Available freeleech tokens | - -### 2. Register the adapter - -Add it to `src/lib/adapters/index.ts`: - -```ts -import { MyPlatformAdapter } from "./myplatform" - -const adapters: Record = { - gazelle: new GazelleAdapter(), - ggn: new GGnAdapter(), - unit3d: new Unit3dAdapter(), - myplatform: new MyPlatformAdapter(), // add here -} - -export const DEFAULT_API_PATHS: Record = { - unit3d: "/api/user", - gazelle: "/ajax.php", - ggn: "/api.php", - myplatform: "/api/endpoint", // add here -} -``` - -### 3. Update the platform type - -Add your platform to the union type in `src/data/tracker-registry.ts`: - -```ts -platform: "unit3d" | "gazelle" | "ggn" | "myplatform" | "custom" -``` - -### 4. Create the registry entry - -Follow the registry entry steps above, setting `platform` to your new platform name. - -### Proxy support - -If you want proxy support, use `proxyFetch` from `src/lib/proxy.ts` when `options.proxyAgent` is set. See the UNIT3D adapter (`src/lib/adapters/unit3d.ts`) for the pattern — it falls back to regular `fetch` when no proxy is configured. - -### Byte parsing - -UNIT3D returns formatted byte strings like `"500.25 GiB"`. If your tracker's API does the same, use `parseBytes()` from `src/lib/parser.ts` to convert them to `bigint`. If the API returns raw byte counts, convert directly with `BigInt()`. diff --git a/docs/api-comparison-unit3d-gazelle.md b/docs/api-comparison-unit3d-gazelle.md deleted file mode 100644 index 940962b1..00000000 --- a/docs/api-comparison-unit3d-gazelle.md +++ /dev/null @@ -1,243 +0,0 @@ -# API Comparison: UNIT3D vs Gazelle vs GGn - -Reference document for adapter development. Compares the three tracker platform APIs and maps their fields to the shared `TrackerStats` interface. - ---- - -## Connection Details - -| Aspect | UNIT3D | Gazelle (RED/OPS) | GGn | -| ----------------------- | ----------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------- | -| **Base endpoint** | `/api/user` | `/ajax.php?action=index` | `/api.php?request=quick_user` + `/api.php?request=user&id=X` | -| **Auth method** | Query parameter: `?api_token=TOKEN` | Header: `Authorization: token TOKEN` | Query parameter: `?key=TOKEN` | -| **Response format** | Flat JSON object | Nested: `{ status, response: { userstats } }` | Nested: `{ status, response: { stats, personal, community } }` | -| **Status indication** | HTTP status codes only | HTTP codes + `status` field (`"success"` / `"failure"`) | HTTP codes + `status` field | -| **Byte representation** | Formatted strings (`"500.25 GiB"`) | Raw integers (bytes) | Raw integers (bytes) | -| **API calls per poll** | 1 | 1 | 2 (quick_user → user) | -| **Timeout** | 15s `AbortSignal` | 15s `AbortSignal` | 15s `AbortSignal` per call | - -## Field Mapping - -### Core Fields (always available) - -| TrackerStats field | UNIT3D source | Gazelle source | GGn source | Notes | -| ------------------ | ----------------------------- | ----------------------------------------- | ----------------------------------- | -------------------------------- | -| `username` | `username` (string) | `response.username` (string) | `response.username` (string) | Direct on all platforms | -| `group` | `group` (string) | `userstats.class` (string) | `personal.class` (string) | Different locations per platform | -| `uploadedBytes` | `uploaded` → `parseBytes()` | `userstats.uploaded` → `BigInt()` | `stats.uploaded` → `BigInt()` | UNIT3D needs string parsing | -| `downloadedBytes` | `downloaded` → `parseBytes()` | `userstats.downloaded` → `BigInt()` | `stats.downloaded` → `BigInt()` | UNIT3D needs string parsing | -| `ratio` | `ratio` → `parseFloat()` | `userstats.ratio` (number) | `stats.ratio` (string or number) | GGn full response returns string | -| `bufferBytes` | `buffer` → `parseBytes()` | Calculated: `uploaded - downloaded` | Calculated: `uploaded - downloaded` | Only UNIT3D provides directly | -| `seedbonus` | `seedbonus` → `parseFloat()` | `userstats.bonusPoints` or `.bonuspoints` | `stats.gold` (number) | GGn uses "gold" currency system | - -### Platform-Dependent Fields - -| TrackerStats field | UNIT3D | Gazelle (RED/OPS) | GGn | Notes | -| ------------------ | ------------------------ | -------------------------------------- | ------------------------------- | -------------------------------- | -| `seedingCount` | `seeding` (number) | `userstats.seedingcount` (optional) | `community.seeding` (nullable) | GGn depends on paranoia settings | -| `leechingCount` | `leeching` (number) | `userstats.leechingcount` (optional) | `community.leeching` (nullable) | GGn depends on paranoia settings | -| `hitAndRuns` | `hit_and_runs` (number) | **Not available** (0) | `personal.hnrs` (nullable) | Defaults to 0 when absent | -| `requiredRatio` | **Not available** (null) | `userstats.requiredratio` (number) | `stats.requiredRatio` (number) | UNIT3D returns null | -| `warned` | **Not available** (null) | **Requires 2nd call** (false) | `personal.warned` (boolean) | GGn gets it in the user call | -| `freeleechTokens` | **Not available** (null) | `userstats.freeleechTokens` (optional) | **Not available** (null) | GGn does not expose FL tokens | - -### Platform Capabilities Summary - -| Capability | UNIT3D | Gazelle | GGn | Status | -| ------------------------- | ------------ | -------------------- | ------------------ | ---------------------------------------- | -| Upload / Download / Ratio | Yes | Yes | Yes | **Implemented** | -| Buffer | Yes (direct) | Yes (calculated) | Yes (calculated) | **Implemented** | -| Seedbonus / Gold | Yes | Yes (most forks) | Yes (gold) | **Implemented** | -| Seeding / Leeching counts | Yes | Partial (some forks) | Paranoia-dependent | **Implemented** (defaults to 0) | -| Hit & Runs | Yes | No | Partial (nullable) | **Implemented** | -| Required Ratio | No | Yes | Yes | **Implemented** | -| Freeleech Tokens | No | Yes (some forks) | No | **Implemented** | -| Warned status | No | Needs 2nd call | Yes | **Implemented** (Gazelle defaults false) | -| Snatched count | No | Needs 2nd call | Available | **Not implemented** — future | -| Last access time | No | Needs 2nd call | Available | **Not implemented** — future | -| Upload/Download buffs | No | No | Yes (multipliers) | **Not implemented** — GGn-specific | - -## Response Shapes - -### UNIT3D `/api/user` - -```json -{ - "username": "JohnDoe", - "group": "Power User", - "uploaded": "500.25 GiB", - "downloaded": "125.50 GiB", - "ratio": "3.99", - "buffer": "374.75 GiB", - "seeding": 156, - "leeching": 2, - "seedbonus": "12500.00", - "hit_and_runs": 0 -} -``` - -### Gazelle `?action=index` - -```json -{ - "status": "success", - "response": { - "username": "JohnDoe", - "id": 12345, - "authkey": "...", - "passkey": "...", - "notifications": { - "messages": 0, - "notifications": 0, - "newAnnouncement": false, - "newBlog": false - }, - "userstats": { - "uploaded": 536870912000, - "downloaded": 134217728000, - "ratio": 4.0, - "requiredratio": 0.6, - "class": "Power User", - "bonusPoints": 12500, - "freeleechTokens": 3 - } - } -} -``` - -### GGn `?request=quick_user` - -```json -{ - "status": "success", - "response": { - "username": "thesneakyrobot", - "id": 74360, - "authkey": "...", - "passkey": "...", - "notifications": { "messages": 0, "notifications": 0, "newAnnouncement": 0 }, - "userstats": { - "uploaded": 372518353895, - "downloaded": 373640248681, - "ratio": 0.99, - "requiredratio": 0.012, - "class": "Elite Gamer" - } - } -} -``` - -### GGn `?request=user&id=X` - -```json -{ - "status": "success", - "response": { - "id": 12345, - "username": "ExampleUser", - "avatar": "", - "avatarType": 1, - "isFriend": false, - "bbProfileText": "", - "profileText": "", - "bbTitle": "", - "title": "", - "stats": { - "joinedDate": "2025-02-18 08:59:21", - "lastAccess": "2026-03-07 17:03:24", - "onIRC": true, - "uploaded": 372518353895, - "downloaded": 373640248681, - "fullDownloaded": 2854978605675, - "purchasedDownload": null, - "ratio": "0.99699", - "requiredRatio": 0.012, - "shareScore": 10.82, - "gold": 39858 - }, - "personal": { - "class": "Elite Gamer", - "facilitator": false, - "hnrs": null, - "paranoia": [], - "paranoiaText": "Off", - "donor": false, - "warned": false, - "enabled": true, - "publicKey": "", - "parked": false, - "ip": "xxx.xxx.xxx.xxx", - "passkey": "...", - "donated": "", - "invites": 2 - }, - "community": { - "clan": "None", - "profileViews": 14, - "hourlyGold": 9, - "posts": 2, - "actualPosts": 2, - "threads": null, - "forumLikes": 0, - "forumDislikes": 0, - "ircLines": 0, - "ircActualLines": null, - "torrentComments": 1, - "collections": null, - "requestsFilled": null, - "bountyEarnedUpload": null, - "bountyEarnedGold": null, - "requestsVoted": 11, - "bountySpentUpload": null, - "bountySpentGold": 260, - "reviews": 1, - "uploaded": null, - "seeding": null, - "leeching": null, - "snatched": 4425, - "uniqueSnatched": 4413, - "seedSize": null, - "invited": null - }, - "buffs": { - "Upload": 2, - "Download": 0.5, - "ForumPosts": 1, - "IRCLines": 1, - "IRCBonus": 2, - "CommunityXP": 1, - "TorrentsXP": 1, - "CommunityGold": 1.2, - "TorrentsGold": 1, - "ItemCost": 1, - "BountyFrom": 1, - "BountyOn": 1, - "Chance": 2 - }, - "achievements": { - "userLevel": "Elite Gamer", - "nextLevel": "Legendary Gamer", - "totalPoints": 2100, - "pointsToNextLvl": 900 - } - } -} -``` - -## Known Gazelle Fork Variations - -| Fork / Site | Platform | Auth Method | bonusPoints field | freeleechTokens | seedingcount in index | -| -------------------- | --------- | ------------ | ----------------- | --------------- | --------------------- | -| Redacted (RED) | `gazelle` | Header token | `bonusPoints` | Sometimes | No | -| Orpheus (OPS) | `gazelle` | Header token | `bonusPoints` | Sometimes | No | -| GazelleGames (GGn) | `ggn` | Query key | N/A (uses `gold`) | No | No (paranoia) | -| BroadcasTheNet (BTN) | `gazelle` | Header token | Varies | No | No | -| PassThePopcorn (PTP) | `gazelle` | Header token | Varies | No | No | -| AnimeBytes (AB) | `gazelle` | Header token | Varies | Varies | No | - -## Future Work - -- **Gazelle enrichment**: Add optional `?action=user&id=X` call to fetch warned/snatched for standard Gazelle sites -- **GGn buffs tracking**: Store upload/download multipliers for buffer projection -- **Per-site overrides**: Allow registry entries to specify custom field mappings -- **Rate limiting**: Some sites (especially RED) have strict API rate limits; adapter should respect them diff --git a/docs/kb/docs/contributing/adding-a-tracker.md b/docs/kb/docs/contributing/adding-a-tracker.md index e255060a..47ffc4bc 100644 --- a/docs/kb/docs/contributing/adding-a-tracker.md +++ b/docs/kb/docs/contributing/adding-a-tracker.md @@ -59,11 +59,13 @@ Here is the full template for reference: // // Validator checks: // - slug: lowercase letters and hyphens only -// - platform: "unit3d" | "gazelle" | "ggn" | "nebulance" | "custom" +// - platform: "unit3d" | "gazelle" | "ggn" | "nebulance" | "mam" | "custom" // - apiPath must match platform default: -// unit3d → "/api/user" -// gazelle → "/ajax.php" -// ggn → "/api.php" +// unit3d → "/api/user" +// gazelle → "/ajax.php" +// ggn → "/api.php" +// nebulance → "/api.php" +// mam → "/jsonLoad.php" // - url: https only // - contentCategories: values must come from the allowed list above // - language: required @@ -80,12 +82,12 @@ export const mytracker: TrackerRegistryEntry = { description: "TODO", // 1-2 sentence overview // ── Platform & API ────────────────────────────────────────────────── - platform: "unit3d", // "unit3d" | "gazelle" | "ggn" | "nebulance" | "custom" + platform: "unit3d", // "unit3d" | "gazelle" | "ggn" | "nebulance" | "mam" | "custom" // Platform-specific fields (uncomment for your platform): // gazelleAuthStyle: "token", // gazelle only — "token" | "raw" // gazelleEnrich: true, // gazelle only — enables enrichment call // unit3dAuthStyle: "bearer", // unit3d only — "bearer" | "query" - apiPath: "/api/user", // unit3d: "/api/user" | gazelle: "/ajax.php" | ggn: "/api.php" + apiPath: "/api/user", // unit3d: "/api/user" | gazelle: "/ajax.php" | ggn: "/api.php" | mam: "/jsonLoad.php" // ── Content ───────────────────────────────────────────────────────── specialty: "", // what the tracker is known for (e.g. "HD Movies", "Anime") @@ -206,7 +208,7 @@ description: "The largest general music tracker (also has some software). Has an #### `platform` -Type: `"unit3d" | "gazelle" | "ggn" | "nebulance" | "custom"` +Type: `"unit3d" | "gazelle" | "ggn" | "nebulance" | "mam" | "custom"` Which adapter handles API requests for this tracker. This controls how the scheduler fetches stats. Must match the software the tracker runs. @@ -216,6 +218,7 @@ Which adapter handles API requests for this tracker. This controls how the sched | `"gazelle"` | Runs Gazelle or a derivative (Orpheus, Gazelle-Music, etc.) | | `"ggn"` | GazelleGames only — custom API different from standard Gazelle | | `"nebulance"` | Nebulance-specific API | +| `"mam"` | MyAnonaMouse — cookie-based auth via `mam_id` session cookie | | `"custom"` | Placeholder, not implemented — do not use | #### `gazelleAuthStyle` diff --git a/docs/kb/docs/contributing/tracker-responses-mam.md b/docs/kb/docs/contributing/tracker-responses-mam.md new file mode 100644 index 00000000..8eedd415 --- /dev/null +++ b/docs/kb/docs/contributing/tracker-responses-mam.md @@ -0,0 +1,361 @@ +# MAM (MyAnonaMouse) API Response + +MAM uses a custom JSON API with cookie-based authentication. A single endpoint returns all user stats including a detailed snatch summary breakdown. + +## Endpoint + +One request per poll: + +``` +GET {baseUrl}/jsonLoad.php?snatch_summary¬if +``` + +The `snatch_summary` query parameter enables the detailed torrent category breakdown. The `notif` parameter includes notification counts (PMs, tickets, requests). Without these, only basic stats (username, ratio, uploaded, downloaded) are returned. + +Optional parameters (not used by the adapter): + +- `clientStats` — includes per-client connectivity info from MAM's perspective (30min cache). Returns empty array without `?id=`, full breakdown with `?id={uid}`. +- `pretty` — pretty-prints the JSON output +- `id={userid}` — load a specific user's data. When set to your own UID, `clientStats` returns the full per-IP/port breakdown. When set to another user's UID, returns limited public data. + +**Note:** The `?id=` parameter does NOT return additional profile fields (like join date). The response shape is identical to the self-lookup — `created` and `update` fields are cache timestamps that change between requests, not account dates. + +## Authentication + +MAM uses a `mam_id` session cookie instead of an API key or authorization header: + +``` +Cookie: mam_id={SESSION_COOKIE} +``` + +The session cookie is obtained from MAM's Security Settings page (User Preferences → Security). Users should create an IP-locked or ASN-locked session for API use. **Session cookies rotate monthly** — users must update the stored token periodically. + +Auth failures return an HTML error string, not JSON: + +``` +Error, you are not signed in
Other error +``` + +The adapter detects this by checking for the absence of `username` in the response. + +## Example Response + +```json +{ + "username": "thesneakyrobot", + "uid": 230500, + "classname": "VIP", + "ratio": 26.7, + "uploaded": "5.125 TiB", + "downloaded": "196.47 GiB", + "uploaded_bytes": 5635036489461, + "downloaded_bytes": 210957629456, + "seedbonus": 99999, + "wedges": 207, + "vip_until": "2026-06-10 06:44:38", + "connectable": "offline", + "country_code": "us", + "country_name": "United States", + "created": 1774543581, + "update": 1774543581, + "ipv6_mac": false, + "v6_connectable": null, + "partial": false, + "recently_deleted": 45, + "leeching": { "name": "Leeching Torrents", "count": 0, "red": false, "size": null }, + "sSat": { "name": "Seeding - Satisfied", "count": 0, "red": false, "size": null }, + "seedHnr": { "name": "Seeding - H&R - Not Yet Satisfied", "count": 0, "red": true, "size": null }, + "seedUnsat": { "name": "Seeding - pre-H&R - Not Yet Satisfied", "count": 0, "red": false, "size": null }, + "upAct": { "name": "Seeding - Uploads", "count": 0, "red": false, "size": null }, + "upInact": { "name": "Not Seeding - Uploads", "count": 0, "red": false, "size": null }, + "inactHnr": { "name": "Not Seeding - H&R - Not Yet Satisfied", "count": 0, "red": true, "size": null }, + "inactSat": { "name": "Not Seeding - Satisfied", "count": 2306, "red": false, "size": 477079402691 }, + "inactUnsat": { "name": "Not Seeding - pre-H&R - Not Yet Satisfied", "count": 0, "red": true, "size": null }, + "unsat": { "name": "Unsatisfied", "count": 0, "red": false, "limit": 150, "size": null }, + "duplicates": { "name": "Duplicate peer entries", "count": 0, "red": true }, + "reseed": { "name": "Reseed requests", "count": 0, "inactive": 0, "red": false }, + "ite": { "name": "Important Tracker Errors", "count": 0, "latest": 0 } +} +``` + +## Field Mapping + +| TrackerStats field | MAM path | Type | Notes | +| --- | --- | --- | --- | +| `username` | `username` | `string` | Direct | +| `group` | `classname` | `string` | Falls back to `"Unknown"` | +| `remoteUserId` | `uid` | `number` | Cached for future use | +| `uploadedBytes` | `uploaded_bytes` | `number` | `BigInt()` — raw bytes, no parsing needed | +| `downloadedBytes` | `downloaded_bytes` | `number` | `BigInt()` — raw bytes, no parsing needed | +| `ratio` | `ratio` | `number` | Direct float value | +| `bufferBytes` | — | — | Calculated: `uploadedBytes - downloadedBytes` (min `0`) | +| `seedingCount` | `sSat.count + seedHnr.count + seedUnsat.count + upAct.count` | `number` | Sum of all seeding snatch categories | +| `leechingCount` | `leeching.count` | `number` | Direct | +| `seedbonus` | `seedbonus` | `number` | Direct; MAM caps at 99,999 | +| `hitAndRuns` | `inactHnr.count` | `number` | Only inactive (not seeding) HnR torrents | +| `requiredRatio` | — | — | Always `null` — MAM uses class-based ratio thresholds | +| `warned` | — | — | Always `null` — not exposed in API | +| `freeleechTokens` | `wedges` | `number` | MAM calls them "FL Wedges" | +| `joinedDate` | — | — | Not available from this endpoint | +| `lastAccessDate` | — | — | Not available from this endpoint | +| `platformMeta` | Multiple fields | — | `MamPlatformMeta` object (see below) | + +## Platform Meta (MamPlatformMeta) + +| Field | MAM path | Type | Notes | +| --- | --- | --- | --- | +| `vipUntil` | `vip_until` | `string \| null` | VIP expiry date (`"2026-06-10 06:44:38"`) | +| `connectable` | `connectable` | `string` | `"offline"` or `"online"` | +| `unsatisfiedCount` | `unsat.count` | `number` | Current unsatisfied torrent count | +| `unsatisfiedLimit` | `unsat.limit` | `number` | Class-dependent limit (User=50, PU=100, VIP=150, above VIP=200) | +| `inactiveSatisfiedCount` | `inactSat.count` | `number` | Completed torrents no longer seeding | +| `seedingHnrCount` | `seedHnr.count` | `number` | HnR torrents being actively resolved | +| `inactiveUnsatisfiedCount` | `inactUnsat.count` | `number` | Pre-HnR torrents not being seeded (ticking clock) | +| `trackerErrorCount` | `ite.count` | `number` | Important Tracker Errors | +| `recentlyDeleted` | `recently_deleted` | `number` | Recently deleted torrents | + +## Snatch Summary Categories + +MAM's snatch summary groups all torrents into categories based on seeding status and satisfaction: + +| Category | Field | Meaning | Red flag? | +| --- | --- | --- | --- | +| Seeding - Satisfied | `sSat` | Fully seeded past 72hrs, still active | No | +| Seeding - H&R - Not Yet Satisfied | `seedHnr` | Active HnR being resolved by seeding | Yes | +| Seeding - pre-H&R - Not Yet Satisfied | `seedUnsat` | Not yet HnR, still seeding toward 72hrs | No | +| Seeding - Uploads | `upAct` | User's own uploads, still seeding | No | +| Not Seeding - H&R - Not Yet Satisfied | `inactHnr` | **Danger:** inactive HnR, needs immediate attention | Yes | +| Not Seeding - pre-H&R - Not Yet Satisfied | `inactUnsat` | Ticking clock toward HnR status | Yes | +| Not Seeding - Satisfied | `inactSat` | Completed, no longer seeding | No | +| Not Seeding - Uploads | `upInact` | User's uploads, not currently seeding | No | +| Leeching | `leeching` | Currently downloading | No | +| Unsatisfied | `unsat` | Total unsatisfied (includes `limit` field) | No (has limit) | + +Each category object has: `name` (human-readable), `count` (number), `red` (boolean — MAM flags it as concerning), `size` (bytes or null). + +## Quirks + +**Dual byte representation.** MAM returns both formatted strings (`uploaded`: `"5.125 TiB"`) and raw integers (`uploaded_bytes`: `5635036489461`). The adapter uses the raw integers directly via `BigInt()`, avoiding the `parseBytes()` parsing that UNIT3D requires. + +**Bonus points cap at 99,999.** MAM has a hard cap on seedbonus. Points earned above this are lost. The notification system should alert when the cap is reached. + +**FL Wedges are not bonus points.** Wedges (`wedges`) are a separate currency from seedbonus. They are earned from the Millionaire's Vault and can be exchanged for Personal or Staff Freeleech on individual torrents. + +**Cookie auth, not API key.** MAM is the only platform using cookie-based auth. The `mam_id` session cookie must be set up in MAM's Security Settings as an IP-locked or ASN-locked session. Regular browser session cookies also work but are less stable. Cookies rotate monthly. + +**`created` and `update` are cache timestamps, NOT account dates.** Verified: these values change between consecutive API calls (observed `1774552832` → `1774553470` seconds apart). They reflect when MAM's internal cache was last refreshed. **MAM does not expose account join date via any API endpoint** — users must enter it manually. + +**`unsat.limit` is class-dependent.** The unsatisfied torrent limit varies by user class: User=50, Power User=100, VIP=150, above VIP=200. The API returns the current limit for the authenticated user. + +**72-hour seed requirement.** MAM requires 72 hours of seeding within 30 days per torrent. Failure to meet this results in a Hit & Run. The `inactHnr` count represents torrents that have passed the deadline without sufficient seeding. + +## Other MAM Endpoints (Not Used by Adapter) + +The following endpoints exist in the MAM API but are not used by the Tracker Tracker adapter. They are documented here for reference and potential future use. + +### `/jsonLoad.php?clientStats` + +Returns torrent client connectivity information from MAM's perspective (30-minute cache). Not used by the adapter because Tracker Tracker has its own qBittorrent integration. However, the data is useful for diagnosing connectivity issues since it shows what MAM sees. + +When called without `?id=`, `clientStats` is an empty array. When called with `?id={uid}` (your own user ID), it returns the full client breakdown: + +```json +{ + "clientStats": { + "uid": 230500, + "username": "thesneakyrobot", + "seedbonus": 99999, + "uploaded": "5.125 TiB", + "downloaded": "196.47 GiB", + "uploaded_bytes": 5635036489461, + "downloaded_bytes": 210957629456, + "classname": "VIP", + "wedges": 207, + "vip_until": "2026-06-10 06:44:38", + "ratio": 26.7, + "country_name": "United States", + "country_code": "us", + "clientStats": [ + { + "ip": "79.127.136.26", + "port": 37649, + "agent": "qBittorrent/5.1.4", + "connectable": "no", + "subResponse": "timeout", + "startTime": 1774550236, + "count": 17, + "lastcheck": 1774552228, + "timeTaken": 7166 + }, + { + "ip": "79.127.136.70", + "port": 42068, + "agent": "qBittorrent/5.1.4", + "connectable": "yes", + "subResponse": "Connect", + "startTime": 1774550262, + "count": 2106, + "lastcheck": 1774551105, + "timeTaken": 250 + } + ] + } +} +``` + +**Client entry fields:** + +| Field | Type | Description | +| --- | --- | --- | +| `ip` | string | IP address MAM sees the client connecting from | +| `port` | number | Port the client announces | +| `agent` | string | Torrent client user agent string | +| `connectable` | string | `"yes"` or `"no"` — whether MAM can reach the client | +| `subResponse` | string | `"Connect"` (success) or `"timeout"` (unreachable) | +| `startTime` | number | Unix timestamp of first seen | +| `count` | number | Number of torrents associated with this client entry | +| `lastcheck` | number | Unix timestamp of last connectivity check | +| `timeTaken` | number | Milliseconds for the connectivity check | + +Note: A single user can have multiple client entries across different IPs/ports (e.g., home connection + VPN + seedbox). The top-level `"connectable"` field on the main response is `"yes"` if ANY client entry is connectable. + +### `/jsonLoad.php?notif` + +Returns notification counts (PMs, tickets, requests). The adapter includes `?notif` in every poll alongside `?snatch_summary`. The counts are stored in `MamPlatformMeta` (`unreadPMs`, `openTickets`, `pendingRequests`, `unreadTopics`) and surfaced as an unread badge on the tracker detail page. + +The `notifs` object appears when `?notif` is included: + +```json +{ + "notifs": { + "pms": 0, + "aboutToDropClient": 0, + "tickets": 0, + "waiting_tickets": 0, + "requests": 0, + "topics": 0 + } +} +``` + +These fields are merged into the standard `/jsonLoad.php` response alongside the other user data fields. + +### `/json/userBonusHistory.php` + +Shows a history of bonus points and wedge transactions. Requires `mam_id` cookie auth on `www.myanonamouse.net`. + +**Parameters:** + +| Parameter | Type | Description | +| --- | --- | --- | +| `other_userid` | int | Filter to transactions with a specific user | +| `type[]` | list | Which transaction types to show: `giftPoints`, `giftWedge`, `wedgePF`, `wedgeGFL`, `torrentThanks`, `millionaires` | + +**Example request:** + +``` +GET /json/userBonusHistory.php?type[]=giftWedge&type[]=wedgePF&type[]=wedgeGFL +``` + +**Example response:** + +```json +[ + { + "timestamp": 1644804607.0995, + "amount": -1, + "type": "wedgePF", + "tid": 330896, + "title": "1984", + "other_userid": null, + "other_name": null + }, + { + "timestamp": 1641914230.1078, + "amount": -1, + "type": "giftWedge", + "tid": null, + "title": null, + "other_userid": 192566, + "other_name": "pezzap7" + }, + { + "timestamp": 1610486584.1027, + "amount": -10, + "type": "wedgeGFL", + "tid": 220870, + "title": "Asimov's Robot, Empire, and Foundation Series", + "other_userid": null, + "other_name": null + } +] +``` + +**Transaction types:** + +| Type | Meaning | +| --- | --- | +| `giftPoints` | Bonus points gifted to/from another user | +| `giftWedge` | FL Wedge gifted to/from another user | +| `wedgePF` | Wedge spent on Personal Freeleech | +| `wedgeGFL` | Wedges spent on Staff Freeleech pick | +| `torrentThanks` | Points received from torrent "thank you" | +| `millionaires` | Wedges earned from Millionaire's Vault | + +### `/json/dynamicSeedbox.php` + +Sets the dynamic seedbox IP. Operational tool for VPN/seedbox users — not relevant to stats tracking. Requires a specially configured API session (ASN-locked + Dynamic Seedbox permission) on `t.myanonamouse.net`. + +**Rate limit:** Once per hour (rolling window). + +**Example response (success):** + +```json +{ + "Success": true, + "msg": "Completed", + "ip": "10.2.3.4", + "ASN": 1234, + "AS": "Org for 1234" +} +``` + +**Example response (rate limited):** + +```json +{ + "Success": false, + "msg": "Last change too recent", + "ip": "10.2.3.4", + "ASN": 1234, + "AS": "Org for 1234" +} +``` + +**Error codes:** + +| HTTP | Message | Meaning | +| --- | --- | --- | +| 200 | `No Change` | IP already set to this address | +| 200 | `Completed` | IP updated successfully | +| 429 | `Last change too recent` | Rate limited (1 hour window) | +| 403 | `No Session Cookie` | Missing `mam_id` cookie | +| 403 | `Invalid session` | Bad cookie value or IP/ASN mismatch | +| 403 | `Incorrect session type - not allowed this function` | Session lacks Dynamic Seedbox permission | +| 403 | `Incorrect session type - non-API session` | Browser session used instead of API session | + +### `/json/jsonIp.php` + +Returns the caller's current IP, ASN, and AS organization name. Available on both `www.myanonamouse.net` and `t.myanonamouse.net`. No authentication required. + +```json +{ + "ip": "10.2.3.4", + "ASN": 123, + "AS": "Some Provider Here" +} +``` + +## Supported Trackers + +- [MyAnonaMouse](trackers/myanonamouse.md) *(to be created)* diff --git a/docs/kb/docs/contributing/tracker-responses.md b/docs/kb/docs/contributing/tracker-responses.md index b37ee1e6..ee223c15 100644 --- a/docs/kb/docs/contributing/tracker-responses.md +++ b/docs/kb/docs/contributing/tracker-responses.md @@ -32,7 +32,7 @@ interface TrackerStats { lastAccessDate?: string shareScore?: number avatarUrl?: string - platformMeta?: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta + platformMeta?: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | MamPlatformMeta } ``` @@ -43,6 +43,7 @@ Fields marked `null` in the platform pages mean the platform does not expose tha - [UNIT3D API Response](tracker-responses-unit3d.md) - [Gazelle API Response](tracker-responses-gazelle.md) - [GGn API Response](tracker-responses-ggn.md) +- [MAM API Response](tracker-responses-mam.md) --- diff --git a/docs/kb/docs/reference/platform-differences.md b/docs/kb/docs/reference/platform-differences.md index 55d105da..3a7b25f2 100644 --- a/docs/kb/docs/reference/platform-differences.md +++ b/docs/kb/docs/reference/platform-differences.md @@ -5,7 +5,7 @@ description: Stat availability and behavior differences across UNIT3D, Gazelle, # Platform Differences -Tracker Tracker supports three tracker platforms: **UNIT3D**, **Gazelle**, and **GGn**. Each platform exposes different stats and uses a different authentication method. This page tells you what to expect when adding a tracker of each type. +Tracker Tracker supports multiple tracker platforms: **UNIT3D**, **Gazelle**, **GGn**, **Nebulance**, and **MAM** (MyAnonaMouse). Each platform exposes different stats and uses a different authentication method. This page tells you what to expect when adding a tracker of each type. --- @@ -18,6 +18,7 @@ How you authenticate with each platform's API depends on the platform type. In a | **UNIT3D** | Appended as a query parameter on every request (`?api_token=TOKEN`). HTTPS is required to prevent the token from being exposed in server logs. | | **Gazelle** | Sent as an HTTP `Authorization` header (`Authorization: token TOKEN`). Some Gazelle forks accept the token without the `token ` prefix — Tracker Tracker handles both. | | **GGn** | Appended as a query parameter (`?key=TOKEN`), similar to UNIT3D but using a different parameter name. | +| **MAM** | Sent as a `Cookie: mam_id=VALUE` header. Uses a session cookie from MAM's Security Settings page, not a traditional API key. Session cookies rotate monthly. | --- @@ -25,26 +26,26 @@ How you authenticate with each platform's API depends on the platform type. In a The table below shows which stats Tracker Tracker can collect from each platform. A note in the cell means the stat is available but with caveats. -| Stat | UNIT3D | Gazelle | GGn | -| ------------------------- | ------------------------ | -------------------------------- | -------------------------------- | -| Upload / Download / Ratio | Yes | Yes | Yes | -| Buffer | Yes (tracker-calculated) | Approximate (calculated locally) | Approximate (calculated locally) | -| Seeding count | Yes | Some forks only | Paranoia-dependent | -| Leeching count | Yes | Some forks only | Paranoia-dependent | -| Seedbonus / Bonus Points | Yes | Yes (most forks) | Yes (called "gold") | -| Required Ratio | No | Yes | Yes | -| Hit & Runs | Yes | No | Partial (may be null) | -| Freeleech Tokens | No | Some forks only | No | -| Warned status | No | Some sites only | Yes | -| Class / Rank | Yes | Yes | Yes | -| Join date | No | Some sites only | Yes | -| Last access date | No | Some sites only | Yes | -| Share Score | No | No | Yes | -| Donor status | No | Some sites only | Yes | -| Snatched count | No | Some sites only | Yes | -| Community / rank data | No | Some sites only | Yes | -| Upload / download buffs | No | No | Yes | -| Avatar | No | Some sites only | No | +| Stat | UNIT3D | Gazelle | GGn | MAM | +| ------------------------- | ------------------------ | -------------------------------- | -------------------------------- | ----------------------------------- | +| Upload / Download / Ratio | Yes | Yes | Yes | Yes (raw bytes + formatted strings) | +| Buffer | Yes (tracker-calculated) | Approximate (calculated locally) | Approximate (calculated locally) | Approximate (calculated locally) | +| Seeding count | Yes | Some forks only | Paranoia-dependent | Yes (sum of snatch_summary seeding) | +| Leeching count | Yes | Some forks only | Paranoia-dependent | Yes | +| Seedbonus / Bonus Points | Yes | Yes (most forks) | Yes (called "gold") | Yes | +| Required Ratio | No | Yes | Yes | No | +| Hit & Runs | Yes | No | Partial (may be null) | Yes (inactive unsatisfied HnRs) | +| Freeleech Tokens | No | Some forks only | No | Yes (called "wedges") | +| Warned status | No | Some sites only | Yes | No | +| Class / Rank | Yes | Yes | Yes | Yes | +| Join date | No | Some sites only | Yes | No | +| Last access date | No | Some sites only | Yes | No | +| Share Score | No | No | Yes | No | +| Donor status | No | Some sites only | Yes | No (VIP status + expiry available) | +| Snatched count | No | Some sites only | Yes | Yes (via snatch_summary categories) | +| Community / rank data | No | Some sites only | Yes | No | +| Upload / download buffs | No | No | Yes | No | +| Avatar | No | Some sites only | No | No | ### Notes on specific cells @@ -108,3 +109,17 @@ GGn's full user profile includes: - Total and unique snatch counts - Active multiplier buffs (upload, download, forum posts, etc.) - Achievement level, points, and progress toward next level + +### MAM (MyAnonaMouse) + +MAM uses a single `/jsonLoad.php` endpoint with `?snatch_summary` to return everything in one call. MAM-specific extras include: + +- VIP status and expiry date +- Connectivity status (connectable/offline) +- Unsatisfied torrent count and limit (class-dependent: User=50, PU=100, VIP=150, above VIP=200) +- Detailed snatch summary breakdown (seeding satisfied, seeding HnR, inactive satisfied, etc.) +- Tracker error count (important tracker errors) +- Recently deleted torrent count +- FL Wedge count (freeleech tokens) + +**Authentication note:** MAM uses a `mam_id` session cookie rather than a traditional API key. The cookie is obtained from MAM's Security Settings page (User Preferences → Security). Session cookies rotate monthly, so users will need to update their token periodically. diff --git a/docs/kb/mkdocs.yml b/docs/kb/mkdocs.yml index 79d2f96a..8efeb1fc 100644 --- a/docs/kb/mkdocs.yml +++ b/docs/kb/mkdocs.yml @@ -69,6 +69,7 @@ nav: - UNIT3D: contributing/tracker-responses-unit3d.md - Gazelle: contributing/tracker-responses-gazelle.md - GGn: contributing/tracker-responses-ggn.md + - MAM: contributing/tracker-responses-mam.md - Individual Trackers: - Aither: contributing/trackers/aither.md - AlphaRatio: contributing/trackers/alpharatio.md From ab576d82cca46a829499011be2bb3aa03681bb1b Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 17:25:06 -0500 Subject: [PATCH 54/59] chore: linting --- biome.json | 2 +- scripts/security-audit.ts | 10 ++++---- .../trackers/[id]/TrackerDetailClient.tsx | 6 ++--- src/app/api/auth/change-password/route.ts | 2 +- src/app/api/settings/backup/export/route.ts | 2 +- src/app/api/settings/backup/restore/route.ts | 7 ++++-- src/app/api/trackers/[id]/mousehole/route.ts | 8 ++++++- src/app/api/trackers/[id]/poll/route.ts | 5 ++-- src/app/api/trackers/[id]/torrents/route.ts | 3 +-- src/app/api/trackers/test/route.ts | 2 +- src/app/api/trackers/tracker-routes.test.ts | 4 ++-- src/app/api/upload-image/route.ts | 9 ++------ src/components/TrackerSettingsDialog.tsx | 2 +- .../platform/MamHealthOverview.tsx | 23 +++++-------------- src/lib/__tests__/security.test.ts | 1 - 15 files changed, 39 insertions(+), 47 deletions(-) diff --git a/biome.json b/biome.json index a5af73b4..2c54afc6 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.8/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.9/schema.json", "files": { "includes": ["**", "!.next", "!node_modules", "!public", "!.history", "!.claude"] }, diff --git a/scripts/security-audit.ts b/scripts/security-audit.ts index ee9bbf60..d7fe8435 100644 --- a/scripts/security-audit.ts +++ b/scripts/security-audit.ts @@ -281,9 +281,9 @@ function extractHandlerBodies( ): Array<{ method: string; startLine: number; body: string }> { const HANDLER_RE = /export\s+(?:async\s+)?function\s+(GET|POST|PATCH|PUT|DELETE)\b/g const results: Array<{ method: string; startLine: number; body: string }> = [] - let handlerMatch: RegExpExecArray | null + let handlerMatch: RegExpExecArray | null = HANDLER_RE.exec(content) - while ((handlerMatch = HANDLER_RE.exec(content)) !== null) { + while (handlerMatch !== null) { const method = handlerMatch[1] const startLine = content.slice(0, handlerMatch.index).split("\n").length @@ -324,6 +324,7 @@ function extractHandlerBodies( startLine, body: content.slice(openBrace, end + 1), }) + handlerMatch = HANDLER_RE.exec(content) } return results @@ -2180,9 +2181,9 @@ function checkNotificationSsrfValidation(): CheckResult { // Find case blocks for notification types that are NOT "not yet supported" const CASE_RE = /case\s+["'](\w+)["']\s*:\s*\{/g - let caseMatch: RegExpExecArray | null + let caseMatch: RegExpExecArray | null = CASE_RE.exec(content) - while ((caseMatch = CASE_RE.exec(content)) !== null) { + while (caseMatch !== null) { const typeName = caseMatch[1] const caseStart = caseMatch.index @@ -2212,6 +2213,7 @@ function checkNotificationSsrfValidation(): CheckResult { detail: `Notification type "${typeName}" has active validation but does not call isUnsafeNetworkHost() — SSRF risk`, }) } + caseMatch = CASE_RE.exec(content) } return { diff --git a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx index 926a874d..0aa1ee70 100644 --- a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx +++ b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx @@ -19,7 +19,6 @@ import { resolveSlots } from "@/components/tracker-detail/resolve-slots" import { TrackerDetailHeader } from "@/components/tracker-detail/TrackerDetailHeader" import { TrackerInfoTab } from "@/components/tracker-detail/TrackerInfoTab" import { TrackerStatusBanner } from "@/components/tracker-detail/TrackerStatusBanner" -import type { TrackerRegistryEntry } from "@/data/tracker-registry" import { findRegistryEntry } from "@/data/tracker-registry" import { useTrackerTorrents } from "@/hooks/useTrackerTorrents" import { computeDelta, hexToRgba } from "@/lib/formatters" @@ -207,9 +206,10 @@ export function TrackerDetailClient({ const delta = useMemo(() => computeDelta(snapshots), [snapshots]) const tc = tracker?.color || CHART_THEME.accent + const baseUrl = tracker?.baseUrl const registryEntry = useMemo( - () => (tracker ? findRegistryEntry(tracker.baseUrl) : undefined), - [tracker?.baseUrl] + () => (baseUrl ? findRegistryEntry(baseUrl) : undefined), + [baseUrl] ) const { statCardSlots, badgeSlots, progressSlots } = useMemo(() => { diff --git a/src/app/api/auth/change-password/route.ts b/src/app/api/auth/change-password/route.ts index 245ed3a7..d255e372 100644 --- a/src/app/api/auth/change-password/route.ts +++ b/src/app/api/auth/change-password/route.ts @@ -190,7 +190,7 @@ export async function POST(request: Request) { log.error( { route: "POST /api/auth/change-password", - error: err instanceof Error ? err.message : "unknown", + error: String(err), }, "transaction failed during password change" ) diff --git a/src/app/api/settings/backup/export/route.ts b/src/app/api/settings/backup/export/route.ts index 93e80162..4b49505c 100644 --- a/src/app/api/settings/backup/export/route.ts +++ b/src/app/api/settings/backup/export/route.ts @@ -104,7 +104,7 @@ export async function POST(request: Request) { log.error( { route: "POST /api/settings/backup/export", - error: err instanceof Error ? err.message : String(err), + error: String(err), }, "backup export failed" ) diff --git a/src/app/api/settings/backup/restore/route.ts b/src/app/api/settings/backup/restore/route.ts index b6463353..c1e2aabe 100644 --- a/src/app/api/settings/backup/restore/route.ts +++ b/src/app/api/settings/backup/restore/route.ts @@ -153,9 +153,12 @@ export async function POST(request: Request) { payload = raw as BackupPayload } } catch (err) { - const message = err instanceof Error ? err.message : "Unknown error" + log.error( + { route: "POST /api/settings/backup/restore", error: String(err) }, + "backup file validation or decryption failed" + ) return NextResponse.json( - { error: `Invalid or corrupted backup file: ${message}` }, + { error: "Invalid or corrupted backup file" }, { status: 400 } ) } diff --git a/src/app/api/trackers/[id]/mousehole/route.ts b/src/app/api/trackers/[id]/mousehole/route.ts index 81f2decb..e78b4f37 100644 --- a/src/app/api/trackers/[id]/mousehole/route.ts +++ b/src/app/api/trackers/[id]/mousehole/route.ts @@ -125,6 +125,12 @@ export async function POST(request: Request, { params }: RouteContext) { const { mouseholeBase } = resolved + const MAX_BODY_SIZE = 256 + const contentLength = Number(request.headers.get("content-length") ?? 0) + if (contentLength > MAX_BODY_SIZE) { + return NextResponse.json({ error: "Request body too large" }, { status: 413 }) + } + let body: { force?: boolean } = {} try { const raw = await request.json() @@ -132,7 +138,7 @@ export async function POST(request: Request, { params }: RouteContext) { body = { force: Boolean(raw.force) } } } catch (_err) { - log.debug({ route: "POST /api/trackers/[id]/mousehole" }, "no request body (optional)") + log.warn({ route: "POST /api/trackers/[id]/mousehole" }, "no request body (optional)") } const controller = new AbortController() diff --git a/src/app/api/trackers/[id]/poll/route.ts b/src/app/api/trackers/[id]/poll/route.ts index fb1af665..dcaed335 100644 --- a/src/app/api/trackers/[id]/poll/route.ts +++ b/src/app/api/trackers/[id]/poll/route.ts @@ -65,11 +65,10 @@ export async function POST(_request: Request, props: { params: Promise<{ id: str ) return NextResponse.json({ error: "Session expired — please log in again" }, { status: 401 }) } - const message = error instanceof Error ? error.message : "Poll failed" log.error( - { route: "POST /api/trackers/[id]/poll", trackerId, error: message }, + { route: "POST /api/trackers/[id]/poll", trackerId, error: String(error) }, "manual poll failed" ) - return NextResponse.json({ error: message }, { status: 500 }) + return NextResponse.json({ error: "Poll failed" }, { status: 500 }) } } diff --git a/src/app/api/trackers/[id]/torrents/route.ts b/src/app/api/trackers/[id]/torrents/route.ts index 8b398e56..b3e19935 100644 --- a/src/app/api/trackers/[id]/torrents/route.ts +++ b/src/app/api/trackers/[id]/torrents/route.ts @@ -63,9 +63,8 @@ export async function GET(request: Request, props: { params: Promise<{ id: strin const result = await fetchAndMergeTorrents(clients, [tag], key, qbtFilter) return NextResponse.json(result) } catch (error) { - const message = error instanceof Error ? error.message : "unknown" log.error( - { route: "GET /api/trackers/[id]/torrents", trackerId, error: message }, + { route: "GET /api/trackers/[id]/torrents", trackerId, error: String(error) }, "torrent fetch failed" ) return NextResponse.json({ error: "Failed to fetch torrents" }, { status: 502 }) diff --git a/src/app/api/trackers/test/route.ts b/src/app/api/trackers/test/route.ts index 39acf4ee..fec70b8f 100644 --- a/src/app/api/trackers/test/route.ts +++ b/src/app/api/trackers/test/route.ts @@ -64,7 +64,7 @@ export async function POST(request: Request) { log.warn( { route: "POST /api/trackers/test", - error: error instanceof Error ? error.message : String(error), + error: String(error), }, "tracker connection test failed" ) diff --git a/src/app/api/trackers/tracker-routes.test.ts b/src/app/api/trackers/tracker-routes.test.ts index f346736a..b933798d 100644 --- a/src/app/api/trackers/tracker-routes.test.ts +++ b/src/app/api/trackers/tracker-routes.test.ts @@ -776,7 +776,7 @@ describe("POST /api/trackers/[id]/poll", () => { expect(data.success).toBe(true) }) - it("returns 500 when poll fails with error message", async () => { + it("returns 500 with generic message when poll fails", async () => { ;(pollTracker as ReturnType).mockRejectedValue(new Error("Connection refused")) const request = makeRequest("http://localhost/api/trackers/1/poll", undefined, "POST") @@ -785,7 +785,7 @@ describe("POST /api/trackers/[id]/poll", () => { const data = await response.json() expect(response.status).toBe(500) - expect(data.error).toBe("Connection refused") + expect(data.error).toBe("Poll failed") }) it("returns 'Poll failed' when pollTracker rejects with non-Error", async () => { diff --git a/src/app/api/upload-image/route.ts b/src/app/api/upload-image/route.ts index 2cea57c2..958651bd 100644 --- a/src/app/api/upload-image/route.ts +++ b/src/app/api/upload-image/route.ts @@ -160,14 +160,9 @@ export async function POST(request: Request) { return NextResponse.json(result) } catch (err) { const isTimeout = err instanceof DOMException && err.name === "TimeoutError" - const message = isTimeout - ? "Upload timed out after 30 seconds" - : err instanceof Error - ? err.message - : "Upload failed" - log.error({ route: "POST /api/upload-image", host: hostId, error: message }, "upload failed") + log.error({ route: "POST /api/upload-image", host: hostId, error: String(err) }, "upload failed") return NextResponse.json( - { error: isTimeout ? message : "Upload failed" }, + { error: isTimeout ? "Upload timed out after 30 seconds" : "Image upload failed" }, { status: isTimeout ? 504 : 502 } ) } diff --git a/src/components/TrackerSettingsDialog.tsx b/src/components/TrackerSettingsDialog.tsx index 673b0e3b..33d698fb 100644 --- a/src/components/TrackerSettingsDialog.tsx +++ b/src/components/TrackerSettingsDialog.tsx @@ -13,8 +13,8 @@ import { Input } from "@/components/ui/Input" import { MaskedSecret } from "@/components/ui/MaskedSecret" import { QbtTagWarning } from "@/components/ui/QbtTagWarning" import { Sheet } from "@/components/ui/Sheet" -import { Tooltip } from "@/components/ui/Tooltip" import { Toggle } from "@/components/ui/Toggle" +import { Tooltip } from "@/components/ui/Tooltip" import { findRegistryEntry } from "@/data/tracker-registry" import type { TrackerSummary } from "@/types/api" diff --git a/src/components/tracker-detail/platform/MamHealthOverview.tsx b/src/components/tracker-detail/platform/MamHealthOverview.tsx index 6f6bceeb..83e0be42 100644 --- a/src/components/tracker-detail/platform/MamHealthOverview.tsx +++ b/src/components/tracker-detail/platform/MamHealthOverview.tsx @@ -34,18 +34,6 @@ const SEGMENTS = [ { key: "completed", label: "Completed", color: "var(--color-text-tertiary)" }, ] as const -function formatResult(msg: string): string { - switch (msg) { - case "Completed": - return "IP updated" - case "No change": - case "No Change": - return "No change needed" - default: - return msg - } -} - export function MamHealthOverview({ meta, seedingCount, @@ -81,10 +69,11 @@ export function MamHealthOverview({ } // ── Satisfaction data ─────────────────────────────────────────────────── - const hasCapacity = unsatisfiedLimit != null && unsatisfiedLimit > 0 - const satUsed = hasCapacity ? Math.min(unsatisfiedCount ?? 0, unsatisfiedLimit!) : 0 - const satPct = hasCapacity ? (satUsed / unsatisfiedLimit!) * 100 : 0 - const satRemaining = hasCapacity ? unsatisfiedLimit! - satUsed : 0 + const safeLimit = unsatisfiedLimit ?? 0 + const hasCapacity = safeLimit > 0 + const satUsed = hasCapacity ? Math.min(unsatisfiedCount ?? 0, safeLimit) : 0 + const satPct = hasCapacity ? (satUsed / safeLimit) * 100 : 0 + const satRemaining = hasCapacity ? safeLimit - satUsed : 0 let satColor = accentColor if (satPct >= 90) satColor = "var(--color-danger)" else if (satPct >= 70) satColor = "var(--color-warn)" @@ -187,7 +176,7 @@ export function MamHealthOverview({
Bonus Cap Reached - {seedbonus!.toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} + {(seedbonus ?? 0).toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} {wastePerDay > 0 && (

diff --git a/src/lib/__tests__/security.test.ts b/src/lib/__tests__/security.test.ts index 1d54d0a3..8bc6625e 100644 --- a/src/lib/__tests__/security.test.ts +++ b/src/lib/__tests__/security.test.ts @@ -1639,7 +1639,6 @@ describe("Backup restore authenticated flows", () => { expect(res.status).toBe(400) const data = await res.json() expect(data.error).toContain("Invalid or corrupted backup file") - expect(data.error).toContain("Unsupported backup version") }) it("POST /api/settings/backup/restore with no password returns 400", async () => { From 07e72a8d2181207f75adeac2b0a6a7ff76e4c808 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 17:30:24 -0500 Subject: [PATCH 55/59] docs: update MyAnonaMouse tracker link --- docs/kb/docs/contributing/tracker-responses-mam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kb/docs/contributing/tracker-responses-mam.md b/docs/kb/docs/contributing/tracker-responses-mam.md index 8eedd415..60b86d1f 100644 --- a/docs/kb/docs/contributing/tracker-responses-mam.md +++ b/docs/kb/docs/contributing/tracker-responses-mam.md @@ -358,4 +358,4 @@ Returns the caller's current IP, ASN, and AS organization name. Available on bot ## Supported Trackers -- [MyAnonaMouse](trackers/myanonamouse.md) *(to be created)* +- MyAnonaMouse From 8cff4ee6fabc274ca644aac65433a03fedbc0d53 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 17:33:00 -0500 Subject: [PATCH 56/59] fix: update VALID_PLATFORMS to use VALID_PLATFORM_TYPES constant --- scripts/validate-trackers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/validate-trackers.ts b/scripts/validate-trackers.ts index d83c44e3..ac4e7c28 100644 --- a/scripts/validate-trackers.ts +++ b/scripts/validate-trackers.ts @@ -12,9 +12,9 @@ import fs from "node:fs" import path from "node:path" import type { TrackerRegistryEntry } from "@/data/tracker-registry" import { ALL_TRACKERS } from "@/data/trackers" -import { DEFAULT_API_PATHS } from "@/lib/adapters/constants" +import { DEFAULT_API_PATHS, VALID_PLATFORM_TYPES } from "@/lib/adapters/constants" -const VALID_PLATFORMS = ["unit3d", "gazelle", "ggn", "nebulance", "custom"] as const +const VALID_PLATFORMS = [...VALID_PLATFORM_TYPES, "custom"] as const const HEX_COLOR_RE = /^#[0-9a-fA-F]{6}$/ const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/ const LOGO_NAME_RE = /^\/tracker-logos\/[a-z0-9_]+_logo\.(svg|png)$/ From 8cd588435824929eb7582a4f7353bc9f52cefb1a Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 17:44:44 -0500 Subject: [PATCH 57/59] chore: prettier --- CHANGELOG.md | 4 --- pnpm-lock.yaml | 16 +++++------ scripts/security-audit.ts | 13 ++++----- src/app/(auth)/settings/page.tsx | 8 +++++- .../trackers/[id]/TrackerDetailClient.tsx | 5 +--- src/app/api/settings/backup/restore/route.ts | 10 ++----- src/app/api/trackers/[id]/route.ts | 3 +- src/app/api/trackers/route.ts | 21 +++++++------- src/app/api/upload-image/route.ts | 5 +++- src/components/AddTrackerDialog.tsx | 9 ++++-- src/components/TrackerSettingsDialog.tsx | 5 +++- src/components/charts/MetricChart.tsx | 9 +++++- .../tracker-detail/TrackerDetailHeader.tsx | 8 ++---- .../tracker-detail/platform/MamBonusWaste.tsx | 4 +-- .../platform/MamHealthOverview.tsx | 13 +++++---- .../platform/MamTorrentHealth.tsx | 7 ++++- src/lib/adapters/mam.test.ts | 21 +++++++------- src/lib/notifications/dispatch.ts | 28 ++++++++++++++++--- src/lib/scheduler.ts | 25 +++++++++-------- src/types/api.ts | 7 ++++- 20 files changed, 128 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb3406a..74ee0759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## [2.5.0](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.5.0) (2026-03-26) - ### Features - add development image to docker hub ([8785094](https://github.com/jordanlambrecht/tracker-tracker/commit/87850942f118ccfd23c0a04d537928cd3db82976)) @@ -17,7 +16,6 @@ - **ui:** add pause/resume button ([c89299e](https://github.com/jordanlambrecht/tracker-tracker/commit/c89299ee80b9ce5ab22743039b6abd40be5a27b6)) - **ui:** lazy-load chart sections, prefetch sidebar links, and fix scroll-to-top on navigation ([ba8f59e](https://github.com/jordanlambrecht/tracker-tracker/commit/ba8f59ee33a21be72034f296f5da1ae795e03c70)) - ### Bug Fixes - **api:** improve session expiration error message ([5a95cd0](https://github.com/jordanlambrecht/tracker-tracker/commit/5a95cd039daf5aebb128f547acf70ae749132221)) @@ -37,13 +35,11 @@ - update workflow triggers to include development branch for pull requests ([d159775](https://github.com/jordanlambrecht/tracker-tracker/commit/d15977566a9dbd341c0db6f3b67e0ace9bb70f16)) - wrong postgres setup in docker-compose (closes [#78](https://github.com/jordanlambrecht/tracker-tracker/issues/78)) ([a0a3e0e](https://github.com/jordanlambrecht/tracker-tracker/commit/a0a3e0e16fe4e3b97dea9c7ebc5616cb54e22332)) - ### Performance - add 5s per-client fetch deadline ([558c4be](https://github.com/jordanlambrecht/tracker-tracker/commit/558c4be0f9b05b198fd09ca5df4aad0dc6cde637)) - **settings:** settings page optimizations ([63aabab](https://github.com/jordanlambrecht/tracker-tracker/commit/63aabab9afdfde3d492b81b86f581c4c035269d1)) - ### Refactoring - **charts:** consolidate duplicate Fleet/Torrent chart pairs and normalize upstream data flow ([ca051a8](https://github.com/jordanlambrecht/tracker-tracker/commit/ca051a8f4284565f6b0c09e4c9f0522a70ff7e2c)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e924bb75..e1afbeee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -367,8 +367,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1': - resolution: {integrity: sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==} + '@csstools/css-syntax-patches-for-csstree@1.1.2': + resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -2132,8 +2132,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true @@ -3672,7 +3672,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.1(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -4639,7 +4639,7 @@ snapshots: dependencies: conventional-commits-filter: 3.0.0 dateformat: 3.0.3 - handlebars: 4.7.8 + handlebars: 4.7.9 json-stringify-safe: 5.0.1 meow: 8.1.2 semver: 7.7.4 @@ -5011,7 +5011,7 @@ snapshots: graceful-fs@4.2.11: {} - handlebars@4.7.8: + handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -5156,7 +5156,7 @@ snapshots: '@asamuzakjp/css-color': 5.0.1 '@asamuzakjp/dom-selector': 7.0.4 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.1(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@2.0.1) diff --git a/scripts/security-audit.ts b/scripts/security-audit.ts index d7fe8435..34c13549 100644 --- a/scripts/security-audit.ts +++ b/scripts/security-audit.ts @@ -2257,16 +2257,14 @@ function checkErrorMessageDisclosure(files?: string[]): CheckResult { const window = lines.slice(i, windowEnd + 1).join("\n") const hasJsonResponse = - /NextResponse\.json\s*\(/.test(window) || - /return\s+new\s+Response/.test(window) + /NextResponse\.json\s*\(/.test(window) || /return\s+new\s+Response/.test(window) if (!hasJsonResponse) continue // Check: is the captured variable or err.message in the response? const varName = captureMatch?.[1] const varInResponse = varName - ? new RegExp(`\\b${varName}\\b`).test(window) && - /(?:error|message)\s*:/.test(window) + ? new RegExp(`\\b${varName}\\b`).test(window) && /(?:error|message)\s*:/.test(window) : /(?:err|error)\.message/.test(window) if (varInResponse) { @@ -2355,10 +2353,9 @@ const SECRET_ENV_VARS = [ function checkClientEnvLeak(): CheckResult { const findings: Finding[] = [] - const clientFiles = [ - ...walkFiles(SRC_DIR, ".tsx"), - ...walkFiles(SRC_DIR, ".ts"), - ].filter((f) => !isTestFile(f)) + const clientFiles = [...walkFiles(SRC_DIR, ".tsx"), ...walkFiles(SRC_DIR, ".ts")].filter( + (f) => !isTestFile(f) + ) for (const file of clientFiles) { const content = fs.readFileSync(file, "utf8") diff --git a/src/app/(auth)/settings/page.tsx b/src/app/(auth)/settings/page.tsx index 683de34b..5a16e35a 100644 --- a/src/app/(auth)/settings/page.tsx +++ b/src/app/(auth)/settings/page.tsx @@ -22,5 +22,11 @@ export default async function SettingsPage() { ) } - return + return ( + + ) } diff --git a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx index 0aa1ee70..a111b512 100644 --- a/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx +++ b/src/app/(auth)/trackers/[id]/TrackerDetailClient.tsx @@ -207,10 +207,7 @@ export function TrackerDetailClient({ const tc = tracker?.color || CHART_THEME.accent const baseUrl = tracker?.baseUrl - const registryEntry = useMemo( - () => (baseUrl ? findRegistryEntry(baseUrl) : undefined), - [baseUrl] - ) + const registryEntry = useMemo(() => (baseUrl ? findRegistryEntry(baseUrl) : undefined), [baseUrl]) const { statCardSlots, badgeSlots, progressSlots } = useMemo(() => { if (!tracker) return { statCardSlots: [], badgeSlots: [], progressSlots: [] } diff --git a/src/app/api/settings/backup/restore/route.ts b/src/app/api/settings/backup/restore/route.ts index c1e2aabe..262cbc1e 100644 --- a/src/app/api/settings/backup/restore/route.ts +++ b/src/app/api/settings/backup/restore/route.ts @@ -157,10 +157,7 @@ export async function POST(request: Request) { { route: "POST /api/settings/backup/restore", error: String(err) }, "backup file validation or decryption failed" ) - return NextResponse.json( - { error: "Invalid or corrupted backup file" }, - { status: 400 } - ) + return NextResponse.json({ error: "Invalid or corrupted backup file" }, { status: 400 }) } // Step 5: Get current settings for ID @@ -684,10 +681,7 @@ export async function POST(request: Request) { "Restore operation failed" ) - return NextResponse.json( - { error: "Backup restore failed" }, - { status: 409 } - ) + return NextResponse.json({ error: "Backup restore failed" }, { status: 409 }) } finally { if (backupKey.length > 0) backupKey.fill(0) if (currentKey !== backupKey && currentKey.length > 0) currentKey.fill(0) diff --git a/src/app/api/trackers/[id]/route.ts b/src/app/api/trackers/[id]/route.ts index 5d448306..1eacbeb8 100644 --- a/src/app/api/trackers/[id]/route.ts +++ b/src/app/api/trackers/[id]/route.ts @@ -90,8 +90,7 @@ export async function PATCH(request: Request, props: { params: Promise<{ id: str if (typeof body.useProxy === "boolean") updates.useProxy = body.useProxy if (typeof body.countCrossSeedUnsatisfied === "boolean") updates.countCrossSeedUnsatisfied = body.countCrossSeedUnsatisfied - if (typeof body.hideUnreadBadges === "boolean") - updates.hideUnreadBadges = body.hideUnreadBadges + if (typeof body.hideUnreadBadges === "boolean") updates.hideUnreadBadges = body.hideUnreadBadges if (typeof body.isFavorite === "boolean") updates.isFavorite = body.isFavorite if (typeof body.pollingPaused === "boolean") { updates.userPausedAt = body.pollingPaused ? new Date() : null diff --git a/src/app/api/trackers/route.ts b/src/app/api/trackers/route.ts index 51257764..08a50755 100644 --- a/src/app/api/trackers/route.ts +++ b/src/app/api/trackers/route.ts @@ -34,17 +34,16 @@ export async function POST(request: Request) { const body = await parseJsonBody(request) if (body instanceof NextResponse) return body - const { name, baseUrl, apiToken, platformType, color, qbtTag, mouseholeUrl, joinedAt } = - body as { - name?: string - baseUrl?: string - apiToken?: string - platformType?: string - color?: string - qbtTag?: string - mouseholeUrl?: string - joinedAt?: string - } + const { name, baseUrl, apiToken, platformType, color, qbtTag, mouseholeUrl, joinedAt } = body as { + name?: string + baseUrl?: string + apiToken?: string + platformType?: string + color?: string + qbtTag?: string + mouseholeUrl?: string + joinedAt?: string + } if ( typeof name !== "string" || diff --git a/src/app/api/upload-image/route.ts b/src/app/api/upload-image/route.ts index 958651bd..86d84d02 100644 --- a/src/app/api/upload-image/route.ts +++ b/src/app/api/upload-image/route.ts @@ -160,7 +160,10 @@ export async function POST(request: Request) { return NextResponse.json(result) } catch (err) { const isTimeout = err instanceof DOMException && err.name === "TimeoutError" - log.error({ route: "POST /api/upload-image", host: hostId, error: String(err) }, "upload failed") + log.error( + { route: "POST /api/upload-image", host: hostId, error: String(err) }, + "upload failed" + ) return NextResponse.json( { error: isTimeout ? "Upload timed out after 30 seconds" : "Image upload failed" }, { status: isTimeout ? 504 : 502 } diff --git a/src/components/AddTrackerDialog.tsx b/src/components/AddTrackerDialog.tsx index 9f0dc4c0..57bd114f 100644 --- a/src/components/AddTrackerDialog.tsx +++ b/src/components/AddTrackerDialog.tsx @@ -539,9 +539,14 @@ function AddTrackerDialog({ /> - + + ⓘ +

diff --git a/src/components/TrackerSettingsDialog.tsx b/src/components/TrackerSettingsDialog.tsx index 33d698fb..21df8056 100644 --- a/src/components/TrackerSettingsDialog.tsx +++ b/src/components/TrackerSettingsDialog.tsx @@ -305,7 +305,10 @@ function TrackerSettingsDialog({ open, tracker, onClose, onUpdated }: TrackerSet /> diff --git a/src/components/charts/MetricChart.tsx b/src/components/charts/MetricChart.tsx index ba8bfddf..75b8057b 100644 --- a/src/components/charts/MetricChart.tsx +++ b/src/components/charts/MetricChart.tsx @@ -40,7 +40,14 @@ import { useLogScale } from "./lib/useLogScale" // ── Types ── -type Metric = "ratio" | "buffer" | "seedbonus" | "seedingCount" | "dailyDelta" | "shareScore" | "freeleechTokens" +type Metric = + | "ratio" + | "buffer" + | "seedbonus" + | "seedingCount" + | "dailyDelta" + | "shareScore" + | "freeleechTokens" interface MetricConfig { label: string diff --git a/src/components/tracker-detail/TrackerDetailHeader.tsx b/src/components/tracker-detail/TrackerDetailHeader.tsx index 93fda677..948f1597 100644 --- a/src/components/tracker-detail/TrackerDetailHeader.tsx +++ b/src/components/tracker-detail/TrackerDetailHeader.tsx @@ -149,7 +149,8 @@ export function TrackerDetailHeader({
- {(registryEntry?.trackerHubSlug || (tracker.platformType === "mam" && tracker.mouseholeUrl)) && ( + {(registryEntry?.trackerHubSlug || + (tracker.platformType === "mam" && tracker.mouseholeUrl)) && (
{registryEntry?.trackerHubSlug && ( )} {tracker.platformType === "mam" && tracker.mouseholeUrl && ( - + )}
)} diff --git a/src/components/tracker-detail/platform/MamBonusWaste.tsx b/src/components/tracker-detail/platform/MamBonusWaste.tsx index 9d2a6d26..03af24ed 100644 --- a/src/components/tracker-detail/platform/MamBonusWaste.tsx +++ b/src/components/tracker-detail/platform/MamBonusWaste.tsx @@ -17,9 +17,7 @@ export function MamBonusWaste({ seedbonus, seedingCount }: MamBonusWasteProps) { return (
- - Bonus Cap Reached - + Bonus Cap Reached {seedbonus.toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} diff --git a/src/components/tracker-detail/platform/MamHealthOverview.tsx b/src/components/tracker-detail/platform/MamHealthOverview.tsx index 83e0be42..fdadaae0 100644 --- a/src/components/tracker-detail/platform/MamHealthOverview.tsx +++ b/src/components/tracker-detail/platform/MamHealthOverview.tsx @@ -91,9 +91,7 @@ export function MamHealthOverview({ // ── Bonus cap data ───────────────────────────────────────────────────── const atCap = seedbonus != null && seedbonus >= MAM_BONUS_CAP - const wastePerDay = atCap - ? Math.round(seedingCount * ESTIMATED_POINTS_PER_SEED_PER_HOUR * 24) - : 0 + const wastePerDay = atCap ? Math.round(seedingCount * ESTIMATED_POINTS_PER_SEED_PER_HOUR * 24) : 0 const hasVip = vipDays != null const hasHealth = healthTotal > 0 @@ -137,7 +135,9 @@ export function MamHealthOverview({
Torrent Health - + + ⓘ +
@@ -158,7 +158,10 @@ export function MamHealthOverview({ {SEGMENTS.map(({ key, label, color }) => { if (healthValues[key] <= 0) return null return ( - + = { seeding: Math.max(0, seedingCount - (meta.seedingHnrCount ?? 0)), seedingHnr: meta.seedingHnrCount ?? 0, diff --git a/src/lib/adapters/mam.test.ts b/src/lib/adapters/mam.test.ts index fceb420f..0c90758d 100644 --- a/src/lib/adapters/mam.test.ts +++ b/src/lib/adapters/mam.test.ts @@ -35,7 +35,14 @@ function mockMamResponse(overrides?: Partial>) { duplicates: { name: "Duplicates", count: 0, red: false, size: null }, reseed: { name: "Reseed", count: 0, inactive: 0, red: false }, ite: { name: "Tracker Errors", count: 1, latest: 1711000000 }, - notifs: { pms: 3, aboutToDropClient: 0, tickets: 1, waiting_tickets: 0, requests: 2, topics: 5 }, + notifs: { + pms: 3, + aboutToDropClient: 0, + tickets: 1, + waiting_tickets: 0, + requests: 2, + topics: 5, + }, ...overrides, } } @@ -215,11 +222,7 @@ describe("MamAdapter - auth", () => { json: async () => mockMamResponse(), } as Response) - await adapter.fetchStats( - "https://www.myanonamouse.net", - "my-session-cookie", - "/jsonLoad.php" - ) + await adapter.fetchStats("https://www.myanonamouse.net", "my-session-cookie", "/jsonLoad.php") const callOpts = fetchSpy.mock.calls[0][1] as RequestInit const headers = callOpts.headers as Record @@ -232,11 +235,7 @@ describe("MamAdapter - auth", () => { json: async () => mockMamResponse(), } as Response) - await adapter.fetchStats( - "https://www.myanonamouse.net", - "session-id", - "/jsonLoad.php" - ) + await adapter.fetchStats("https://www.myanonamouse.net", "session-id", "/jsonLoad.php") const calledUrl = fetchSpy.mock.calls[0][0] as string expect(calledUrl).toContain("snatch_summary") diff --git a/src/lib/notifications/dispatch.ts b/src/lib/notifications/dispatch.ts index 1ecdbc31..f1ec78c1 100644 --- a/src/lib/notifications/dispatch.ts +++ b/src/lib/notifications/dispatch.ts @@ -274,7 +274,13 @@ export function detectEvents( if (target.notifyBonusCap) { const capLimit = (thresholds?.bonusCapLimit as number | undefined) ?? MAM_BONUS_CAP - if (checkBonusCapReached(ctx.mamContext?.currentSeedbonus ?? null, ctx.mamContext?.previousSeedbonus ?? null, capLimit)) { + if ( + checkBonusCapReached( + ctx.mamContext?.currentSeedbonus ?? null, + ctx.mamContext?.previousSeedbonus ?? null, + capLimit + ) + ) { events.push("bonus_cap") } } @@ -288,13 +294,24 @@ export function detectEvents( if (target.notifyUnsatisfiedLimit) { const pct = (thresholds?.unsatisfiedLimitPercent as number | undefined) ?? 80 - if (checkUnsatisfiedLimitApproaching(ctx.mamContext?.unsatisfiedCount ?? null, ctx.mamContext?.unsatisfiedLimit ?? null, pct)) { + if ( + checkUnsatisfiedLimitApproaching( + ctx.mamContext?.unsatisfiedCount ?? null, + ctx.mamContext?.unsatisfiedLimit ?? null, + pct + ) + ) { events.push("unsatisfied_limit") } } if (target.notifyActiveHnrs) { - if (checkActiveHnrs(ctx.mamContext?.inactiveHnrCount ?? null, ctx.mamContext?.previousInactiveHnrCount ?? null)) { + if ( + checkActiveHnrs( + ctx.mamContext?.inactiveHnrCount ?? null, + ctx.mamContext?.previousInactiveHnrCount ?? null + ) + ) { events.push("active_hnrs") } } @@ -331,7 +348,10 @@ export function buildEventData( case "vip_expiring": return { vipUntil: ctx.mamContext?.vipUntil ?? null } case "unsatisfied_limit": - return { count: ctx.mamContext?.unsatisfiedCount ?? null, limit: ctx.mamContext?.unsatisfiedLimit ?? null } + return { + count: ctx.mamContext?.unsatisfiedCount ?? null, + limit: ctx.mamContext?.unsatisfiedLimit ?? null, + } case "active_hnrs": return { count: ctx.mamContext?.inactiveHnrCount ?? null } default: diff --git a/src/lib/scheduler.ts b/src/lib/scheduler.ts index c02008c0..e14c4714 100644 --- a/src/lib/scheduler.ts +++ b/src/lib/scheduler.ts @@ -146,9 +146,7 @@ export async function pollTracker( } const adapter = getAdapter(tracker.platformType) if (tracker.useProxy && !proxyAgent) { - throw new Error( - "Proxy required but not available, refusing to leak IP via direct connection" - ) + throw new Error("Proxy required but not available, refusing to leak IP via direct connection") } const fetchOptions = buildFetchOptions(tracker.baseUrl, { proxyAgent: tracker.useProxy ? proxyAgent : undefined, @@ -278,15 +276,18 @@ export async function pollTracker( trackerPausedAt: null, trackerJoinedAt: tracker.joinedAt ?? null, minimumRatio: findRegistryEntry(tracker.baseUrl)?.rules?.minimumRatio, - mamContext: tracker.platformType === "mam" ? { - currentSeedbonus: stats.seedbonus ?? null, - previousSeedbonus: previousSnapshot?.seedbonus ?? null, - vipUntil: mamMeta?.vipUntil ?? null, - unsatisfiedCount: mamMeta?.unsatisfiedCount ?? null, - unsatisfiedLimit: mamMeta?.unsatisfiedLimit ?? null, - inactiveHnrCount: stats.hitAndRuns ?? null, - previousInactiveHnrCount: previousSnapshot?.hitAndRuns ?? null, - } : undefined, + mamContext: + tracker.platformType === "mam" + ? { + currentSeedbonus: stats.seedbonus ?? null, + previousSeedbonus: previousSnapshot?.seedbonus ?? null, + vipUntil: mamMeta?.vipUntil ?? null, + unsatisfiedCount: mamMeta?.unsatisfiedCount ?? null, + unsatisfiedLimit: mamMeta?.unsatisfiedLimit ?? null, + inactiveHnrCount: stats.hitAndRuns ?? null, + previousInactiveHnrCount: previousSnapshot?.hitAndRuns ?? null, + } + : undefined, }, encryptionKey, enabledTargets diff --git a/src/types/api.ts b/src/types/api.ts index 1f3dafa5..fb781adf 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -54,7 +54,12 @@ export interface TrackerSummary { joinedAt: string | null lastAccessAt: string | null remoteUserId: number | null - platformMeta: GGnPlatformMeta | GazellePlatformMeta | NebulancePlatformMeta | MamPlatformMeta | null + platformMeta: + | GGnPlatformMeta + | GazellePlatformMeta + | NebulancePlatformMeta + | MamPlatformMeta + | null createdAt: string latestStats: TrackerLatestStats | null } From 815b479047fc956b965556cdcc01d39bc1ce4a33 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 19:50:32 -0500 Subject: [PATCH 58/59] fix: resolve lint warnings, Copilot review issues, remove dead code, and harden error handling --- Dockerfile | 4 +- src/app/api/clients/client-routes.test.ts | 2 +- src/app/api/trackers/[id]/mousehole/route.ts | 9 +++ src/app/api/trackers/[id]/route.ts | 5 +- src/app/api/trackers/route.ts | 5 +- .../tracker-detail/platform/MamBonusWaste.tsx | 31 -------- .../platform/MamSatisfactionProgress.tsx | 40 ----------- .../platform/MamTorrentHealth.tsx | 72 ------------------- .../platform/MamVipCountdown.tsx | 42 ----------- src/lib/__tests__/client-decrypt.test.ts | 35 +++++++-- src/lib/__tests__/notifications.test.ts | 2 +- src/lib/client-decrypt.ts | 12 +++- src/lib/formatters.ts | 4 ++ src/lib/notifications/dispatch.ts | 10 ++- src/lib/qbt/qbt.test.ts | 2 +- src/lib/qbt/types.ts | 2 +- src/lib/today.ts | 17 ++++- 17 files changed, 90 insertions(+), 204 deletions(-) delete mode 100644 src/components/tracker-detail/platform/MamBonusWaste.tsx delete mode 100644 src/components/tracker-detail/platform/MamSatisfactionProgress.tsx delete mode 100644 src/components/tracker-detail/platform/MamTorrentHealth.tsx delete mode 100644 src/components/tracker-detail/platform/MamVipCountdown.tsx diff --git a/Dockerfile b/Dockerfile index d1382d81..1d45ac35 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,8 +35,8 @@ RUN pnpm build # --------------------------------------------------------------------------- FROM base AS schema-deps WORKDIR /schema-sync -RUN printf '{"private":true,"pnpm":{"overrides":{"esbuild":">=0.25.0"}}}\n' > package.json \ - && pnpm add drizzle-kit@0.31.10 drizzle-orm@0.45.1 postgres@3.4.8 +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile # --------------------------------------------------------------------------- # Stage 4 — Production runner diff --git a/src/app/api/clients/client-routes.test.ts b/src/app/api/clients/client-routes.test.ts index c4f84fe9..a6af0ad7 100644 --- a/src/app/api/clients/client-routes.test.ts +++ b/src/app/api/clients/client-routes.test.ts @@ -108,7 +108,7 @@ const MOCK_TORRENTS = [ progress: 1, content_path: "/downloads/Show.S01.BluRay", save_path: "/downloads", - isPrivate: true, + is_private: true, }, ] diff --git a/src/app/api/trackers/[id]/mousehole/route.ts b/src/app/api/trackers/[id]/mousehole/route.ts index e78b4f37..41fdd3f5 100644 --- a/src/app/api/trackers/[id]/mousehole/route.ts +++ b/src/app/api/trackers/[id]/mousehole/route.ts @@ -45,6 +45,15 @@ async function resolveMouseholeBase( return NextResponse.json({ error: "Mousehole URL not configured" }, { status: 400 }) } + try { + const parsed = new URL(tracker.mouseholeUrl) + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + return NextResponse.json({ error: "Mousehole URL must use http or https" }, { status: 400 }) + } + } catch { + return NextResponse.json({ error: "Invalid Mousehole URL in database" }, { status: 400 }) + } + const mouseholeBase = tracker.mouseholeUrl.replace(/\/+$/, "") return { mouseholeBase } } diff --git a/src/app/api/trackers/[id]/route.ts b/src/app/api/trackers/[id]/route.ts index 1eacbeb8..bfd57ace 100644 --- a/src/app/api/trackers/[id]/route.ts +++ b/src/app/api/trackers/[id]/route.ts @@ -79,7 +79,10 @@ export async function PATCH(request: Request, props: { params: Promise<{ id: str const trimmed = body.mouseholeUrl.trim() if (trimmed) { try { - new URL(trimmed) + const parsed = new URL(trimmed) + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + return NextResponse.json({ error: "Mousehole URL must use http or https" }, { status: 400 }) + } } catch { return NextResponse.json({ error: "Invalid Mousehole URL format" }, { status: 400 }) } diff --git a/src/app/api/trackers/route.ts b/src/app/api/trackers/route.ts index 08a50755..fc2eacef 100644 --- a/src/app/api/trackers/route.ts +++ b/src/app/api/trackers/route.ts @@ -95,7 +95,10 @@ export async function POST(request: Request) { if (typeof mouseholeUrl === "string" && mouseholeUrl.trim()) { try { - new URL(mouseholeUrl.trim()) + const parsed = new URL(mouseholeUrl.trim()) + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + return NextResponse.json({ error: "Mousehole URL must use http or https" }, { status: 400 }) + } } catch { return NextResponse.json({ error: "Invalid Mousehole URL format" }, { status: 400 }) } diff --git a/src/components/tracker-detail/platform/MamBonusWaste.tsx b/src/components/tracker-detail/platform/MamBonusWaste.tsx deleted file mode 100644 index 03af24ed..00000000 --- a/src/components/tracker-detail/platform/MamBonusWaste.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// src/components/tracker-detail/platform/MamBonusWaste.tsx - -import { MAM_BONUS_CAP } from "@/lib/adapters/constants" - -export interface MamBonusWasteProps { - seedbonus: number - seedingCount: number -} -// Rough average — MAM's actual formula varies by torrent size, seeder count, etc. -const ESTIMATED_POINTS_PER_SEED_PER_HOUR = 0.5 - -export function MamBonusWaste({ seedbonus, seedingCount }: MamBonusWasteProps) { - if (seedbonus < MAM_BONUS_CAP) return null - - const wastePerHour = seedingCount * ESTIMATED_POINTS_PER_SEED_PER_HOUR - const wastePerDay = Math.round(wastePerHour * 24) - - return ( -
- Bonus Cap Reached - - {seedbonus.toLocaleString()} / {MAM_BONUS_CAP.toLocaleString()} - - {wastePerDay > 0 && ( -

- ~{wastePerDay.toLocaleString()} pts/day wasted -

- )} -
- ) -} diff --git a/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx b/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx deleted file mode 100644 index 5c3040e1..00000000 --- a/src/components/tracker-detail/platform/MamSatisfactionProgress.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// src/components/tracker-detail/platform/MamSatisfactionProgress.tsx - -import { ProgressBar } from "@/components/ui/ProgressBar" - -export interface MamSatisfactionProgressProps { - unsatisfiedCount: number - unsatisfiedLimit: number - accentColor: string -} - -export function MamSatisfactionProgress({ - unsatisfiedCount, - unsatisfiedLimit, - accentColor, -}: MamSatisfactionProgressProps) { - if (unsatisfiedLimit <= 0) return null - - const used = Math.min(unsatisfiedCount, unsatisfiedLimit) - const pct = (used / unsatisfiedLimit) * 100 - const remaining = unsatisfiedLimit - used - - let barColor = accentColor - if (pct >= 90) barColor = "var(--color-danger)" - else if (pct >= 70) barColor = "var(--color-warn)" - - return ( -
-
- Download Capacity - - {remaining} / {unsatisfiedLimit} slots - -
- -

- {used} unsatisfied torrent{used !== 1 ? "s" : ""} -

-
- ) -} diff --git a/src/components/tracker-detail/platform/MamTorrentHealth.tsx b/src/components/tracker-detail/platform/MamTorrentHealth.tsx deleted file mode 100644 index b01d2333..00000000 --- a/src/components/tracker-detail/platform/MamTorrentHealth.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// src/components/tracker-detail/platform/MamTorrentHealth.tsx - -import type { MamPlatformMeta } from "@/lib/adapters/types" - -export interface MamTorrentHealthProps { - meta: MamPlatformMeta - seedingCount: number - leechingCount: number - hitAndRuns: number -} - -const SEGMENTS = [ - { key: "seeding", label: "Seeding", color: "var(--color-success)" }, - { key: "seedingHnr", label: "Seeding HnR", color: "var(--color-warn)" }, - { key: "leeching", label: "Leeching", color: "var(--color-accent)" }, - { key: "inactiveHnr", label: "Inactive HnR", color: "var(--color-danger)" }, - { key: "preHnr", label: "Pre-HnR", color: "var(--color-warn)" }, - { key: "completed", label: "Completed", color: "var(--color-text-tertiary)" }, -] as const - -export function MamTorrentHealth({ - meta, - seedingCount, - leechingCount, - hitAndRuns, -}: MamTorrentHealthProps) { - const values: Record = { - seeding: Math.max(0, seedingCount - (meta.seedingHnrCount ?? 0)), - seedingHnr: meta.seedingHnrCount ?? 0, - leeching: leechingCount, - inactiveHnr: hitAndRuns, - preHnr: meta.inactiveUnsatisfiedCount ?? 0, - completed: meta.inactiveSatisfiedCount ?? 0, - } - - const total = Object.values(values).reduce((a, b) => a + b, 0) - if (total === 0) return null - - return ( -
- Torrent Health -
- {SEGMENTS.map(({ key, label, color }) => { - const pct = (values[key] / total) * 100 - if (pct <= 0) return null - return ( -
- ) - })} -
-
- {SEGMENTS.map(({ key, label, color }) => { - if (values[key] <= 0) return null - return ( - - - {label}: {values[key].toLocaleString()} - - ) - })} -
-
- ) -} diff --git a/src/components/tracker-detail/platform/MamVipCountdown.tsx b/src/components/tracker-detail/platform/MamVipCountdown.tsx deleted file mode 100644 index 58014c3a..00000000 --- a/src/components/tracker-detail/platform/MamVipCountdown.tsx +++ /dev/null @@ -1,42 +0,0 @@ -// src/components/tracker-detail/platform/MamVipCountdown.tsx - -import { ProgressBar } from "@/components/ui/ProgressBar" - -export interface MamVipCountdownProps { - vipUntil: string - accentColor: string -} - -export function MamVipCountdown({ vipUntil, accentColor }: MamVipCountdownProps) { - const expiry = new Date(vipUntil) - if (Number.isNaN(expiry.getTime())) return null - - const msRemaining = expiry.getTime() - Date.now() - if (msRemaining <= 0) return null - - const daysRemaining = Math.ceil(msRemaining / (1000 * 60 * 60 * 24)) - // VIP duration varies by donation tier. 90 days is a reasonable default for the progress bar - const totalDays = 90 - const pct = (daysRemaining / totalDays) * 100 - - let urgencyColor = accentColor - if (daysRemaining <= 3) urgencyColor = "var(--color-danger)" - else if (daysRemaining <= 7) urgencyColor = "var(--color-warn)" - - const dateStr = expiry.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - year: "numeric", - }) - - return ( -
-
- VIP Expires - {daysRemaining}d remaining -
- -

{dateStr}

-
- ) -} diff --git a/src/lib/__tests__/client-decrypt.test.ts b/src/lib/__tests__/client-decrypt.test.ts index 270f649f..3b0c6928 100644 --- a/src/lib/__tests__/client-decrypt.test.ts +++ b/src/lib/__tests__/client-decrypt.test.ts @@ -7,6 +7,7 @@ vi.mock("@/lib/crypto", () => ({ })) import { decrypt } from "@/lib/crypto" +import { isDecryptionError } from "@/lib/error-utils" const { decryptClientCredentials } = await import("@/lib/client-decrypt") @@ -18,13 +19,39 @@ describe("decryptClientCredentials", () => { expect(result).toEqual({ username: "decrypted:enc-user", password: "decrypted:enc-pass" }) }) - it("throws descriptive error when decrypt fails", () => { + it("throws an error that isDecryptionError() recognises when decrypt throws a crypto error", () => { + // "bad decrypt" matches the /bad\s*decrypt/i pattern in isDecryptionError + ;(decrypt as ReturnType).mockImplementation(() => { + throw new Error("bad decrypt") + }) + const client = { name: "MyClient", encryptedUsername: "x", encryptedPassword: "y" } + let thrown: unknown + expect(() => { + try { + decryptClientCredentials(client, Buffer.alloc(32)) + } catch (err) { + thrown = err + throw err + } + }).toThrow() + expect(isDecryptionError(thrown)).toBe(true) + }) + + it("throws an error that isDecryptionError() does NOT recognise for non-crypto failures", () => { + // "bad key" does not match any pattern in isDecryptionError ;(decrypt as ReturnType).mockImplementation(() => { throw new Error("bad key") }) const client = { name: "MyClient", encryptedUsername: "x", encryptedPassword: "y" } - expect(() => decryptClientCredentials(client, Buffer.alloc(32))).toThrow( - 'Failed to decrypt credentials for client "MyClient"' - ) + let thrown: unknown + expect(() => { + try { + decryptClientCredentials(client, Buffer.alloc(32)) + } catch (err) { + thrown = err + throw err + } + }).toThrow(/Failed to read credentials for client "MyClient"/) + expect(isDecryptionError(thrown)).toBe(false) }) }) diff --git a/src/lib/__tests__/notifications.test.ts b/src/lib/__tests__/notifications.test.ts index b5427903..287f357b 100644 --- a/src/lib/__tests__/notifications.test.ts +++ b/src/lib/__tests__/notifications.test.ts @@ -489,7 +489,7 @@ describe("buildEventData", () => { // Catches: the label argument being ignored due to parameter order confusion. const { buildEventData } = await import("@/lib/notifications/dispatch") const ctx = makeContext() - const data = buildEventData("anniversary", ctx, "3 Year Anniversary") + const data = buildEventData("anniversary", ctx, null, "3 Year Anniversary") expect(data.label).toBe("3 Year Anniversary") }) diff --git a/src/lib/client-decrypt.ts b/src/lib/client-decrypt.ts index 440fe1dc..6af2d763 100644 --- a/src/lib/client-decrypt.ts +++ b/src/lib/client-decrypt.ts @@ -5,6 +5,7 @@ import "server-only" import { decrypt } from "@/lib/crypto" +import { isDecryptionError } from "@/lib/error-utils" export function decryptClientCredentials( client: { name: string; encryptedUsername: string; encryptedPassword: string }, @@ -16,7 +17,16 @@ export function decryptClientCredentials( password: decrypt(client.encryptedPassword, key), } } catch (err) { + // Use "decrypt" prefix only for genuine AES-GCM auth failures so + // isDecryptionError() in callers correctly classifies key mismatches. const cause = err instanceof Error ? err.message : String(err) - throw new Error(`Failed to decrypt credentials for client "${client.name}": ${cause}`) + if (isDecryptionError(err)) { + throw new Error(`decrypt credentials failed for client "${client.name}": ${cause}`, { + cause: err, + }) + } + throw new Error(`Failed to read credentials for client "${client.name}": ${cause}`, { + cause: err, + }) } } diff --git a/src/lib/formatters.ts b/src/lib/formatters.ts index bfdc2c9a..aae13ecf 100644 --- a/src/lib/formatters.ts +++ b/src/lib/formatters.ts @@ -309,6 +309,10 @@ export function compareBigIntDesc(a: bigint, b: bigint): number { export function computePctChange(today: string, yesterday: string | null): number | null { if (yesterday === null) return null try { + // NOTE: Converting BigInt to Number can lose precision for values larger than + // Number.MAX_SAFE_INTEGER (~8 PiB). For typical tracker upload/download totals + // this is unlikely to be a problem, but a full bigint arithmetic refactor would + // be required to handle extreme values correctly. const y = Number(BigInt(yesterday)) if (y === 0) return null const t = Number(BigInt(today)) diff --git a/src/lib/notifications/dispatch.ts b/src/lib/notifications/dispatch.ts index f1ec78c1..0a650a81 100644 --- a/src/lib/notifications/dispatch.ts +++ b/src/lib/notifications/dispatch.ts @@ -117,6 +117,7 @@ export async function dispatchNotifications( const anniversaryLabel = events.includes("anniversary") ? checkAnniversaryMilestone(ctx.trackerJoinedAt)?.label : undefined + const targetThresholds = (target.thresholds as NotificationThresholds | null) ?? null // Only Discord is currently supported — skip unknown types if (target.type !== "discord") { @@ -151,7 +152,7 @@ export async function dispatchNotifications( trackerName: ctx.trackerName, includeTrackerName: target.includeTrackerName, storeUsernames: ctx.storeUsernames, - data: buildEventData(event, ctx, anniversaryLabel), + data: buildEventData(event, ctx, targetThresholds, anniversaryLabel), }) const result = await deliverDiscordWebhook(target.id, config.webhookUrl, [ @@ -322,6 +323,7 @@ export function detectEvents( export function buildEventData( event: NotificationEventType, ctx: SnapshotContext, + thresholds?: NotificationThresholds | null, anniversaryLabel?: string ): Record { switch (event) { @@ -343,8 +345,10 @@ export function buildEventData( return { newGroup: ctx.currentGroup, previousGroup: ctx.previousGroup } case "anniversary": return { label: anniversaryLabel ?? "Anniversary" } - case "bonus_cap": - return { currentBonus: ctx.mamContext?.currentSeedbonus ?? null, capLimit: MAM_BONUS_CAP } + case "bonus_cap": { + const effectiveCap = (thresholds?.bonusCapLimit as number | undefined) ?? MAM_BONUS_CAP + return { currentBonus: ctx.mamContext?.currentSeedbonus ?? null, capLimit: effectiveCap } + } case "vip_expiring": return { vipUntil: ctx.mamContext?.vipUntil ?? null } case "unsatisfied_limit": diff --git a/src/lib/qbt/qbt.test.ts b/src/lib/qbt/qbt.test.ts index 53850c82..fdd6bd31 100644 --- a/src/lib/qbt/qbt.test.ts +++ b/src/lib/qbt/qbt.test.ts @@ -165,7 +165,7 @@ describe("getTorrents", () => { progress: 1, content_path: "/downloads/My.Show.S01.BluRay", save_path: "/downloads", - isPrivate: true, + is_private: true, }, ] diff --git a/src/lib/qbt/types.ts b/src/lib/qbt/types.ts index 5306d694..b86ec143 100644 --- a/src/lib/qbt/types.ts +++ b/src/lib/qbt/types.ts @@ -29,7 +29,7 @@ export interface QbtTorrent { progress: number // float 0-1, download progress content_path: string // full path to content save_path: string // save directory - isPrivate?: boolean // qBT returns this as is_private (snake_case) — may be undefined in raw API responses + is_private?: boolean // qBT API returns this field in snake_case } // From GET /api/v2/transfer/info diff --git a/src/lib/today.ts b/src/lib/today.ts index 6d6f5981..ea8da1df 100644 --- a/src/lib/today.ts +++ b/src/lib/today.ts @@ -196,7 +196,10 @@ export async function computeTodayAtAGlance(): Promise { fleetSeedbonusChange += delta.seedbonusChange if (delta.uploadDelta > 0n) { - // Convert upload delta to a Number for the weight + // NOTE: Number(delta.uploadDelta) can lose precision when uploadDelta + // exceeds Number.MAX_SAFE_INTEGER (~8 PiB). For realistic tracker upload + // volumes this is unlikely, but a full bigint weighted-average refactor + // would eliminate this limitation if ever needed. const weight = Number(delta.uploadDelta) weightedRatioSum += delta.ratioChange * weight totalUploadWeight += delta.uploadDelta @@ -289,9 +292,10 @@ export async function computeTodayAtAGlance(): Promise { if (torrent.tags) { const torrentTags = parseTorrentTags(torrent.tags) for (const tag of torrentTags) { - if (trackerTagToColor.has(tag)) { + const tagLower = tag.toLowerCase() + if (trackerTagToColor.has(tagLower)) { matchedTag = tag - matchedColor = trackerTagToColor.get(tag) ?? null + matchedColor = trackerTagToColor.get(tagLower) ?? null break } } @@ -436,10 +440,17 @@ export async function backfillTrackerCheckpoints(): Promise { bufferBytesEnd: row.bufferBytesEnd, ratioEnd: row.ratioEnd != null ? Number(row.ratioEnd) : null, seedbonusEnd: row.seedbonusEnd != null ? Number(row.seedbonusEnd) : null, + // snapshotCount is hard-coded to 1 because backfill selects only the + // last snapshot per day — the actual count is not available without a + // separate COUNT query per (trackerId, date) pair. Acceptable for backfill. snapshotCount: 1, })) ) .onConflictDoNothing() + // NOTE: inserted tracks chunk.length rather than actual DB rows written. + // onConflictDoNothing() silently skips duplicate rows, so this count may + // overstate the number of rows inserted. The return value is informational + // only (used for a log.info call) and does not affect correctness. inserted += chunk.length } From dd7e53692c07b99285b4036cb0b383f17371db16 Mon Sep 17 00:00:00 2001 From: Jordy Date: Thu, 26 Mar 2026 21:50:33 -0500 Subject: [PATCH 59/59] chore(release): 2.6.0 --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74ee0759..e947f16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## [2.6.0](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.4.1...v2.6.0) (2026-03-27) + + +### Features + +- **dashboard:** add Today At A Glance server logic, checkpoints, and deep poll fixes ([d3826a1](https://github.com/jordanlambrecht/tracker-tracker/commit/d3826a181c017c6b2ac8bbc6fa9d77f6dfd0edc2)) +- **dashboard:** add Today At A Glance UI ([84c5227](https://github.com/jordanlambrecht/tracker-tracker/commit/84c522750f517c95653b73e1298aa72bd204e27f)) +- **mam:** add bonus cap, VIP expiry, unsatisfied limit, and active HnR notifications ([2b210d7](https://github.com/jordanlambrecht/tracker-tracker/commit/2b210d76542db6a7377c13d7e9a9abf51cc48752)) +- **mam:** add Mousehole integration ([0e28443](https://github.com/jordanlambrecht/tracker-tracker/commit/0e28443875c1de70b1dea0f3662ae9625000da0f)) +- **mam:** add MyAnonaMouse adapter ([692d312](https://github.com/jordanlambrecht/tracker-tracker/commit/692d3124cd648a3f1278b38881df5083f7febdc1)) +- **mam:** add platform UI with health overview, badges, and FL Wedges chart ([3bb280e](https://github.com/jordanlambrecht/tracker-tracker/commit/3bb280e302ae4f4afeab667b912f5cea0e6f501c)) +- **schema:** add daily checkpoint tables and TodayAtAGlance types ([636d227](https://github.com/jordanlambrecht/tracker-tracker/commit/636d22701cd43d8827f9afe49a5105820578f892)) +- **security:** enhance security audit checks and improve vulnerability reporting ([23b4cae](https://github.com/jordanlambrecht/tracker-tracker/commit/23b4cae6420d17a1e1f86e9b925841726e28ccfe)) +- **settings:** display database size ([67ff496](https://github.com/jordanlambrecht/tracker-tracker/commit/67ff4961e5d6ba50655ed0a9229ba807a26e1bbe)) + + +### Bug Fixes + +- **api:** improve session expiration error message ([5a95cd0](https://github.com/jordanlambrecht/tracker-tracker/commit/5a95cd039daf5aebb128f547acf70ae749132221)) +- **auth:** decouple cookie secure flag from node_env for self-hosted http deployments. Closes [#101](https://github.com/jordanlambrecht/tracker-tracker/issues/101) ([b2a7902](https://github.com/jordanlambrecht/tracker-tracker/commit/b2a790245ca76f1ec3ef8220c273a4ab9ca508fd)) +- **auth:** return 401 on stale session instead of misleading credential errors ([cf54c7f](https://github.com/jordanlambrecht/tracker-tracker/commit/cf54c7fbd6c2d9171b5d11b621e9a1f68abc381a)) +- **backups:** enforce maximum length for backup password to 128 characters ([5e6d58e](https://github.com/jordanlambrecht/tracker-tracker/commit/5e6d58e361fbcfa6323e414b3702768d895d85d7)) +- **Dockerfile:** update package.json for drizzle-kit with esbuild overrides ([bce0854](https://github.com/jordanlambrecht/tracker-tracker/commit/bce0854d8ea18cf4a99488ed6d9243b25fc71658)) +- ensure backfill flag is set after successful checkpoint backfill ([60f5786](https://github.com/jordanlambrecht/tracker-tracker/commit/60f5786134f346e4ba07684d210f3806bd384468)) +- error logging for BigInt conversion failures ([e91b30a](https://github.com/jordanlambrecht/tracker-tracker/commit/e91b30a79605ac73c34c39e372ad2aaca4c09c44)) +- error logging for BigInt conversion failures in computeTodayAtAGlance ([15eb043](https://github.com/jordanlambrecht/tracker-tracker/commit/15eb043989fa2611acc846032aae1e049fef7000)) +- **errors:** improve error handling and logging for backup and tracker operations ([7f7b202](https://github.com/jordanlambrecht/tracker-tracker/commit/7f7b202e2557d25cd011c8bd96e14332f3565bb1)) +- **Icons:** update DownloadArrowIcon stroke width ([d2cd450](https://github.com/jordanlambrecht/tracker-tracker/commit/d2cd450af0b6cf55bc5fc1c94ea01452c55252fb)) +- improve error handling for decryption failures in fetchAndMergeTorrents ([0b07d40](https://github.com/jordanlambrecht/tracker-tracker/commit/0b07d40ddaca93f188eeb166e53431764cb1bb8e)) +- normalize tracker tags to lowercase ([762988f](https://github.com/jordanlambrecht/tracker-tracker/commit/762988f2228218cf27b2d12c138bb0e2dfa1c5b1)) +- optimize torrent checkpoint insertion by batching database writes ([90285d6](https://github.com/jordanlambrecht/tracker-tracker/commit/90285d69b1dd709d0f77682ecb797a20fe3fea1c)) +- resolve lint warnings, Copilot review issues, remove dead code, and harden error handling ([815b479](https://github.com/jordanlambrecht/tracker-tracker/commit/815b479047fc956b965556cdcc01d39bc1ce4a33)) +- **ui:** prevent StatCard DOM prop leak ([2d0b22a](https://github.com/jordanlambrecht/tracker-tracker/commit/2d0b22aa51b1badeb8916ace9505fdf32f526dc9)) +- update drizzle-kit, drizzle-orm, and postgres to specific versions in Dockerfile ([303c6f5](https://github.com/jordanlambrecht/tracker-tracker/commit/303c6f5b21195a9a15a66f227dab4c022eca36b9)) +- update VALID_PLATFORMS to use VALID_PLATFORM_TYPES constant ([8cff4ee](https://github.com/jordanlambrecht/tracker-tracker/commit/8cff4ee6fabc274ca644aac65433a03fedbc0d53)) +- use localDateStr for cutoff date in pruneOldCheckpoints function ([0b465b6](https://github.com/jordanlambrecht/tracker-tracker/commit/0b465b6c139333ebcb8650fcf8224d5a70076bee)) + + +### Refactoring + +- **Dockerfile:** cleaned up build stages ([3b96ff9](https://github.com/jordanlambrecht/tracker-tracker/commit/3b96ff964058f33d3fe8fd65bf7a6fcde9dbcd3b)) +- reuse ProgressBar component and extract slot-label utility ([c3d9031](https://github.com/jordanlambrecht/tracker-tracker/commit/c3d90315d4ca8717f983a3d270d922cc0355de18)) + ## [2.5.0](https://github.com/jordanlambrecht/tracker-tracker/compare/v2.3.0...v2.5.0) (2026-03-26) ### Features diff --git a/package.json b/package.json index e4e568cb..37acf9ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "private-tracker-tracker", - "version": "2.5.0", + "version": "2.6.0", "description": "Self-hosted dashboard for monitoring private tracker stats over time", "license": "GPL-3.0", "repository": {