diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..b1a1b31 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3934 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@aws-sdk/client-s3': + specifier: ^3.500.0 + version: 3.1116.0 + '@getgitops/gitdb': + specifier: ^0.2.0 + version: 0.2.0 + '@google-cloud/storage': + specifier: ^7.21.0 + version: 7.22.0(supports-color@7.2.0) + chart.js: + specifier: ^4.5.1 + version: 4.5.1 + lucide-svelte: + specifier: ^0.320.0 + version: 0.320.0(svelte@4.2.20) + devDependencies: + '@eslint/js': + specifier: ^9.14.0 + version: 9.39.5 + '@sveltejs/adapter-auto': + specifier: ^3.0.0 + version: 3.3.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(svelte@4.2.20)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0))) + '@sveltejs/kit': + specifier: ^2.5.0 + version: 2.70.3(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(svelte@4.2.20)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^3.0.0 + version: 3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + '@tailwindcss/postcss': + specifier: ^4.3.2 + version: 4.3.3 + '@types/better-sqlite3': + specifier: ^7.6.13 + version: 7.6.13 + '@types/node': + specifier: ^22.10.1 + version: 22.20.1 + '@typescript-eslint/eslint-plugin': + specifier: ^8.17.0 + version: 8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.17.0 + version: 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@vitest/coverage-v8': + specifier: ^4.1.11 + version: 4.1.11(vitest@4.1.11) + autoprefixer: + specifier: ^10.5.3 + version: 10.5.4(postcss@8.5.26) + eslint: + specifier: ^9.14.0 + version: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-config-prettier: + specifier: ^9.0.0 + version: 9.1.2(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + eslint-plugin-svelte: + specifier: ^2.0.0 + version: 2.46.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(svelte@4.2.20) + globals: + specifier: ^15.13.0 + version: 15.15.0 + husky: + specifier: ^9.1.0 + version: 9.1.7 + postcss: + specifier: ^8.5.19 + version: 8.5.26 + prettier: + specifier: ^3.3.0 + version: 3.9.6 + prettier-plugin-svelte: + specifier: ^3.0.0 + version: 3.5.2(prettier@3.9.6)(svelte@4.2.20) + svelte: + specifier: ^4.2.20 + version: 4.2.20 + svelte-check: + specifier: ^4.7.2 + version: 4.7.6(picomatch@4.0.7)(svelte@4.2.20)(typescript@5.9.3) + svelte-eslint-parser: + specifier: ^0.25.0 + version: 0.25.1(svelte@4.2.20) + tailwindcss: + specifier: ^4.3.2 + version: 4.3.3 + typescript: + specifier: ^5.6.3 + version: 5.9.3 + vite: + specifier: ^5.4.10 + version: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + vitest: + specifier: ^4.1.11 + version: 4.1.11(@types/node@22.20.1)(@vitest/coverage-v8@4.1.11)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@aws-sdk/checksums@3.1000.29': + resolution: {integrity: sha512-Dtu0gr4dnATZAPwEYbpCsG+MpLM7OAliy2gTepEFQwl1vZ6DL3QMH2FveMa3HLvPsOdhJsPRB3KtxVhph9T75A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/client-s3@3.1116.0': + resolution: {integrity: sha512-UKRl9qSVW0rZpvSOauQNpYAy8+ONBAVYnpfKVtCyOF+FZVT1tl6MunYuHvuarCrroD2/YJs+tHTALYNgAlec3Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.977.9': + resolution: {integrity: sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.70': + resolution: {integrity: sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.72': + resolution: {integrity: sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.15': + resolution: {integrity: sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.77': + resolution: {integrity: sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.81': + resolution: {integrity: sha512-Rml+WitoFvXmv6JZ18U/xGdGDGGvB/mOin0ya0lTnTrdC0Z1lrVxTYh7iNklZBcvcRMrs4DoEf6xy1KWyrLQQw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.70': + resolution: {integrity: sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.14': + resolution: {integrity: sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.76': + resolution: {integrity: sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.972.75': + resolution: {integrity: sha512-wMIsNumRVKaNMKhvU/s9VrdEwE8S6gSzXp4RygFG5BEMnGkkXf8cjh8zf7cKJBpUDpqTWqwbz5isEgp9rH6Lng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/nested-clients@3.997.44': + resolution: {integrity: sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.46': + resolution: {integrity: sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1116.0': + resolution: {integrity: sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.974.5': + resolution: {integrity: sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.40': + resolution: {integrity: sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@getgitops/gitdb@0.2.0': + resolution: {integrity: sha512-31tPFQQR4wA8U/pDYF3KkjoIH5kga7jfBIEPyDbew1h0kHSwhDlKIDOHct0LsBtwkkHSBmsbVTiqsFQaokn+6w==} + engines: {node: '>=20'} + + '@google-cloud/paginator@5.0.2': + resolution: {integrity: sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==} + engines: {node: '>=14.0.0'} + + '@google-cloud/projectify@4.0.0': + resolution: {integrity: sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==} + engines: {node: '>=14.0.0'} + + '@google-cloud/promisify@4.0.0': + resolution: {integrity: sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==} + engines: {node: '>=14'} + + '@google-cloud/storage@7.22.0': + resolution: {integrity: sha512-W98gTQOAntEeEQ7/pZxSQXxfUO5CiQcXJRsxBpUa6UU25n8YN/VtogPBi0H+MAmUrn/6bY/sBhFansoWEsr2/g==} + engines: {node: '>=18'} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@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==} + + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@nodable/entities@3.0.0': + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/rollup-android-arm-eabi@4.62.5': + resolution: {integrity: sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.5': + resolution: {integrity: sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.5': + resolution: {integrity: sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.5': + resolution: {integrity: sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.5': + resolution: {integrity: sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.5': + resolution: {integrity: sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.5': + resolution: {integrity: sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.5': + resolution: {integrity: sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.5': + resolution: {integrity: sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.5': + resolution: {integrity: sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.5': + resolution: {integrity: sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.5': + resolution: {integrity: sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.5': + resolution: {integrity: sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.5': + resolution: {integrity: sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.5': + resolution: {integrity: sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.5': + resolution: {integrity: sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.5': + resolution: {integrity: sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.5': + resolution: {integrity: sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.5': + resolution: {integrity: sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.5': + resolution: {integrity: sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.5': + resolution: {integrity: sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.5': + resolution: {integrity: sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.5': + resolution: {integrity: sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.5': + resolution: {integrity: sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.5': + resolution: {integrity: sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg==} + cpu: [x64] + os: [win32] + + '@smithy/core@3.33.3': + resolution: {integrity: sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.7.2': + resolution: {integrity: sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.11.3': + resolution: {integrity: sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.7.3': + resolution: {integrity: sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.17.2': + resolution: {integrity: sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==} + engines: {node: '>=18.0.0'} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@sveltejs/acorn-typescript@1.0.13': + resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@3.3.1': + resolution: {integrity: sha512-5Sc7WAxYdL6q9j/+D0jJKjGREGlfIevDyHSQ2eNETHcB1TKlQWHcAo8AS8H1QdjNvSXpvOwNjykDUHPEAyGgdQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.70.3': + resolution: {integrity: sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + + '@sveltejs/load-config@0.2.3': + resolution: {integrity: sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==} + engines: {node: '>= 18.0.0'} + + '@sveltejs/vite-plugin-svelte-inspector@2.1.0': + resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} + engines: {node: ^18.0.0 || >=20} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.0 + + '@sveltejs/vite-plugin-svelte@3.1.2': + resolution: {integrity: sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==} + engines: {node: ^18.0.0 || >=20} + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.0 + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.3': + resolution: {integrity: sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==} + + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + engines: {node: '>= 10'} + + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + + '@types/caseless@0.12.5': + resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@types/request@2.48.13': + resolution: {integrity: sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@typescript-eslint/eslint-plugin@8.67.0': + resolution: {integrity: sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.67.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.67.0': + resolution: {integrity: sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.67.0': + resolution: {integrity: sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.67.0': + resolution: {integrity: sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.67.0': + resolution: {integrity: sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.67.0': + resolution: {integrity: sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.67.0': + resolution: {integrity: sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.67.0': + resolution: {integrity: sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.67.0': + resolution: {integrity: sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitest/coverage-v8@4.1.11': + resolution: {integrity: sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==} + peerDependencies: + '@vitest/browser': 4.1.11 + vitest: 4.1.11 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anynum@1.0.1: + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.5: + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.5.4: + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.11.19: + resolution: {integrity: sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==} + engines: {node: '>=6.0.0'} + hasBin: true + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + brace-expansion@1.1.18: + resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chart.js@4.5.1: + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + engines: {pnpm: '>=8'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.9.1: + resolution: {integrity: sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + electron-to-chromium@1.5.413: + resolution: {integrity: sha512-F1XPKvt7HVfly5WND90ec16nFsdr4g5x/cVUP3EqjeyXynupabGDqpMa84wwvuYGDnldXLBz6DLXyZXWO9TPvw==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-svelte@2.46.1: + resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-xml-builder@1.3.1: + resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==} + + fast-xml-parser@5.11.0: + resolution: {integrity: sha512-9IGxMqvqLOnqP+Egi1nqDHKv5k8aZ7r9n558enxcucmyVGEBNPAU+MOg/8jPIS7rO7sSq4gFm1/nHtiaubMruw==} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + + form-data@2.5.6: + resolution: {integrity: sha512-Ogz/E85h9tlfJzpI6TuFpGcHZFhLrb9Gw8wq9v40CxSCPnv7ahKr6Xgtkn0KYCDQJ8DNn5VoMO8EXr9V5PadyA==} + engines: {node: '>= 0.12'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fsevents@2.3.3: + 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==} + + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + google-auth-library@9.15.1: + resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} + engines: {node: '>=14'} + + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + gtoken@7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-unsafe@2.0.2: + resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.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'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.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'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.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'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.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'} + 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'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.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'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lucide-svelte@0.320.0: + resolution: {integrity: sha512-Xycr1wFIPIEsFMvY6TkVKdzzA2XMdqO4BEy2upvCGmoRUwJQgKrPFW3D5vI7VcN8hy91trZ1levO9pHpu3vlVw==} + deprecated: Package deprecated. Please use @lucide/svelte instead. + peerDependencies: + svelte: '>=3 <5' + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.4: + resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-expression-matcher@1.6.2: + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} + engines: {node: '>=14.0.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.1.4: + resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.5.2: + resolution: {integrity: sha512-ItFouLvzSFE3ulNl4DKoWM3BGcbDCNVpIyy/Y3F2gC3aNiGLxtFUdffVqO5Z5hhYG+DFT5KULWaxmeFFpdbvaQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + retry-request@7.0.2: + resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} + engines: {node: '>=14'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + rollup@4.62.5: + resolution: {integrity: sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + stream-events@1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strnum@2.4.2: + resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} + + stubs@3.0.0: + resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + svelte-check@4.7.6: + resolution: {integrity: sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.0.0 || ^6.0.0 + + svelte-eslint-parser@0.25.1: + resolution: {integrity: sha512-so8+Een0Gmt+c7Fw0POu2UKbsVYvPys0cM0WGFgGbYtIiIvJ55s6Ud4+jnqjIAL5UxKmSt2v3z367gjodYwdqg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte-eslint-parser@0.43.0: + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte-hmr@0.16.0: + resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + + svelte@4.2.20: + resolution: {integrity: sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==} + engines: {node: '>=16'} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + teeny-request@9.0.0: + resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} + engines: {node: '>=14'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.3.1: + resolution: {integrity: sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitefu@0.2.5: + resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + vite: + optional: true + + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} + 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.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + 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'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xml-naming@0.3.0: + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} + engines: {node: '>=16.0.0'} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@aws-sdk/checksums@3.1000.29': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/client-s3@3.1116.0': + dependencies: + '@aws-sdk/checksums': 3.1000.29 + '@aws-sdk/core': 3.977.9 + '@aws-sdk/credential-provider-node': 3.972.81 + '@aws-sdk/middleware-sdk-s3': 3.972.75 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.11.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/core@3.977.9': + dependencies: + '@aws-sdk/types': 3.974.5 + '@aws-sdk/xml-builder': 3.972.40 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.33.3 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.17.2 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.70': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.72': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.11.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.15': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/credential-provider-env': 3.972.70 + '@aws-sdk/credential-provider-http': 3.972.72 + '@aws-sdk/credential-provider-login': 3.972.77 + '@aws-sdk/credential-provider-process': 3.972.70 + '@aws-sdk/credential-provider-sso': 3.973.14 + '@aws-sdk/credential-provider-web-identity': 3.972.76 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.77': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.81': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.70 + '@aws-sdk/credential-provider-http': 3.972.72 + '@aws-sdk/credential-provider-ini': 3.973.15 + '@aws-sdk/credential-provider-process': 3.972.70 + '@aws-sdk/credential-provider-sso': 3.973.14 + '@aws-sdk/credential-provider-web-identity': 3.972.76 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.70': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.14': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/token-providers': 3.1116.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.76': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-sdk-s3@3.972.75': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.44': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/fetch-http-handler': 5.7.2 + '@smithy/node-http-handler': 4.11.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.46': + dependencies: + '@aws-sdk/types': 3.974.5 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1116.0': + dependencies: + '@aws-sdk/core': 3.977.9 + '@aws-sdk/nested-clients': 3.997.44 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/types@3.974.5': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.40': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.3.0': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))': + dependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2(supports-color@7.2.0)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.6(supports-color@7.2.0)': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@7.2.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.3.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.5': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@getgitops/gitdb@0.2.0': {} + + '@google-cloud/paginator@5.0.2': + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + + '@google-cloud/projectify@4.0.0': {} + + '@google-cloud/promisify@4.0.0': {} + + '@google-cloud/storage@7.22.0(supports-color@7.2.0)': + dependencies: + '@google-cloud/paginator': 5.0.2 + '@google-cloud/projectify': 4.0.0 + '@google-cloud/promisify': 4.0.0 + abort-controller: 3.0.0 + async-retry: 1.3.3 + duplexify: 4.1.3 + fast-xml-parser: 5.11.0 + gaxios: 6.7.1(supports-color@7.2.0) + google-auth-library: 9.15.1(supports-color@7.2.0) + html-entities: 2.6.0 + mime: 3.0.0 + p-limit: 3.1.0 + retry-request: 7.0.2(supports-color@7.2.0) + teeny-request: 9.0.0(supports-color@7.2.0) + transitivePeerDependencies: + - encoding + - supports-color + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@kurkle/color@0.3.4': {} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@nodable/entities@3.0.0': {} + + '@polka/url@1.0.0-next.29': {} + + '@rollup/rollup-android-arm-eabi@4.62.5': + optional: true + + '@rollup/rollup-android-arm64@4.62.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.5': + optional: true + + '@rollup/rollup-darwin-x64@4.62.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.5': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.5': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.5': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.5': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.5': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.5': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.5': + optional: true + + '@smithy/core@3.33.3': + dependencies: + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.7.2': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.11.3': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/signature-v4@5.7.3': + dependencies: + '@smithy/core': 3.33.3 + '@smithy/types': 4.17.2 + tslib: 2.8.1 + + '@smithy/types@4.17.2': + dependencies: + tslib: 2.8.1 + + '@standard-schema/spec@1.1.0': {} + + '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': + dependencies: + acorn: 8.18.0 + + '@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(svelte@4.2.20)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))': + dependencies: + '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(svelte@4.2.20)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + import-meta-resolve: 4.2.0 + + '@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(svelte@4.2.20)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) + '@sveltejs/vite-plugin-svelte': 3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + '@types/cookie': 0.6.0 + acorn: 8.18.0 + cookie: 0.6.0 + devalue: 5.9.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.2 + sirv: 3.0.2 + svelte: 4.2.20 + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + optionalDependencies: + typescript: 5.9.3 + + '@sveltejs/load-config@0.2.3': {} + + '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + debug: 4.4.3(supports-color@7.2.0) + svelte: 4.2.20 + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)))(supports-color@7.2.0)(svelte@4.2.20)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + debug: 4.4.3(supports-color@7.2.0) + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.21 + svelte: 4.2.20 + svelte-hmr: 0.16.0(svelte@4.2.20) + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + vitefu: 0.2.5(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-arm64': 4.3.3 + '@tailwindcss/oxide-darwin-x64': 4.3.3 + '@tailwindcss/oxide-freebsd-x64': 4.3.3 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.3 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.3 + '@tailwindcss/oxide-linux-x64-musl': 4.3.3 + '@tailwindcss/oxide-wasm32-wasi': 4.3.3 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 + + '@tailwindcss/postcss@4.3.3': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + postcss: 8.5.26 + tailwindcss: 4.3.3 + + '@tootallnate/once@2.0.1': {} + + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 22.20.1 + + '@types/caseless@0.12.5': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/cookie@0.6.0': {} + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/request@2.48.13': + dependencies: + '@types/caseless': 0.12.5 + '@types/node': 22.20.1 + '@types/tough-cookie': 4.0.5 + form-data: 2.5.6 + + '@types/tough-cookie@4.0.5': {} + + '@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/type-utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.67.0(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@5.9.3) + '@typescript-eslint/types': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + + '@typescript-eslint/tsconfig-utils@8.67.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.67.0': {} + + '@typescript-eslint/typescript-estree@8.67.0(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.67.0(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.67.0(typescript@5.9.3) + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/visitor-keys': 8.67.0 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.67.0(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@typescript-eslint/scope-manager': 8.67.0 + '@typescript-eslint/types': 8.67.0 + '@typescript-eslint/typescript-estree': 8.67.0(supports-color@7.2.0)(typescript@5.9.3) + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.67.0': + dependencies: + '@typescript-eslint/types': 8.67.0 + eslint-visitor-keys: 5.0.1 + + '@vitest/coverage-v8@4.1.11(vitest@4.1.11)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.11 + ast-v8-to-istanbul: 1.0.5 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.4 + obug: 2.1.4 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 4.1.11(@types/node@22.20.1)(@vitest/coverage-v8@4.1.11)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + + '@vitest/expect@4.1.11': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.11(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0))': + dependencies: + '@vitest/spy': 4.1.11 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + + '@vitest/pretty-format@4.1.11': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.11': + dependencies: + '@vitest/utils': 4.1.11 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.11': {} + + '@vitest/utils@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + + acorn@8.18.0: {} + + agent-base@6.0.2(supports-color@7.2.0): + dependencies: + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anynum@1.0.1: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + arrify@2.0.1: {} + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.5: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + asynckit@0.4.0: {} + + autoprefixer@10.5.4(postcss@8.5.26): + dependencies: + browserslist: 4.28.8 + caniuse-lite: 1.0.30001809 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.11.19: {} + + bignumber.js@9.3.1: {} + + bowser@2.14.1: {} + + brace-expansion@1.1.18: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.19 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.413 + node-releases: 2.0.53 + update-browserslist-db: 1.3.1(browserslist@4.28.8) + + buffer-equal-constant-time@1.0.1: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001809: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chart.js@4.5.1: + dependencies: + '@kurkle/color': 0.3.4 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + code-red@1.0.4: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@types/estree': 1.0.9 + acorn: 8.18.0 + estree-walker: 3.0.3 + periscopic: 3.1.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@0.6.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + devalue@5.9.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + electron-to-chromium@1.5.413: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-compat-utils@0.5.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): + dependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + semver: 7.8.5 + + eslint-config-prettier@9.1.2(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)): + dependencies: + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + + eslint-plugin-svelte@2.46.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0))(svelte@4.2.20): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.39.5(jiti@2.7.0)(supports-color@7.2.0) + eslint-compat-utils: 0.5.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + esutils: 2.0.3 + known-css-properties: 0.35.0 + postcss: 8.5.26 + postcss-load-config: 3.1.4(postcss@8.5.26) + postcss-safe-parser: 6.0.0(postcss@8.5.26) + postcss-selector-parser: 6.1.4 + semver: 7.8.5 + svelte-eslint-parser: 0.43.0(svelte@4.2.20) + optionalDependencies: + svelte: 4.2.20 + transitivePeerDependencies: + - ts-node + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)(supports-color@7.2.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.6(supports-color@7.2.0) + '@eslint/js': 9.39.5 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.4.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 4.2.1 + + espree@9.6.1: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} + + expect-type@1.4.0: {} + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-xml-builder@1.3.1: + dependencies: + path-expression-matcher: 1.6.2 + xml-naming: 0.3.0 + + fast-xml-parser@5.11.0: + dependencies: + '@nodable/entities': 3.0.0 + fast-xml-builder: 1.3.1 + is-unsafe: 2.0.2 + path-expression-matcher: 1.6.2 + strnum: 2.4.2 + xml-naming: 0.3.0 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + + flatted@3.4.4: {} + + form-data@2.5.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + safe-buffer: 5.2.1 + + fraction.js@5.3.4: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gaxios@6.7.1(supports-color@7.2.0): + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6(supports-color@7.2.0) + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.1(supports-color@7.2.0): + dependencies: + gaxios: 6.7.1(supports-color@7.2.0) + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@15.15.0: {} + + google-auth-library@9.15.1(supports-color@7.2.0): + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1(supports-color@7.2.0) + gcp-metadata: 6.1.1(supports-color@7.2.0) + gtoken: 7.1.0(supports-color@7.2.0) + jws: 4.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + google-logging-utils@0.0.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + gtoken@7.1.0(supports-color@7.2.0): + dependencies: + gaxios: 6.7.1(supports-color@7.2.0) + jws: 4.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + html-entities@2.6.0: {} + + html-escaper@2.0.2: {} + + http-proxy-agent@5.0.0(supports-color@7.2.0): + dependencies: + '@tootallnate/once': 2.0.1 + agent-base: 6.0.2(supports-color@7.2.0) + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1(supports-color@7.2.0): + dependencies: + agent-base: 6.0.2(supports-color@7.2.0) + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6(supports-color@7.2.0): + dependencies: + agent-base: 7.1.4 + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + husky@9.1.7: {} + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + is-stream@2.0.1: {} + + is-unsafe@2.0.2: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jiti@2.7.0: {} + + js-tokens@10.0.0: {} + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@4.1.5: {} + + known-css-properties@0.35.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + lilconfig@2.1.0: {} + + locate-character@3.0.0: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lucide-svelte@0.320.0(svelte@4.2.20): + dependencies: + svelte: 4.2.20 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.4: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + math-intrinsics@1.1.0: {} + + mdn-data@2.0.30: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@3.0.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.18 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + natural-compare@1.4.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-releases@2.0.53: {} + + obug@2.1.4: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-expression-matcher@1.6.2: {} + + path-key@3.1.1: {} + + pathe@2.0.3: {} + + periscopic@3.1.0: + dependencies: + '@types/estree': 1.0.9 + estree-walker: 3.0.3 + is-reference: 3.0.3 + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss-load-config@3.1.4(postcss@8.5.26): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.3 + optionalDependencies: + postcss: 8.5.26 + + postcss-safe-parser@6.0.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-scss@4.0.9(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-selector-parser@6.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.5.2(prettier@3.9.6)(svelte@4.2.20): + dependencies: + prettier: 3.9.6 + svelte: 4.2.20 + + prettier@3.9.6: {} + + punycode@2.3.1: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@4.1.2: {} + + resolve-from@4.0.0: {} + + retry-request@7.0.2(supports-color@7.2.0): + dependencies: + '@types/request': 2.48.13 + extend: 3.0.2 + teeny-request: 9.0.0(supports-color@7.2.0) + transitivePeerDependencies: + - encoding + - supports-color + + retry@0.13.1: {} + + rollup@4.62.5: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.62.5 + '@rollup/rollup-android-arm64': 4.62.5 + '@rollup/rollup-darwin-arm64': 4.62.5 + '@rollup/rollup-darwin-x64': 4.62.5 + '@rollup/rollup-freebsd-arm64': 4.62.5 + '@rollup/rollup-freebsd-x64': 4.62.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.5 + '@rollup/rollup-linux-arm-musleabihf': 4.62.5 + '@rollup/rollup-linux-arm64-gnu': 4.62.5 + '@rollup/rollup-linux-arm64-musl': 4.62.5 + '@rollup/rollup-linux-loong64-gnu': 4.62.5 + '@rollup/rollup-linux-loong64-musl': 4.62.5 + '@rollup/rollup-linux-ppc64-gnu': 4.62.5 + '@rollup/rollup-linux-ppc64-musl': 4.62.5 + '@rollup/rollup-linux-riscv64-gnu': 4.62.5 + '@rollup/rollup-linux-riscv64-musl': 4.62.5 + '@rollup/rollup-linux-s390x-gnu': 4.62.5 + '@rollup/rollup-linux-x64-gnu': 4.62.5 + '@rollup/rollup-linux-x64-musl': 4.62.5 + '@rollup/rollup-openbsd-x64': 4.62.5 + '@rollup/rollup-openharmony-arm64': 4.62.5 + '@rollup/rollup-win32-arm64-msvc': 4.62.5 + '@rollup/rollup-win32-ia32-msvc': 4.62.5 + '@rollup/rollup-win32-x64-gnu': 4.62.5 + '@rollup/rollup-win32-x64-msvc': 4.62.5 + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-buffer@5.2.1: {} + + semver@7.8.5: {} + + set-cookie-parser@3.1.2: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stream-events@1.0.5: + dependencies: + stubs: 3.0.0 + + stream-shift@1.0.3: {} + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@3.1.1: {} + + strnum@2.4.2: + dependencies: + anynum: 1.0.1 + + stubs@3.0.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + svelte-check@4.7.6(picomatch@4.0.7)(svelte@4.2.20)(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.3 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.7) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 4.2.20 + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte-eslint-parser@0.25.1(svelte@4.2.20): + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + optionalDependencies: + svelte: 4.2.20 + + svelte-eslint-parser@0.43.0(svelte@4.2.20): + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + postcss: 8.5.26 + postcss-scss: 4.0.9(postcss@8.5.26) + optionalDependencies: + svelte: 4.2.20 + + svelte-hmr@0.16.0(svelte@4.2.20): + dependencies: + svelte: 4.2.20 + + svelte@4.2.20: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + '@types/estree': 1.0.9 + acorn: 8.18.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + periscopic: 3.1.0 + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + teeny-request@9.0.0(supports-color@7.2.0): + dependencies: + http-proxy-agent: 5.0.0(supports-color@7.2.0) + https-proxy-agent: 5.0.1(supports-color@7.2.0) + node-fetch: 2.7.0 + stream-events: 1.0.5 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + tinybench@2.9.0: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tinyrainbow@3.1.1: {} + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + update-browserslist-db@1.3.1(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@9.0.1: {} + + vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.26 + rollup: 4.62.5 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + lightningcss: 1.32.0 + + vitefu@0.2.5(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)): + optionalDependencies: + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + + vitest@4.1.11(@types/node@22.20.1)(@vitest/coverage-v8@4.1.11)(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.20.1 + '@vitest/coverage-v8': 4.1.11(vitest@4.1.11) + transitivePeerDependencies: + - msw + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + xml-naming@0.3.0: {} + + yaml@1.10.3: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..00f6fc4 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: set this to true or false diff --git a/src/hooks.server.ts b/src/hooks.server.ts index c416505..3651215 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -10,14 +10,12 @@ const authWithToken = async (token: string) => { }; export const handle: Handle = async ({ event, resolve }) => { - if ( - event.url.pathname === '/login' - ) { + if (event.url.pathname === '/login') { return resolve(event); } if (event.url.pathname.startsWith('/api/')) { - const token = event.request.headers.get('Authorization') || ""; + const token = event.request.headers.get('Authorization') || ''; if (!token || token.trim() === '') { return new Response(null, { status: 401 }); } @@ -54,11 +52,7 @@ export const handle: Handle = async ({ event, resolve }) => { ) { return new Response(null, { status: 302, headers: { location: '/' } }); } - } else if ( - event.url.pathname.startsWith('/cluster-settings') || - event.url.pathname.startsWith('/api/system') || - event.url.pathname.startsWith('/api/organizations') - ) { + } else if (event.url.pathname.startsWith('/cluster-settings')) { if (!cancanService.canAccessAdminArea(currentUser)) { return new Response(null, { status: 302, headers: { location: '/' } }); } diff --git a/src/lib/code-report/policy-evaluation.ts b/src/lib/code-report/policy-evaluation.ts new file mode 100644 index 0000000..c112597 --- /dev/null +++ b/src/lib/code-report/policy-evaluation.ts @@ -0,0 +1,276 @@ +import type { SecretFinding, VulnerabilityFinding } from './analysis-summary'; +import type { + SecurityPolicy, + SecurityPolicyType, + VulnerabilitiesRules, +} from './security-policy'; + +export type PolicyCheck = { + key: string; + label: string; + actual: number; + limit: number | null; + passed: boolean; + message: string; + samples: string[]; +}; + +export type PolicyEvaluation = { + policyId: string; + policyName: string; + policySlug: string; + type: SecurityPolicyType; + enforcement: SecurityPolicy['enforcement']; + applies: boolean; + evaluable: boolean; + skippedReason: string | null; + passed: boolean; + checks: PolicyCheck[]; + violations: PolicyCheck[]; +}; + +export type PolicyComplianceReport = { + status: 'no_policies' | 'not_applicable' | 'compliant' | 'violated'; + evaluations: PolicyEvaluation[]; + passed: PolicyEvaluation[]; + failed: PolicyEvaluation[]; + totalViolations: number; + blockingFailures: number; +}; + +export type PolicyEvaluationContext = { + serviceId: string; + serviceTags: string[]; + vulnerabilities: VulnerabilityFinding[]; + secrets: SecretFinding[]; + hasVulnerabilityScan: boolean; + hasSecretScan: boolean; +}; + +const DAY_MS = 24 * 60 * 60 * 1000; + +export function policyApplies(policy: SecurityPolicy, context: PolicyEvaluationContext): boolean { + if (policy.scope.mode === 'services') return policy.scope.services.includes(context.serviceId); + if (policy.scope.mode === 'tags') + return policy.scope.tags.some((tag) => context.serviceTags.includes(tag)); + return true; +} + +function check( + key: string, + label: string, + actual: number, + limit: number | null, + samples: string[], + message: string, +): PolicyCheck { + return { + key, + label, + actual, + limit, + passed: limit === null || actual <= limit, + message, + samples: samples.slice(0, 5), + }; +} + +function relevantVulnerabilities(rules: VulnerabilitiesRules, findings: VulnerabilityFinding[]) { + const ignored = new Set(rules.ignoredCves.map((cve) => cve.toUpperCase())); + return findings.filter((finding) => { + if (ignored.has(finding.id.toUpperCase())) return false; + if (rules.ignoreUnfixed && !finding.fixedVersion) return false; + if (rules.minCvssScore !== null && (finding.cvssScore ?? 0) < rules.minCvssScore) return false; + return true; + }); +} + +function evaluateVulnerabilities( + rules: VulnerabilitiesRules, + findings: VulnerabilityFinding[], +): PolicyCheck[] { + const scoped = relevantVulnerabilities(rules, findings); + const bySeverity = (severity: VulnerabilityFinding['severity']) => + scoped.filter((finding) => finding.severity === severity); + + const severityChecks: { key: keyof VulnerabilitiesRules; label: string; severity: VulnerabilityFinding['severity'] }[] = + [ + { key: 'maxCritical', label: 'Vulnerabilidades críticas', severity: 'critical' }, + { key: 'maxHigh', label: 'Vulnerabilidades altas', severity: 'high' }, + { key: 'maxMedium', label: 'Vulnerabilidades medias', severity: 'medium' }, + { key: 'maxLow', label: 'Vulnerabilidades bajas', severity: 'low' }, + ]; + + const checks = severityChecks + .filter(({ key }) => rules[key] !== null && rules[key] !== undefined) + .map(({ key, label, severity }) => { + const matches = bySeverity(severity); + const limit = rules[key] as number; + return check( + String(key), + label, + matches.length, + limit, + matches.map((finding) => finding.id), + `${matches.length} encontradas · máximo permitido ${limit}`, + ); + }); + + if (rules.maxAgeDays !== null) { + const cutoff = Date.now() - rules.maxAgeDays * DAY_MS; + const stale = scoped.filter((finding) => { + if (!finding.publishedDate) return false; + const published = new Date(finding.publishedDate).getTime(); + return Number.isFinite(published) && published < cutoff; + }); + checks.push( + check( + 'maxAgeDays', + `Hallazgos con más de ${rules.maxAgeDays} días`, + stale.length, + 0, + stale.map((finding) => finding.id), + `${stale.length} vulnerabilidades publicadas hace más de ${rules.maxAgeDays} días`, + ), + ); + } + + return checks; +} + +function evaluateSecrets(rules: any, secrets: SecretFinding[]): PolicyCheck[] { + const ignored = new Set((rules.ignoredRules ?? []).map((rule: string) => rule.toLowerCase())); + const scoped = secrets.filter((secret) => !ignored.has(secret.ruleId.toLowerCase())); + + if (rules.maxSecrets === null || rules.maxSecrets === undefined) return []; + + return [ + check( + 'maxSecrets', + 'Secretos expuestos', + scoped.length, + rules.maxSecrets, + scoped.map((secret) => `${secret.ruleId} · ${secret.file}`), + `${scoped.length} secretos detectados · máximo permitido ${rules.maxSecrets}`, + ), + ]; +} + +export function evaluatePolicy( + policy: SecurityPolicy, + context: PolicyEvaluationContext, +): PolicyEvaluation { + const base = { + policyId: policy.id, + policyName: policy.name, + policySlug: policy.slug, + type: policy.type, + enforcement: policy.enforcement, + applies: policyApplies(policy, context), + }; + + const notEvaluable = (reason: string): PolicyEvaluation => ({ + ...base, + evaluable: false, + skippedReason: reason, + passed: true, + checks: [], + violations: [], + }); + + if (!base.applies) return notEvaluable('Fuera del alcance de este servicio'); + + let checks: PolicyCheck[]; + + if (policy.type === 'vulnerabilities') { + if (!context.hasVulnerabilityScan) return notEvaluable('Sin análisis de vulnerabilidades'); + checks = evaluateVulnerabilities(policy.rules as VulnerabilitiesRules, context.vulnerabilities); + } else if (policy.type === 'secrets') { + if (!context.hasSecretScan) return notEvaluable('Sin análisis de secretos'); + checks = evaluateSecrets(policy.rules, context.secrets); + } else { + return notEvaluable('Tipo de política todavía no evaluable'); + } + + if (checks.length === 0) return notEvaluable('La política no define reglas aplicables'); + + const violations = checks.filter((item) => !item.passed); + + return { + ...base, + evaluable: true, + skippedReason: null, + passed: violations.length === 0, + checks, + violations, + }; +} + +export function evaluatePolicies( + policies: SecurityPolicy[], + context: PolicyEvaluationContext, +): PolicyComplianceReport { + const active = policies.filter((policy) => policy.enabled); + const evaluations = active.map((policy) => evaluatePolicy(policy, context)); + const evaluable = evaluations.filter((evaluation) => evaluation.evaluable); + const failed = evaluable.filter((evaluation) => !evaluation.passed); + const passed = evaluable.filter((evaluation) => evaluation.passed); + + const status: PolicyComplianceReport['status'] = + active.length === 0 + ? 'no_policies' + : evaluable.length === 0 + ? 'not_applicable' + : failed.length > 0 + ? 'violated' + : 'compliant'; + + return { + status, + evaluations, + passed, + failed, + totalViolations: failed.reduce((total, evaluation) => total + evaluation.violations.length, 0), + blockingFailures: failed.filter((evaluation) => evaluation.enforcement === 'block').length, + }; +} + +// each tool stores its own partial report, so views combine them into a single one +export function mergeComplianceReports( + reports: (PolicyComplianceReport | null | undefined)[], +): PolicyComplianceReport | null { + const available = reports.filter((report): report is PolicyComplianceReport => Boolean(report)); + if (available.length === 0) return null; + + const byPolicy = new Map(); + for (const report of available) { + for (const evaluation of report.evaluations) { + const existing = byPolicy.get(evaluation.policyId); + // an evaluable result always wins over a skipped one + if (!existing || (!existing.evaluable && evaluation.evaluable)) { + byPolicy.set(evaluation.policyId, evaluation); + } + } + } + + const evaluations = [...byPolicy.values()]; + const evaluable = evaluations.filter((evaluation) => evaluation.evaluable); + const failed = evaluable.filter((evaluation) => !evaluation.passed); + const passed = evaluable.filter((evaluation) => evaluation.passed); + + return { + status: + evaluations.length === 0 + ? 'no_policies' + : evaluable.length === 0 + ? 'not_applicable' + : failed.length > 0 + ? 'violated' + : 'compliant', + evaluations, + passed, + failed, + totalViolations: failed.reduce((total, evaluation) => total + evaluation.violations.length, 0), + blockingFailures: failed.filter((evaluation) => evaluation.enforcement === 'block').length, + }; +} diff --git a/src/lib/code-report/security-policy.ts b/src/lib/code-report/security-policy.ts new file mode 100644 index 0000000..285c537 --- /dev/null +++ b/src/lib/code-report/security-policy.ts @@ -0,0 +1,163 @@ +export const SECURITY_POLICY_TYPES = [ + 'vulnerabilities', + 'license', + 'code_coverage', + 'secrets', +] as const; + +export type SecurityPolicyType = (typeof SECURITY_POLICY_TYPES)[number]; + +export const SECURITY_POLICY_ENFORCEMENTS = ['warn', 'block'] as const; +export type SecurityPolicyEnforcement = (typeof SECURITY_POLICY_ENFORCEMENTS)[number]; + +export const SECURITY_POLICY_SCOPE_MODES = ['all', 'services', 'tags'] as const; +export type SecurityPolicyScopeMode = (typeof SECURITY_POLICY_SCOPE_MODES)[number]; + +export type SecurityPolicyScope = { + mode: SecurityPolicyScopeMode; + services: string[]; + tags: string[]; +}; + +export type VulnerabilitiesRules = { + maxCritical: number | null; + maxHigh: number | null; + maxMedium: number | null; + maxLow: number | null; + minCvssScore: number | null; + ignoreUnfixed: boolean; + maxAgeDays: number | null; + ignoredCves: string[]; +}; + +export type LicenseRules = { + mode: 'allowlist' | 'denylist'; + licenses: string[]; + allowUnknown: boolean; +}; + +export type CodeCoverageRules = { + minTotalCoverage: number | null; + minPatchCoverage: number | null; + allowCoverageDrop: boolean; +}; + +export type SecretsRules = { + maxSecrets: number | null; + blockVerifiedOnly: boolean; + ignoredRules: string[]; +}; + +export type SecurityPolicyRules = + | VulnerabilitiesRules + | LicenseRules + | CodeCoverageRules + | SecretsRules + | Record; + +export type SecurityPolicy = { + id: string; + projectId: string; + slug: string; + name: string; + description: string | null; + type: SecurityPolicyType; + enabled: boolean; + enforcement: SecurityPolicyEnforcement; + scope: SecurityPolicyScope; + rules: SecurityPolicyRules; + createdAt: string | Date; + updatedAt: string | Date; +}; + +export const SECURITY_POLICY_TYPE_META: Record< + SecurityPolicyType, + { label: string; description: string; available: boolean } +> = { + vulnerabilities: { + label: 'Vulnerabilidades', + description: 'Umbrales por severidad, CVSS y CVEs ignorados.', + available: true, + }, + license: { + label: 'Licencias', + description: 'Licencias permitidas o denegadas en las dependencias.', + available: false, + }, + code_coverage: { + label: 'Cobertura de código', + description: 'Mínimos de cobertura total y de los cambios.', + available: false, + }, + secrets: { + label: 'Secretos', + description: 'Secretos expuestos detectados en el repositorio.', + available: false, + }, +}; + +export const SECURITY_POLICY_ENFORCEMENT_META: Record< + SecurityPolicyEnforcement, + { label: string; description: string; available: boolean } +> = { + warn: { + label: 'Avisar', + description: 'Marca el análisis como incumplido pero no bloquea el pipeline.', + available: true, + }, + block: { + label: 'Bloquear', + description: 'Falla el pipeline cuando el análisis incumple la política.', + available: false, + }, +}; + +export function defaultRulesFor(type: SecurityPolicyType): SecurityPolicyRules { + switch (type) { + case 'vulnerabilities': + return { + maxCritical: 0, + maxHigh: null, + maxMedium: null, + maxLow: null, + minCvssScore: null, + ignoreUnfixed: false, + maxAgeDays: null, + ignoredCves: [], + } satisfies VulnerabilitiesRules; + case 'license': + return { mode: 'denylist', licenses: [], allowUnknown: true } satisfies LicenseRules; + case 'code_coverage': + return { + minTotalCoverage: 80, + minPatchCoverage: null, + allowCoverageDrop: false, + } satisfies CodeCoverageRules; + case 'secrets': + return { + maxSecrets: 0, + blockVerifiedOnly: false, + ignoredRules: [], + } satisfies SecretsRules; + } +} + +export function defaultScope(): SecurityPolicyScope { + return { mode: 'all', services: [], tags: [] }; +} + +export function isSecurityPolicyType(value: unknown): value is SecurityPolicyType { + return SECURITY_POLICY_TYPES.includes(value as SecurityPolicyType); +} + +export function describeScope(scope: SecurityPolicyScope): string { + if (scope.mode === 'services') { + return scope.services.length > 0 + ? `${scope.services.length} servicio(s)` + : 'Sin servicios seleccionados'; + } + if (scope.mode === 'tags') { + return scope.tags.length > 0 ? scope.tags.join(', ') : 'Sin tags seleccionados'; + } + return 'Todos los servicios'; +} diff --git a/src/lib/components/AppSidebar.svelte b/src/lib/components/AppSidebar.svelte index 92a5c59..2143062 100644 --- a/src/lib/components/AppSidebar.svelte +++ b/src/lib/components/AppSidebar.svelte @@ -34,6 +34,7 @@ export let collapsed = true; export let organizationSlug: string | null = null; export let organizationName: string | null = null; + export let currentProjectSlug: string | null = null; export let projects: { slug: string; modules?: { vault: boolean; codereport: boolean; stateiac: boolean }; @@ -42,9 +43,10 @@ let currentPath = pathname; let openModules: Record = {}; - $: currentPath = $page.url.pathname || pathname; - $: currentProjectOrgSlug = ($page.params.org as string | undefined) ?? organizationSlug; - $: currentProjectSlug = $page.params.slug as string | undefined; + $: currentPath = $page?.url?.pathname || pathname; + // sourced from the server load (parsed from the URL), so it never lags behind or + // blanks out during client-side navigation like $page.params could + $: currentProjectOrgSlug = organizationSlug; $: currentProject = projects.find((project) => project.slug === currentProjectSlug) ?? null; $: projectBase = `/org/${currentProjectOrgSlug}/projects/${currentProjectSlug}`; @@ -64,6 +66,11 @@ href: `/org/${organizationSlug}/overview`, icon: LayoutDashboard, }, + { + label: 'CVEs', + href: `/org/${organizationSlug}/cves`, + icon: ShieldAlert, + }, ] : [ { @@ -104,9 +111,17 @@ icon: LayoutDashboard, }, { label: 'Services', href: `${projectBase}/code-report/services`, icon: Layers }, - { label: 'CVEs', href: `${projectBase}/code-report/cves`, icon: ShieldAlert }, + { + label: 'CVEs', + href: `/org/${currentProjectOrgSlug}/cves?project=${currentProjectSlug}`, + icon: ShieldAlert, + }, + { + label: 'Security Policies', + href: `${projectBase}/code-report/security-policy`, + icon: Shield, + }, { label: 'History', href: `${projectBase}/code-report/history`, icon: GitBranch }, - { label: 'GitOps Report Bot', href: `${projectBase}/code-report/bot`, icon: Bot }, { label: 'Settings', href: `${projectBase}/code-report/settings`, icon: Settings }, ], }, diff --git a/src/lib/components/CodeReportVisualization.svelte b/src/lib/components/CodeReportVisualization.svelte index 6416268..8e5a5c0 100644 --- a/src/lib/components/CodeReportVisualization.svelte +++ b/src/lib/components/CodeReportVisualization.svelte @@ -8,11 +8,14 @@ summarizeAnalysisResult, type VulnerabilityFinding, } from '$lib/code-report/analysis-summary'; + import type { PolicyComplianceReport } from '$lib/code-report/policy-evaluation'; + import { mergeComplianceReports } from '$lib/code-report/policy-evaluation'; import CodeReportFiles from './code-report/CodeReportFiles.svelte'; import CodeReportSbom from './code-report/CodeReportSbom.svelte'; import CodeReportSecrets from './code-report/CodeReportSecrets.svelte'; import CodeReportSummary from './code-report/CodeReportSummary.svelte'; import CodeReportVulnerabilities from './code-report/CodeReportVulnerabilities.svelte'; + import SecurityPolicyComplianceCard from './code-report/SecurityPolicyComplianceCard.svelte'; type AnalysisData = { id: string; @@ -20,6 +23,7 @@ status: 'in_progress' | 'completed' | 'failed'; result: unknown; summary?: unknown; + securityPolicies?: PolicyComplianceReport | null; error?: string | null; gitInfo?: { repositoryUrl?: string | null; @@ -31,6 +35,7 @@ }; type Analysis = AnalysisData | null; type ServiceData = { + id?: string; name: string; slug: string; description?: string | null; @@ -40,6 +45,7 @@ export let analysisHistory: AnalysisData[] = []; export let latestByTool: Record = {}; export let service: ServiceData = null; + export let securityPoliciesHref: string | null = null; let activeTab = 'summary'; let activeVulnerabilityTab = 'cve'; let riskInfoModalOpen = false; @@ -51,7 +57,8 @@ let severityFilter = 'all'; let selectedFilePath = ''; let chart: { destroy: () => void } | null = null; - const riskWeights = { critical: 10, high: 6, medium: 3, low: 1 }; + export let riskWeights = { critical: 10, high: 6, medium: 3, low: 1 }; + const severityStyles: Record = { critical: 'bg-red-50 text-red-700 border-red-200', high: 'bg-orange-50 text-orange-700 border-orange-200', @@ -71,6 +78,12 @@ $: vulnerabilities = trivyAnalysis ? extractVulnerabilities(trivyAnalysis.result) : []; $: secrets = gitleaksAnalysis ? extractSecrets(gitleaksAnalysis.result) : []; $: sbomComponents = sbomAnalysis ? extractSbomComponents(sbomAnalysis.result) : []; + $: complianceReport = mergeComplianceReports([ + trivyAnalysis?.securityPolicies, + gitleaksAnalysis?.securityPolicies, + sbomAnalysis?.securityPolicies, + analysis?.securityPolicies, + ]); $: fileGroups = groupByFile(vulnerabilities); $: selectedFile = fileGroups.find((file) => file.path === selectedFilePath) ?? null; $: historyPoints = analysisHistory @@ -206,6 +219,12 @@
+ {#if analysis && complianceReport} + + {/if}
{#each tabs as tab} + {/each} +
+ + {#if activeTab === 'info'} +
+
+ +
+

Severidad

+

{cve.severity}

+
+
+ {#if cve.publishedDate} +
+ +
+

Publicado

+

{new Date(cve.publishedDate).toLocaleDateString()}

+
+
+ {/if} +
+ +
+

Overview

+

{cve.description || 'Sin descripcion disponible.'}

+ {#if cve.lastModifiedDate} +

+ Ultima actualizacion: {new Date(cve.lastModifiedDate).toLocaleDateString()} +

+ {/if} +
+ +
+

CWE

+ {#if cve.cweIds.length === 0} +

No se ha indicado una clasificacion CWE para este CVE.

+ {:else} +
+ {#each cve.cweIds as cweId (cweId)} + + {cweId} + + {/each} +
+ {/if} +
+ +
+
+
+ + CVSS + + + +
+

+ {cve.cvssScore !== null ? cve.cvssScore.toFixed(1) : '-'} +

+
+
+
+

Escala 0-10

+
+ +
+
+ + EPSS Score + + + +
+

+ {cve.epssScore !== null ? `${(cve.epssScore * 100).toFixed(3)}%` : '-'} +

+
+
+
+

Probabilidad de explotacion

+
+ +
+
+ + EPSS Percentil + + + +
+

+ {cve.epssPercentile !== null ? `${(cve.epssPercentile * 100).toFixed(1)}%` : '-'} +

+
+
+
+

Frente al resto de CVEs conocidas

+
+
+ +
+

+ Como solucionarlo +

+ {#if remediations.some((remediation) => remediation.fixedVersion)} +
    + {#each remediations as remediation (remediation.packageName)} +
  • + {remediation.packageName} + {#if remediation.fixedVersion} +
    + {remediation.installedVersion} + -> + {#each splitVersions(remediation.fixedVersion) as version (version)} + + {version} + + {/each} +
    + {:else} + - todavia no hay una version corregida publicada. + {/if} +
  • + {/each} +
+ {:else} +

+ Ninguno de los paquetes afectados tiene aun una version corregida publicada. Revisa el advisory + para posibles mitigaciones alternativas. +

+ {/if} +
+ +
+

References

+ {#if cve.references.length === 0 && !cve.primaryUrl} +

No hay referencias disponibles para este CVE.

+ {:else} + + {/if} +
+ {:else} +
+ +
+ +
+ + + + + + + + + + + + + {#each paginatedAffectedServices as service (service.serviceId + service.target + service.packageName)} + {@const target = splitTarget(service.target)} + + + + + + + + + {/each} + {#if paginatedAffectedServices.length === 0} + + + + {/if} + +
ServicioPaqueteVersion instaladaVersion corregidaObjetivoUltimo escaneo
+ + {service.serviceName} + + {#if service.projectName || service.projectSlug} +

+ Proyecto: + {service.projectName || service.projectSlug} +

+ {/if} +
{service.packageName} + {service.installedVersion} + + {#if service.fixedVersion} +
+ {#each splitVersions(service.fixedVersion) as version (version)} + + {version} + + {/each} +
+ {:else} + No indicada + {/if} +
+ {#if target.dirParts.length > 0} +

+ {#each target.dirParts as part, i (i)}{part}{#if i < target.dirParts.length - 1}/{/if}{/each}/ +

+ {/if} +

{target.file}

+
+ {service.scannedAt ? new Date(service.scannedAt).toLocaleString() : '-'} +
+ {affectedServices.length === 0 + ? 'No hay servicios afectados.' + : 'Sin resultados para tu busqueda.'} +
+
+ +
+
+ Mostrando {affectedServicesRangeStart}-{affectedServicesRangeEnd} de {filteredAffectedServices.length} + +
+ +
+ + + Pagina {affectedServicesPage} de {affectedServicesTotalPages} + + +
+
+ {/if} +
diff --git a/src/lib/components/code-report/CveListView.svelte b/src/lib/components/code-report/CveListView.svelte new file mode 100644 index 0000000..659e6e7 --- /dev/null +++ b/src/lib/components/code-report/CveListView.svelte @@ -0,0 +1,214 @@ + + +
+
+ +

{infoMessage}

+
+ +
+ + + {#if showProjectFilter} + + {/if} + + +
+ + {#if filteredCves.length === 0} +
+ +

+ {cves.length === 0 ? emptyMessage : 'Sin resultados para tu busqueda.'} +

+
+ {:else} +
+ + + + + + + + + + + + {#each paginatedCves as cve (cve.id)} + + + + + + + + {/each} + +
CVESeveridadCVSSServicios afectadosApariciones
+ + {cve.id} + + {#if cve.title} +

{cve.title}

+ {/if} +
+ + {#if cve.severity === 'critical'}{/if} + {cve.severity} + + + {cve.cvssScore !== null ? cve.cvssScore.toFixed(1) : '-'} + {cve.affectedServiceCount}{cve.occurrenceCount}
+
+ +
+
+ Mostrando {rangeStart}-{rangeEnd} de {filteredCves.length} + +
+ +
+ + Pagina {currentPage} de {totalPages} + +
+
+ {/if} +
diff --git a/src/lib/components/code-report/SecurityPolicyComplianceCard.svelte b/src/lib/components/code-report/SecurityPolicyComplianceCard.svelte new file mode 100644 index 0000000..6ce1398 --- /dev/null +++ b/src/lib/components/code-report/SecurityPolicyComplianceCard.svelte @@ -0,0 +1,177 @@ + + +
+
+ +
+
+
+ +
+
+

+ Security Policies +

+

{theme.title}

+

{subtitle}

+
+
+ +
+ {#if report.blockingFailures > 0} + + + {report.blockingFailures} bloqueantes + + {/if} + {#if policiesHref} + + Ver políticas + + {/if} + {#if report.evaluations.length > 0} + + {/if} +
+
+ + {#if expanded && report.evaluations.length > 0} +
+ {#each report.evaluations as evaluation (evaluation.policyId)} +
+
+
+ {#if !evaluation.evaluable} + + {:else if evaluation.passed} + + {:else} + + {/if} +

{evaluation.policyName}

+ + {evaluation.enforcement === 'block' ? 'Bloquear' : 'Avisar'} + +
+ + {!evaluation.evaluable ? 'No evaluada' : evaluation.passed ? 'Cumple' : 'Incumple'} + +
+ + {#if !evaluation.evaluable} +

{evaluation.skippedReason}

+ {:else} +
    + {#each evaluation.checks as item (item.key)} +
  • +
    +

    + {item.label} +

    +

    {item.message}

    + {#if !item.passed && item.samples.length > 0} +

    + Ej.: {item.samples.join(', ')} + {#if item.actual > item.samples.length} + y {item.actual - item.samples.length} más + {/if} +

    + {/if} +
    + + {item.actual}{item.limit !== null ? ` / ${item.limit}` : ''} + +
  • + {/each} +
+ {/if} +
+ {/each} +
+ {/if} +
diff --git a/src/lib/components/code-report/SecurityPolicyForm.svelte b/src/lib/components/code-report/SecurityPolicyForm.svelte new file mode 100644 index 0000000..dfeeca2 --- /dev/null +++ b/src/lib/components/code-report/SecurityPolicyForm.svelte @@ -0,0 +1,519 @@ + + +
{ + submitting = true; + return async ({ update }) => { + await update(); + submitting = false; + }; + }} + class="space-y-4" +> + + + {#if errorMessage} +

{errorMessage}

+ {/if} + +
+

1. Información general

+

Identifica la política dentro del proyecto.

+ +
+ + +
+ + +
+ +
+

2. Tipo de política

+

Determina qué reglas puedes configurar.

+ +
+ {#each SECURITY_POLICY_TYPES as value} + + {/each} +
+
+ +
+

3. Alcance

+

A qué servicios del proyecto se aplica.

+ +
+ {#each scopeModes as option} + + {/each} +
+ + {#if scope.mode === 'services'} +
+ {#each services as service (service.id)} + + {:else} +

No hay servicios en este proyecto.

+ {/each} +
+ {:else if scope.mode === 'tags'} +
+ {#each tags as tag} + + {:else} +

No hay tags definidos en los servicios.

+ {/each} +
+ {/if} +
+ +
+

4. Reglas

+

+ Deja un campo vacío para no aplicar ese límite. +

+ + {#if type === 'vulnerabilities'} +
+ {#each severityFields as field} + + {/each} +
+ +
+ + +
+ + + +
+ CVEs ignorados +
+ { + if (event.key === 'Enter') { + event.preventDefault(); + addToRuleList('ignoredCves', ignoredCveInput); + ignoredCveInput = ''; + } + }} + class="flex-1 rounded-lg border border-slate-200 px-3 py-2 text-sm focus:border-slate-400 focus:outline-none" + /> + +
+
+ {#each rulesByType.vulnerabilities.ignoredCves as cve} + + {cve} + + + {/each} +
+
+ {:else if type === 'license'} +
+ {#each licenseModes as option} + + {/each} +
+
+ + +
+
+ {#each rulesByType.license.licenses as license} + + {license} + + + {/each} +
+ + {:else if type === 'code_coverage'} +
+ + +
+ + {:else if type === 'secrets'} + + +
+ + +
+
+ {#each rulesByType.secrets.ignoredRules as rule} + + {rule} + + + {/each} +
+ {/if} +
+ +
+

5. Aplicación

+

Qué ocurre cuando un análisis incumple la política.

+ +
+ {#each SECURITY_POLICY_ENFORCEMENTS as value} + + {/each} +
+ + +
+ +
+ + +
+
diff --git a/src/lib/database/schemas.ts b/src/lib/database/schemas.ts index fd0d3c4..50d5213 100644 --- a/src/lib/database/schemas.ts +++ b/src/lib/database/schemas.ts @@ -104,6 +104,9 @@ export const ProjectEntity = entity('projects', { modules: json() .notNull() .$defaultFn(() => ({ vault: true, codereport: true, stateiac: true })), + settings: json() + .notNull() + .$defaultFn(() => ({ 'code-report': {} })), createdAt: timestamp() .notNull() .$defaultFn(() => new Date().toISOString()), @@ -139,6 +142,8 @@ export const CodeReportAnalysisEntity = entity('code_report_analyses', { status: text().notNull().default('in_progress'), result: json(), summary: json(), + // compliance report evaluated when the analysis is completed + securityPolicies: json(), error: text(), gitInfo: json(), createdAt: timestamp() @@ -149,6 +154,33 @@ export const CodeReportAnalysisEntity = entity('code_report_analyses', { .$defaultFn(() => new Date().toISOString()), }); +export const CodeReportSecurityPolicyEntity = entity('code_report_security_policies', { + id: uuid().primaryKey(), + projectId: uuid().notNull(), + slug: text().notNull(), + name: text().notNull(), + description: text(), + // vulnerabilities | license | code_coverage | secrets + type: text().notNull().default('vulnerabilities'), + enabled: bool().notNull().default(true), + // warn | block + enforcement: text().notNull().default('warn'), + // { mode: 'all' | 'services' | 'tags', services: string[], tags: string[] } + scope: json() + .notNull() + .$defaultFn(() => ({ mode: 'all', services: [], tags: [] })), + // type-specific configuration, see $lib/code-report/security-policy + rules: json() + .notNull() + .$defaultFn(() => ({})), + createdAt: timestamp() + .notNull() + .$defaultFn(() => new Date().toISOString()), + updatedAt: timestamp() + .notNull() + .$defaultFn(() => new Date().toISOString()), +}); + relations.for(ProjectEntity, ({ one, many }) => ({ roles: many(RoleEntity, { fields: ['id'], references: ['projectId'] }), organization: one(OrganizationEntity, { fields: ['organizationId'], references: ['id'] }), @@ -157,6 +189,14 @@ relations.for(ProjectEntity, ({ one, many }) => ({ fields: ['id'], references: ['projectId'], }), + codeReportSecurityPolicies: many(CodeReportSecurityPolicyEntity, { + fields: ['id'], + references: ['projectId'], + }), +})); + +relations.for(CodeReportSecurityPolicyEntity, ({ one }) => ({ + project: one(ProjectEntity, { fields: ['projectId'], references: ['id'] }), })); relations.for(CodeReportServiceEntity, ({ one, many }) => ({ diff --git a/src/modules/code-report/application/code-report-analysis.service.ts b/src/modules/code-report/application/code-report-analysis.service.ts index e0484fa..2097678 100644 --- a/src/modules/code-report/application/code-report-analysis.service.ts +++ b/src/modules/code-report/application/code-report-analysis.service.ts @@ -2,15 +2,36 @@ import crypto from 'crypto'; import { CodeReportAnalysisRepository } from '../infrastructure/repositories/code-report-analysis.repository'; import type { CodeReportAnalysisDomain } from '../domain/code-report-analysis.domain'; import type { CodeReportGitInfo } from '../domain/code-report-analysis.domain'; +import { extractSecrets, extractVulnerabilities } from '$lib/code-report/analysis-summary'; +import { evaluatePolicies, type PolicyComplianceReport } from '$lib/code-report/policy-evaluation'; +import type { SecurityPolicy, SecurityPolicyType } from '$lib/code-report/security-policy'; type ServiceLookup = { - findById(id: string): Promise<{ id: string } | null>; + findById(id: string): Promise<{ id: string; projectId?: string; tags?: string[] } | null>; }; +type PolicyLookup = { + listByProject(projectId: string): Promise; +}; + +// each tool only produces evidence for some policy types +const TOOL_POLICY_TYPES: Record = { + trivy: ['vulnerabilities', 'license'], + grype: ['vulnerabilities'], + sbom: ['license'], + syft: ['license'], + gitleaks: ['secrets'], + trufflehog: ['secrets'], + coverage: ['code_coverage'], + 'code-coverage': ['code_coverage'], +}; +const DEFAULT_POLICY_TYPES: SecurityPolicyType[] = ['vulnerabilities', 'license']; + export class CodeReportAnalysisService { constructor( private readonly repository: CodeReportAnalysisRepository, private readonly serviceLookup: ServiceLookup, + private readonly policyLookup?: PolicyLookup, ) {} async listByService(serviceId: string) { @@ -100,6 +121,7 @@ export class CodeReportAnalysisService { status: 'completed', result: input.result, summary: input.summary, + securityPolicies: await this.evaluateSecurityPolicies(analysis.serviceId, analysis.tool, input.result), gitInfo: input.gitInfo, error: null, }); @@ -107,6 +129,67 @@ export class CodeReportAnalysisService { return this.getById(id); } + // compliance is frozen at completion time so the UI never re-evaluates on read + private async evaluateSecurityPolicies( + serviceId: string, + tool: string, + result: unknown, + ): Promise { + if (!this.policyLookup) return null; + + const service = await this.serviceLookup.findById(serviceId); + if (!service?.projectId) return null; + + const policies = await this.policyLookup.listByProject(service.projectId); + const types = TOOL_POLICY_TYPES[tool.toLowerCase()] ?? DEFAULT_POLICY_TYPES; + const scopedPolicies = policies.filter((policy) => types.includes(policy.type)); + if (scopedPolicies.length === 0) return null; + + const checksVulnerabilities = types.includes('vulnerabilities'); + const checksSecrets = types.includes('secrets'); + + return evaluatePolicies(scopedPolicies, { + serviceId: service.id, + serviceTags: service.tags ?? [], + vulnerabilities: checksVulnerabilities ? extractVulnerabilities(result) : [], + secrets: checksSecrets ? extractSecrets(result) : [], + hasVulnerabilityScan: checksVulnerabilities, + hasSecretScan: checksSecrets, + }); + } + + // re-runs the policy evaluation over the latest stored analysis of each service + async revalidateLatestByServices(services: { id: string; tools?: string[] }[]) { + let analysesUpdated = 0; + const reports: { serviceId: string; report: PolicyComplianceReport }[] = []; + + for (const service of services) { + const tools = service.tools?.length ? service.tools : ['trivy']; + const latest = await this.getLatestByTool(service.id, tools); + + for (const analysis of Object.values(latest)) { + if (!analysis || analysis.status !== 'completed') continue; + + const report = await this.evaluateSecurityPolicies( + analysis.serviceId, + analysis.tool, + analysis.result, + ); + if (!report) continue; + + await this.repository.update(analysis.id, { securityPolicies: report }); + analysesUpdated += 1; + reports.push({ serviceId: service.id, report }); + } + } + + return { + servicesEvaluated: new Set(reports.map((entry) => entry.serviceId)).size, + analysesUpdated, + reports, + }; + } + // called when the tool could not run/complete, records the reason instead of a result async failAnalysis(id: string, input: { error: string; gitInfo?: CodeReportGitInfo }) { const analysis = await this.repository.findById(id); diff --git a/src/modules/code-report/application/code-report-cve.service.ts b/src/modules/code-report/application/code-report-cve.service.ts index 0f23bf9..3ea39f6 100644 --- a/src/modules/code-report/application/code-report-cve.service.ts +++ b/src/modules/code-report/application/code-report-cve.service.ts @@ -2,6 +2,12 @@ import { collectCveOccurrences, type CompletedAnalysis, type CveOccurrence } fro import type { CodeReportService } from './code-report.service'; import type { CodeReportAnalysisService } from './code-report-analysis.service'; +export type OrganizationCveOccurrence = CveOccurrence & { + projectId: string; + projectSlug: string; + projectName: string; +}; + export class CodeReportCveService { constructor( private readonly codeReportService: CodeReportService, @@ -16,11 +22,45 @@ export class CodeReportCveService { services.map(async (service) => { const latest = await this.codeReportAnalysisService.getLatestByTool(service.id, ['trivy']); const analysis = latest.trivy; - const completed: CompletedAnalysis = analysis?.status === 'completed' ? analysis : null; + const completed: CompletedAnalysis = + analysis?.status === 'completed' + ? { + result: analysis.result, + createdAt: + analysis.createdAt instanceof Date + ? analysis.createdAt.toISOString() + : String(analysis.createdAt), + } + : null; return [service.id, completed] as const; }), ); return collectCveOccurrences(services, new Map(analysisEntries)); } + + async getOrganizationCveOccurrences( + projects: { id: string; slug: string; name: string }[], + ): Promise> { + const occurrencesByCve = new Map(); + + for (const project of projects) { + const projectOccurrences = await this.getProjectCveOccurrences(project.id); + + for (const [cveId, occurrences] of projectOccurrences.entries()) { + const merged = occurrencesByCve.get(cveId) ?? []; + merged.push( + ...occurrences.map((occurrence) => ({ + ...occurrence, + projectId: project.id, + projectSlug: project.slug, + projectName: project.name, + })), + ); + occurrencesByCve.set(cveId, merged); + } + } + + return occurrencesByCve; + } } diff --git a/src/modules/code-report/application/code-report-security-policy.service.ts b/src/modules/code-report/application/code-report-security-policy.service.ts new file mode 100644 index 0000000..4170f31 --- /dev/null +++ b/src/modules/code-report/application/code-report-security-policy.service.ts @@ -0,0 +1,246 @@ +import crypto from 'crypto'; +import { CodeReportSecurityPolicyRepository } from '../infrastructure/repositories/code-report-security-policy.repository'; +import { + defaultRulesFor, + defaultScope, + isSecurityPolicyType, + SECURITY_POLICY_ENFORCEMENTS, + SECURITY_POLICY_SCOPE_MODES, + type SecurityPolicyEnforcement, + type SecurityPolicyRules, + type SecurityPolicyScope, + type SecurityPolicyType, +} from '$lib/code-report/security-policy'; + +export type SecurityPolicyInput = { + name: string; + slug?: string; + description?: string; + type: string; + enabled?: boolean; + enforcement?: string; + scope?: Partial; + rules?: Record; +}; + +export class CodeReportSecurityPolicyService { + constructor(private readonly repository: CodeReportSecurityPolicyRepository) {} + + async listByProject(projectId: string) { + const policies = await this.repository.findByProjectId(projectId); + return policies.map((policy) => policy.toJson()); + } + + async getById(id: string) { + const policy = await this.repository.findById(id); + if (!policy) { + throw new Error('Security policy not found'); + } + return policy.toJson(); + } + + async create(projectId: string, input: SecurityPolicyInput) { + const name = input.name?.trim(); + if (!name) { + throw new Error('El nombre de la política es obligatorio'); + } + + const type = this.normalizeType(input.type); + const slug = this.normalizeSlug(input.slug || name); + if (!slug) { + throw new Error('El slug de la política es obligatorio'); + } + + const existing = await this.repository.findBySlug(projectId, slug); + if (existing) { + throw new Error('Ya existe una política con este slug en el proyecto'); + } + + const id = crypto.randomUUID(); + await this.repository.create({ + id, + projectId, + slug, + name, + description: input.description?.trim() || undefined, + type, + enabled: input.enabled ?? true, + enforcement: this.normalizeEnforcement(input.enforcement), + scope: this.normalizeScope(input.scope), + rules: this.normalizeRules(type, input.rules), + }); + + return this.getById(id); + } + + async update(id: string, changes: Partial) { + const policy = await this.repository.findById(id); + if (!policy) { + throw new Error('Security policy not found'); + } + + const patch: Parameters[1] = {}; + + if (changes.name !== undefined) { + const name = changes.name.trim(); + if (!name) { + throw new Error('El nombre de la política es obligatorio'); + } + patch.name = name; + } + + if (changes.slug !== undefined) { + const slug = this.normalizeSlug(changes.slug); + if (!slug) { + throw new Error('El slug de la política es obligatorio'); + } + const existing = await this.repository.findBySlug(policy.projectId, slug); + if (existing && existing.id !== id) { + throw new Error('Ya existe una política con este slug en el proyecto'); + } + patch.slug = slug; + } + + if (changes.description !== undefined) { + patch.description = changes.description.trim(); + } + + const type = changes.type !== undefined ? this.normalizeType(changes.type) : policy.type; + if (changes.type !== undefined) { + patch.type = type; + } + + if (changes.enabled !== undefined) { + patch.enabled = Boolean(changes.enabled); + } + + if (changes.enforcement !== undefined) { + patch.enforcement = this.normalizeEnforcement(changes.enforcement); + } + + if (changes.scope !== undefined) { + patch.scope = this.normalizeScope(changes.scope); + } + + if (changes.rules !== undefined || changes.type !== undefined) { + patch.rules = this.normalizeRules(type, changes.rules ?? policy.rules); + } + + await this.repository.update(id, patch); + return this.getById(id); + } + + async setEnabled(id: string, enabled: boolean) { + return this.update(id, { enabled }); + } + + async delete(id: string) { + const policy = await this.repository.findById(id); + if (!policy) { + throw new Error('Security policy not found'); + } + await this.repository.deleteById(id); + } + + private normalizeType(value: unknown): SecurityPolicyType { + if (!isSecurityPolicyType(value)) { + throw new Error('Tipo de política no soportado'); + } + return value; + } + + private normalizeEnforcement(value: unknown): SecurityPolicyEnforcement { + return SECURITY_POLICY_ENFORCEMENTS.includes(value as SecurityPolicyEnforcement) + ? (value as SecurityPolicyEnforcement) + : 'warn'; + } + + private normalizeScope(value: Partial | undefined): SecurityPolicyScope { + const scope = { ...defaultScope(), ...(value ?? {}) }; + const mode = SECURITY_POLICY_SCOPE_MODES.includes(scope.mode) ? scope.mode : 'all'; + return { + mode, + services: mode === 'services' ? this.normalizeStringList(scope.services) : [], + tags: mode === 'tags' ? this.normalizeStringList(scope.tags) : [], + }; + } + + private normalizeRules( + type: SecurityPolicyType, + rules: Record | SecurityPolicyRules | undefined, + ): SecurityPolicyRules { + const input = (rules ?? {}) as Record; + + if (type === 'vulnerabilities') { + return { + maxCritical: this.normalizeCount(input.maxCritical), + maxHigh: this.normalizeCount(input.maxHigh), + maxMedium: this.normalizeCount(input.maxMedium), + maxLow: this.normalizeCount(input.maxLow), + minCvssScore: this.normalizeRange(input.minCvssScore, 0, 10), + ignoreUnfixed: Boolean(input.ignoreUnfixed), + maxAgeDays: this.normalizeCount(input.maxAgeDays), + ignoredCves: this.normalizeStringList(input.ignoredCves).map((cve) => cve.toUpperCase()), + }; + } + + if (type === 'license') { + return { + mode: input.mode === 'allowlist' ? 'allowlist' : 'denylist', + licenses: this.normalizeStringList(input.licenses), + allowUnknown: Boolean(input.allowUnknown), + }; + } + + if (type === 'code_coverage') { + return { + minTotalCoverage: this.normalizeRange(input.minTotalCoverage, 0, 100), + minPatchCoverage: this.normalizeRange(input.minPatchCoverage, 0, 100), + allowCoverageDrop: Boolean(input.allowCoverageDrop), + }; + } + + if (type === 'secrets') { + return { + maxSecrets: this.normalizeCount(input.maxSecrets), + blockVerifiedOnly: Boolean(input.blockVerifiedOnly), + ignoredRules: this.normalizeStringList(input.ignoredRules), + }; + } + + return defaultRulesFor(type); + } + + private normalizeCount(value: unknown): number | null { + if (value === null || value === undefined || value === '') return null; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed < 0) return null; + return Math.floor(parsed); + } + + private normalizeRange(value: unknown, min: number, max: number): number | null { + if (value === null || value === undefined || value === '') return null; + const parsed = Number(value); + if (!Number.isFinite(parsed)) return null; + return Math.min(max, Math.max(min, parsed)); + } + + private normalizeStringList(value: unknown): string[] { + if (!Array.isArray(value)) return []; + return [ + ...new Set( + value + .map((item) => String(item).trim()) + .filter((item) => item.length > 0), + ), + ]; + } + + private normalizeSlug(value: string): string { + return value + .trim() + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); + } +} diff --git a/src/modules/code-report/application/code-report.service.ts b/src/modules/code-report/application/code-report.service.ts index e2ae81c..6672bea 100644 --- a/src/modules/code-report/application/code-report.service.ts +++ b/src/modules/code-report/application/code-report.service.ts @@ -1,6 +1,28 @@ import crypto from 'crypto'; import { CodeReportServiceRepository } from '../infrastructure/repositories/code-report-service.repository'; import { ProjectService } from '../../projects/application/project.service'; + +export type RiskWeights = { + critical: number; + high: number; + medium: number; + low: number; +}; + +export type VulnerabilityTotals = { + critical: number; + high: number; + medium: number; + low: number; +}; + +export const DEFAULT_RISK_WEIGHTS: RiskWeights = { + critical: 10, + high: 6, + medium: 3, + low: 1, +}; + type AnalysisCleanup = { deleteAllByService(serviceId: string): Promise; }; @@ -17,6 +39,23 @@ export class CodeReportService { return services.map((service) => service.toJson()); } + async getRiskWeightsByProjectId(projectId: string): Promise { + const project = await this.projectService.getProject(projectId); + return this.resolveRiskWeights(project.settings); + } + + calculateRiskScore( + vulnerabilities: VulnerabilityTotals, + riskWeights: RiskWeights = DEFAULT_RISK_WEIGHTS, + ): number { + return ( + vulnerabilities.critical * riskWeights.critical + + vulnerabilities.high * riskWeights.high + + vulnerabilities.medium * riskWeights.medium + + vulnerabilities.low * riskWeights.low + ); + } + async getById(id: string) { const service = await this.repository.findById(id); if (!service) { @@ -164,4 +203,20 @@ export class CodeReportService { .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, ''); } + + private resolveRiskWeights(settings: any): RiskWeights { + const multipliers = settings?.['code-report']?.securityRiskMultipliers; + + return { + critical: this.toPositiveNumberOrDefault(multipliers?.critical, DEFAULT_RISK_WEIGHTS.critical), + high: this.toPositiveNumberOrDefault(multipliers?.high, DEFAULT_RISK_WEIGHTS.high), + medium: this.toPositiveNumberOrDefault(multipliers?.medium, DEFAULT_RISK_WEIGHTS.medium), + low: this.toPositiveNumberOrDefault(multipliers?.low, DEFAULT_RISK_WEIGHTS.low), + }; + } + + private toPositiveNumberOrDefault(value: unknown, fallback: number): number { + const parsed = Number(value); + return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback; + } } diff --git a/src/modules/code-report/domain/code-report-analysis.domain.ts b/src/modules/code-report/domain/code-report-analysis.domain.ts index a0fe708..ee463d5 100644 --- a/src/modules/code-report/domain/code-report-analysis.domain.ts +++ b/src/modules/code-report/domain/code-report-analysis.domain.ts @@ -1,4 +1,5 @@ import { Domain } from '$lib/server/domain/domain'; +import type { PolicyComplianceReport } from '$lib/code-report/policy-evaluation'; export type CodeReportAnalysisStatus = 'in_progress' | 'completed' | 'failed'; @@ -16,6 +17,7 @@ export class CodeReportAnalysisDomain extends Domain { public status: CodeReportAnalysisStatus = 'in_progress'; public result: unknown = null; public summary?: unknown = null; + public securityPolicies: PolicyComplianceReport | null = null; public error?: string | null = null; public gitInfo?: CodeReportGitInfo | null = null; @@ -26,6 +28,7 @@ export class CodeReportAnalysisDomain extends Domain { this.status = data.status; this.result = data.result ?? null; this.summary = data.summary ?? null; + this.securityPolicies = data.securityPolicies ?? null; this.error = data.error ?? null; this.gitInfo = data.gitInfo ?? null; } @@ -38,6 +41,7 @@ export class CodeReportAnalysisDomain extends Domain { status: this.status, result: this.result, summary: this.summary, + securityPolicies: this.securityPolicies, error: this.error, gitInfo: this.gitInfo, createdAt: this.createdAt, diff --git a/src/modules/code-report/domain/code-report-security-policy.domain.ts b/src/modules/code-report/domain/code-report-security-policy.domain.ts new file mode 100644 index 0000000..1df557d --- /dev/null +++ b/src/modules/code-report/domain/code-report-security-policy.domain.ts @@ -0,0 +1,54 @@ +import { Domain } from '$lib/server/domain/domain'; +import { ProjectDomain } from '../../projects/domain/project.domain'; +import { + defaultScope, + type SecurityPolicyEnforcement, + type SecurityPolicyRules, + type SecurityPolicyScope, + type SecurityPolicyType, +} from '$lib/code-report/security-policy'; + +export class CodeReportSecurityPolicyDomain extends Domain { + public projectId: string = ''; + public slug: string = ''; + public name: string = ''; + public description?: string | null = null; + public type: SecurityPolicyType = 'vulnerabilities'; + public enabled: boolean = true; + public enforcement: SecurityPolicyEnforcement = 'warn'; + public scope: SecurityPolicyScope = defaultScope(); + public rules: SecurityPolicyRules = {}; + public project: ProjectDomain | null = null; + + constructor(data: any) { + super(data); + this.projectId = data.projectId; + this.slug = data.slug; + this.name = data.name; + this.description = data.description; + this.type = data.type ?? 'vulnerabilities'; + this.enabled = data.enabled ?? true; + this.enforcement = data.enforcement ?? 'warn'; + this.scope = { ...defaultScope(), ...(data.scope ?? {}) }; + this.rules = data.rules ?? {}; + this.project = data.project ? new ProjectDomain(data.project) : null; + } + + toJson() { + return { + id: this.id, + projectId: this.projectId, + slug: this.slug, + name: this.name, + description: this.description ?? null, + type: this.type, + enabled: this.enabled, + enforcement: this.enforcement, + scope: this.scope, + rules: this.rules, + createdAt: this.createdAt, + updatedAt: this.updatedAt, + project: this.project ? this.project.toJson() : null, + }; + } +} diff --git a/src/modules/code-report/index.ts b/src/modules/code-report/index.ts index d5a1654..5b129fd 100644 --- a/src/modules/code-report/index.ts +++ b/src/modules/code-report/index.ts @@ -1,16 +1,23 @@ import { CodeReportService } from './application/code-report.service'; import { CodeReportAnalysisService } from './application/code-report-analysis.service'; import { CodeReportCveService } from './application/code-report-cve.service'; +import { CodeReportSecurityPolicyService } from './application/code-report-security-policy.service'; import { CodeReportServiceRepository } from './infrastructure/repositories/code-report-service.repository'; import { CodeReportAnalysisRepository } from './infrastructure/repositories/code-report-analysis.repository'; +import { CodeReportSecurityPolicyRepository } from './infrastructure/repositories/code-report-security-policy.repository'; import { projectService } from '../projects'; const codeReportServiceRepository = new CodeReportServiceRepository(); const codeReportAnalysisRepository = new CodeReportAnalysisRepository(); +const codeReportSecurityPolicyRepository = new CodeReportSecurityPolicyRepository(); +export const codeReportSecurityPolicyService = new CodeReportSecurityPolicyService( + codeReportSecurityPolicyRepository, +); export const codeReportAnalysisService = new CodeReportAnalysisService( codeReportAnalysisRepository, codeReportServiceRepository, + codeReportSecurityPolicyService, ); export const codeReportService = new CodeReportService( codeReportServiceRepository, diff --git a/src/modules/code-report/infrastructure/repositories/code-report-analysis.repository.ts b/src/modules/code-report/infrastructure/repositories/code-report-analysis.repository.ts index dc3201d..10e77d4 100644 --- a/src/modules/code-report/infrastructure/repositories/code-report-analysis.repository.ts +++ b/src/modules/code-report/infrastructure/repositories/code-report-analysis.repository.ts @@ -60,6 +60,7 @@ export class CodeReportAnalysisRepository extends Repository { status?: CodeReportAnalysisStatus; result?: unknown; summary?: unknown; + securityPolicies?: unknown; error?: string | null; gitInfo?: CodeReportGitInfo; }, diff --git a/src/modules/code-report/infrastructure/repositories/code-report-security-policy.repository.ts b/src/modules/code-report/infrastructure/repositories/code-report-security-policy.repository.ts new file mode 100644 index 0000000..4309540 --- /dev/null +++ b/src/modules/code-report/infrastructure/repositories/code-report-security-policy.repository.ts @@ -0,0 +1,87 @@ +import { Repository } from '$lib/server/infra/repository'; +import { CodeReportSecurityPolicyDomain } from '../../domain/code-report-security-policy.domain'; +import { CodeReportSecurityPolicyEntity } from '$lib/database/schemas'; +import type { + SecurityPolicyEnforcement, + SecurityPolicyRules, + SecurityPolicyScope, + SecurityPolicyType, +} from '$lib/code-report/security-policy'; + +export class CodeReportSecurityPolicyRepository extends Repository { + async findByProjectId(projectId: string): Promise { + const result = await this.db + .select() + .from(CodeReportSecurityPolicyEntity) + .where({ projectId }) + .orderBy('createdAt', 'desc'); + return result.rows.map((row: any) => new CodeReportSecurityPolicyDomain(row)); + } + + async findById(id: string): Promise { + const result = await this.db + .with({ project: true }) + .select() + .from(CodeReportSecurityPolicyEntity) + .where({ id }) + .limit(1); + const row = result.rows[0]; + return row ? new CodeReportSecurityPolicyDomain(row) : null; + } + + async findBySlug( + projectId: string, + slug: string, + ): Promise { + const result = await this.db + .select() + .from(CodeReportSecurityPolicyEntity) + .where({ projectId, slug }) + .limit(1); + const row = result.rows[0]; + return row ? new CodeReportSecurityPolicyDomain(row) : null; + } + + async create(input: { + id: string; + projectId: string; + slug: string; + name: string; + description?: string; + type: SecurityPolicyType; + enabled: boolean; + enforcement: SecurityPolicyEnforcement; + scope: SecurityPolicyScope; + rules: SecurityPolicyRules; + }): Promise { + await this.db.insert(CodeReportSecurityPolicyEntity).values({ + ...input, + description: input.description, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }); + } + + async update( + id: string, + changes: { + name?: string; + slug?: string; + description?: string; + type?: SecurityPolicyType; + enabled?: boolean; + enforcement?: SecurityPolicyEnforcement; + scope?: SecurityPolicyScope; + rules?: SecurityPolicyRules; + }, + ): Promise { + await this.db + .update(CodeReportSecurityPolicyEntity) + .set({ ...changes, updatedAt: new Date().toISOString() }) + .where({ id }); + } + + async deleteById(id: string): Promise { + await this.db.delete(CodeReportSecurityPolicyEntity).where({ id }); + } +} diff --git a/src/modules/projects/application/project.service.ts b/src/modules/projects/application/project.service.ts index 2dec5b8..1a69f28 100644 --- a/src/modules/projects/application/project.service.ts +++ b/src/modules/projects/application/project.service.ts @@ -1,6 +1,10 @@ import crypto from 'crypto'; import { ProjectRepository } from '../infrastructure/repositories/project.repostitory'; -import { DEFAULT_PROJECT_MODULES, type ProjectModules } from '../domain/project.domain'; +import { + DEFAULT_PROJECT_MODULES, + type ProjectModules, + type ProjectSettings, +} from '../domain/project.domain'; export type ProjectStatusValue = 'active' | 'inactive'; @@ -96,6 +100,7 @@ export class ProjectService { status?: string; modules?: Partial; organizationId?: string; + settings?: ProjectSettings; }, ) { const project = await this.repository.findById(id); @@ -110,6 +115,7 @@ export class ProjectService { status?: string; modules?: ProjectModules; organizationId?: string; + settings?: ProjectSettings; } = {}; if (changes.organizationId !== undefined) { @@ -154,6 +160,10 @@ export class ProjectService { patch.modules = this.sanitizeModules({ ...project.modules, ...changes.modules }); } + if (changes.settings !== undefined) { + patch.settings = changes.settings; + } + await this.repository.update(id, patch); const updated = await this.repository.findById(id); diff --git a/src/modules/projects/domain/project.domain.ts b/src/modules/projects/domain/project.domain.ts index 115ffdb..4fc2dca 100644 --- a/src/modules/projects/domain/project.domain.ts +++ b/src/modules/projects/domain/project.domain.ts @@ -18,12 +18,46 @@ export const DEFAULT_PROJECT_MODULES: ProjectModules = { stateiac: true, }; +export interface ProjectSettings { + 'code-report': { + securityRiskMultipliers: { + critical: number; + high: number; + medium: number; + low: number; + }; + tools: { + id: string; + name: string; + description: string; + enabled: boolean; + scanner?: string; + soon?: boolean; + }[]; + }; +} + +export const DEFAULT_PROJECT_SETTINGS: ProjectSettings = { + 'code-report': { + securityRiskMultipliers: { + critical: 10, + high: 6, + medium: 3, + low: 1, + }, + tools: [ + { id: 'trivy', name: 'Trivy', description: 'Comprehensive security scanner', enabled: true }, + ] + } +}; + export class ProjectDomain extends Domain { public name: string = ''; public slug: string | null = null; public description?: string | null = null; public status: ProjectStatus[keyof ProjectStatus] = 'active'; public modules: ProjectModules = { ...DEFAULT_PROJECT_MODULES }; + public settings: ProjectSettings = { ...DEFAULT_PROJECT_SETTINGS }; public organization: OrganizationDomain | null = null; constructor(data: any) { super(data); @@ -32,6 +66,7 @@ export class ProjectDomain extends Domain { this.description = data.description; this.status = data.status; this.modules = { ...DEFAULT_PROJECT_MODULES, ...(data.modules ?? {}) }; + this.settings = { ...DEFAULT_PROJECT_SETTINGS, ...(data.settings ?? {}) }; this.organization = data.organization ? new OrganizationDomain(data.organization) : null; } @@ -44,6 +79,7 @@ export class ProjectDomain extends Domain { createdAt: this.createdAt, status: this.status, modules: this.modules, + settings: this.settings, updatedAt: this.updatedAt, organization: this.organization ? this.organization.toJson() : null, }; diff --git a/src/modules/projects/infrastructure/repositories/project.repostitory.ts b/src/modules/projects/infrastructure/repositories/project.repostitory.ts index f237cd4..b22526a 100644 --- a/src/modules/projects/infrastructure/repositories/project.repostitory.ts +++ b/src/modules/projects/infrastructure/repositories/project.repostitory.ts @@ -1,5 +1,9 @@ import { Repository } from '$lib/server/infra/repository'; -import { ProjectDomain, type ProjectModules } from '../../domain/project.domain'; +import { + ProjectDomain, + type ProjectModules, + type ProjectSettings, +} from '../../domain/project.domain'; import { ProjectEntity } from '$lib/database/schemas'; export class ProjectRepository extends Repository { @@ -85,6 +89,7 @@ export class ProjectRepository extends Repository { status?: string; modules?: ProjectModules; organizationId?: string; + settings?: ProjectSettings; }, ): Promise { await this.db diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 1764cea..e6a7e53 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -20,6 +20,8 @@ export async function load({ locals, url }) { ? await organizationService.tryFindBySlug(orgSlugFromUrl) : await organizationService.getDefaultOrganization(); + const currentProjectSlug = url.pathname.match(/\/projects\/([^/]+)/)?.[1] ?? null; + const projects = locals.user ? ( await Promise.all( @@ -47,5 +49,6 @@ export async function load({ locals, url }) { user: locals.user, organization, projects, + currentProjectSlug, }; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9cd36f5..44bdcb0 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -25,12 +25,12 @@ class="min-h-screen bg-slate-50 text-slate-900 font-sans flex flex-col" style={`--sidebar-width:${sidebarCollapsed ? '96px' : '340px'}`} > - {#if $page.url.pathname !== '/login'} + {#if $page?.url?.pathname !== '/login'}
@@ -57,7 +58,7 @@ {/if} - {#if $page.url.pathname !== '/login'} + {#if $page?.url?.pathname !== '/login'}
{/if}
diff --git a/src/routes/api/backends/+server.ts b/src/routes/api/backends/+server.ts deleted file mode 100644 index 7d3de8b..0000000 --- a/src/routes/api/backends/+server.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { storageBackendService } from '../../../modules/config'; -import { cancanService } from '../../../modules/auth'; - -export async function GET({ locals }) { - if (!(await cancanService.canSessionUser(locals.user, 'stateiac:read', { scope: 'cluster' }))) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - return json({ backends: storageBackendService.list() }); -} - -export async function POST({ request, locals }) { - try { - const data = await request.json(); - - if ( - !(await cancanService.canSessionUser( - locals.user, - data.id ? 'stateiac:update' : 'stateiac:create', - { scope: 'cluster' }, - )) - ) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - const id = storageBackendService.upsert({ - id: data.id, - name: data.name, - provider: data.provider, - bucket: data.bucket, - region: data.region || null, - accessKeyId: data.accessKeyId || null, - secretAccessKey: data.secretAccessKey || null, - endpoint: data.endpoint || null, - gcpProjectId: data.gcpProjectId || null, - gcpCredentials: data.gcpCredentials || null, - }); - - return json({ success: true, id }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/backends/[id]/+server.ts b/src/routes/api/backends/[id]/+server.ts deleted file mode 100644 index 9bfff35..0000000 --- a/src/routes/api/backends/[id]/+server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { storageBackendService } from '../../../../modules/config'; -import { cancanService } from '../../../../modules/auth'; - -export async function DELETE({ params, locals }) { - if (!(await cancanService.canSessionUser(locals.user, 'stateiac:delete', { scope: 'cluster' }))) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - storageBackendService.deleteById(params.id); - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/code-report/scan/+server.ts b/src/routes/api/code-report/scan/+server.ts index b8d553d..5d4d61b 100644 --- a/src/routes/api/code-report/scan/+server.ts +++ b/src/routes/api/code-report/scan/+server.ts @@ -1,6 +1,48 @@ import { json } from '@sveltejs/kit'; import { codeReportService, codeReportAnalysisService } from '../../../../modules/code-report'; import { apiKeysService } from '../../../../modules/auth'; +import { projectService } from '../../../../modules/projects'; + +type ProjectSettingsTool = { + id: string; + name: string; + description: string; + enabled: boolean; + scanner?: string; + soon?: boolean; +}; + +const DEFAULT_CODE_REPORT_TOOLS: ProjectSettingsTool[] = [ + { + id: 'trivy', + name: 'Vulnerabilidades (Trivy)', + description: 'Escaneo de vulnerabilidades y severidad de dependencias con Trivy.', + enabled: true, + scanner: 'trivy', + }, + { + id: 'syft', + name: 'Dependencias, SBOM y Licencias (Syft)', + description: 'Inventario de dependencias, generación de SBOM y análisis de licencias con Syft.', + enabled: true, + scanner: 'syft', + }, + { + id: 'gitleaks', + name: 'Secretos (Gitleaks)', + description: 'Detección de secretos expuestos en el repositorio con Gitleaks.', + enabled: true, + scanner: 'gitleaks', + }, + { + id: 'code-coverage', + name: 'Code Coverage', + description: 'Métricas de cobertura de código por servicio.', + enabled: false, + scanner: 'code-coverage', + soon: true, + }, +]; type AnalyseResultBody = { service: string; @@ -72,7 +114,8 @@ export async function POST({ request }) { let message = ''; let analysis; - let tools = [] + let tools: string[] = []; + let activeSettingsTools: ProjectSettingsTool[] = []; if(['start', 'in_progress'].includes(status)) { //use service and project to find or create the service if (!body.service) { @@ -82,6 +125,38 @@ export async function POST({ request }) { return json({ error: 'project is required' }, { status: 400 }); } + const project = await projectService.getProjectBySlug(body.project).catch(() => null); + if (!project) { + return json({ error: 'project not found' }, { status: 404 }); + } + + const codeReportSettings = project.settings?.['code-report'] || {}; + const persistedTools = Array.isArray(codeReportSettings.tools) + ? (codeReportSettings.tools as ProjectSettingsTool[]) + : []; + const persistedById = new Map( + persistedTools + .filter((tool) => tool && typeof tool.id === 'string') + .map((tool) => [tool.id, tool]), + ); + + const resolvedTools = DEFAULT_CODE_REPORT_TOOLS.map((tool) => { + const persisted = persistedById.get(tool.id); + return { + ...tool, + enabled: persisted?.enabled ?? tool.enabled, + }; + }); + + activeSettingsTools = resolvedTools.filter((tool) => tool.enabled && !tool.soon); + + const configuredScanners = activeSettingsTools + .map((tool) => tool.scanner || tool.id) + .filter(Boolean) + .map((scanner) => scanner.toLowerCase()); + + tools = [...new Set(configuredScanners)]; + let serviceCodeReport = await codeReportService.getByProjectAndSlug(body.project, body.service).catch(async (err) => { console.log('err', err) return await codeReportService.createService({ @@ -89,7 +164,9 @@ export async function POST({ request }) { name: body.service, }); }); - tools = serviceCodeReport?.tools || [] + if (tools.length === 0) { + tools = serviceCodeReport?.tools || []; + } console.log('✅ Service found or created:', serviceCodeReport); if(status === 'start') { message = 'Scan started'; @@ -132,5 +209,6 @@ export async function POST({ request }) { id: analysis?.id }, tools, + activeSettingsTools, }); } diff --git a/src/routes/api/organizations/+server.ts b/src/routes/api/organizations/+server.ts deleted file mode 100644 index bd9ca2f..0000000 --- a/src/routes/api/organizations/+server.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { organizationService } from '../../../modules/organization'; -import { cancanService } from '../../../modules/auth'; - -export async function GET({ locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const organizations = await organizationService.listOrganizations(); - return json({ organizations }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 500 }); - } -} - -export async function POST({ request, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { - name?: string; - slug?: string; - description?: string; - }; - - const organization = await organizationService.createOrganization({ - name: String(data.name || ''), - slug: data.slug ? String(data.slug) : undefined, - description: data.description ? String(data.description) : undefined, - }); - - return json({ success: true, organization }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/organizations/[id]/+server.ts b/src/routes/api/organizations/[id]/+server.ts deleted file mode 100644 index fc43873..0000000 --- a/src/routes/api/organizations/[id]/+server.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { organizationService } from '../../../../modules/organization'; -import { cancanService } from '../../../../modules/auth'; - -export async function GET({ params, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const organization = await organizationService.getOrganization(params.id); - return json({ organization }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 404 }); - } -} - -export async function PATCH({ request, params, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { - name?: string; - slug?: string; - description?: string; - }; - - const organization = await organizationService.updateOrganization(params.id, { - name: data.name, - slug: data.slug, - description: data.description, - }); - - return json({ success: true, organization }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} - -export async function DELETE({ params, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - await organizationService.deleteOrganization(params.id); - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/projects/+server.ts b/src/routes/api/projects/+server.ts deleted file mode 100644 index f94f721..0000000 --- a/src/routes/api/projects/+server.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { projectService } from '../../../modules/projects'; -import { cancanService } from '../../../modules/auth'; - -export async function GET({ url, locals }) { - if (!(await cancanService.canSessionUser(locals.user, 'stateiac:read', { scope: 'cluster' }))) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const organizationId = url.searchParams.get('organizationId'); - const projects = organizationId - ? await projectService.listProjectsByOrganization(organizationId) - : await projectService.listProjects(); - return json({ projects }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 500 }); - } -} - -export async function POST({ request, locals }) { - try { - const data = (await request.json()) as { - organizationId?: string; - name?: string; - slug?: string; - description?: string; - status?: string; - modules?: { vault?: boolean; openreport?: boolean; stateiac?: boolean }; - }; - - const canCreate = data.organizationId - ? await cancanService.canSessionUser(locals.user, 'stateiac:create', { - scope: 'organization', - organizationId: data.organizationId, - }) - : await cancanService.canSessionUser(locals.user, 'stateiac:create', { scope: 'cluster' }); - - if (!canCreate) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - const project = await projectService.createProject({ - organizationId: String(data.organizationId || ''), - name: String(data.name || ''), - slug: data.slug ? String(data.slug) : undefined, - description: data.description ? String(data.description) : undefined, - status: data.status ? String(data.status) : undefined, - modules: data.modules, - }); - - return json({ success: true, project }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/projects/[id]/+server.ts b/src/routes/api/projects/[id]/+server.ts deleted file mode 100644 index 211c975..0000000 --- a/src/routes/api/projects/[id]/+server.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { projectService } from '../../../../modules/projects'; -import { cancanService } from '../../../../modules/auth'; - -export async function GET({ params, locals }) { - try { - const project = await projectService.getProject(params.id); - if ( - !(await cancanService.canSessionUser(locals.user, 'stateiac:read', { - scope: 'project', - projectId: project.id, - organizationId: project.organization?.id, - })) - ) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - return json({ project }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 404 }); - } -} - -export async function PATCH({ request, params, locals }) { - try { - const currentProject = await projectService.getProject(params.id); - if ( - !(await cancanService.canSessionUser(locals.user, 'stateiac:update', { - scope: 'project', - projectId: currentProject.id, - organizationId: currentProject.organization?.id, - })) - ) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - const data = (await request.json()) as { - organizationId?: string; - name?: string; - slug?: string; - description?: string; - status?: string; - modules?: { vault?: boolean; openreport?: boolean; stateiac?: boolean }; - }; - - const project = await projectService.updateProject(params.id, { - organizationId: data.organizationId, - name: data.name, - slug: data.slug, - description: data.description, - status: data.status, - modules: data.modules, - }); - - return json({ success: true, project }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} - -export async function DELETE({ params, locals }) { - try { - const project = await projectService.getProject(params.id); - if ( - !(await cancanService.canSessionUser(locals.user, 'stateiac:delete', { - scope: 'project', - projectId: project.id, - organizationId: project.organization?.id, - })) - ) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - await projectService.deleteProject(params.id); - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/projects/sync/+server.ts b/src/routes/api/projects/sync/+server.ts deleted file mode 100644 index 0567557..0000000 --- a/src/routes/api/projects/sync/+server.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { storageBackendService } from '../../../../modules/config'; -import { projectService } from '../../../../modules/projects'; -import { storageService } from '../../../../modules/storage'; -import { cancanService } from '../../../../modules/auth'; - -export async function POST({ cookies, locals }) { - if (!(await cancanService.canSessionUser(locals.user, 'stateiac:update', { scope: 'cluster' }))) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const backends = storageBackendService.list(); - if (!backends.length) throw new Error('No storage configured'); - - const activeId = cookies.get('active_backend') || backends[0].id; - const config = - storageBackendService.getById(activeId) || storageBackendService.getById(backends[0].id); - - if (!config) throw new Error('No storage configured'); - - const { states: files } = await storageService.listPulumiStates(config); - const count = projectService.syncFromPulumiStateKeys(files); - - return json({ success: true, count }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Unknown error'; - return json({ error: message }, { status: 500 }); - } -} diff --git a/src/routes/api/roles/+server.ts b/src/routes/api/roles/+server.ts deleted file mode 100644 index 57f216a..0000000 --- a/src/routes/api/roles/+server.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { roleService, cancanService } from '../../../modules/auth'; - -export async function GET({ locals, url }) { - if (!locals.user) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const scope = (url.searchParams.get('scope') ?? 'cluster') as - 'cluster' | 'organization' | 'project'; - const scopeId = - url.searchParams.get('organizationId') ?? url.searchParams.get('projectId') ?? undefined; - const roles = await roleService.listRoles(scope, scopeId); - return json({ roles }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to list roles'; - return json({ error: message }, { status: 500 }); - } -} - -export async function POST({ request, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { - name?: string; - slug?: string; - permissions?: string[]; - scope?: 'cluster' | 'organization' | 'project'; - organizationId?: string; - projectId?: string; - }; - - const role = await roleService.createRole({ - name: data.name ?? '', - slug: data.slug ?? '', - permissions: Array.isArray(data.permissions) ? data.permissions : [], - scope: data.scope, - organizationId: data.organizationId, - projectId: data.projectId, - }); - - return json({ success: true, role }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to create role'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/roles/[id]/+server.ts b/src/routes/api/roles/[id]/+server.ts deleted file mode 100644 index c01eca5..0000000 --- a/src/routes/api/roles/[id]/+server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { roleService, cancanService } from '../../../../modules/auth'; - -export async function PATCH({ request, params, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { name?: string; permissions?: string[] }; - - const role = await roleService.updateRole(params.id, { - name: data.name, - permissions: data.permissions, - }); - - return json({ success: true, role }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to update role'; - return json({ error: message }, { status: 400 }); - } -} - -export async function DELETE({ params, locals }) { - if (!cancanService.canAccessAdminArea(locals.user)) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - await roleService.deleteRole(params.id); - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to delete role'; - return json({ error: message }, { status: 400 }); - } -} diff --git a/src/routes/api/users/+server.ts b/src/routes/api/users/+server.ts deleted file mode 100644 index 7023aef..0000000 --- a/src/routes/api/users/+server.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { userService } from '../../../modules/auth'; - -export async function GET({ locals }) { - if (!locals.user) { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const users = await userService.listUsers(); - - return json({ users }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to list users'; - return json({ error: message }, { status: 500 }); - } -} - -export async function POST({ request, locals }) { - if (!locals.user || locals.user.role !== 'admin') { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { username?: string; password?: string; role?: string }; - - const username = data.username?.trim() || ''; - const password = data.password?.trim() || ''; - const role = data.role === 'admin' ? 'admin' : 'developer'; - - if (!username) { - return json({ error: 'Username is required' }, { status: 400 }); - } - - if (!password) { - return json({ error: 'Password is required' }, { status: 400 }); - } - - const user = await userService.createUser({ - username, - password, - role, - email: null, - }); - - return json({ - success: true, - user, - }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to create user'; - - if (message.includes('UNIQUE constraint failed')) { - return json({ error: 'Username already exists' }, { status: 400 }); - } - - return json({ error: message }, { status: 400 }); - } -} \ No newline at end of file diff --git a/src/routes/api/users/[id]/+server.ts b/src/routes/api/users/[id]/+server.ts deleted file mode 100644 index f792534..0000000 --- a/src/routes/api/users/[id]/+server.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { json } from '@sveltejs/kit'; -import { userManagementService } from '../../../../modules/auth'; - -export async function PATCH({ request, params, locals }) { - if (!locals.user || locals.user.role !== 'admin') { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - try { - const data = (await request.json()) as { password?: string; role?: string }; - const id = params.id; - - await userManagementService.updateUser({ - actorUserId: locals.user.id, - targetUserId: id, - password: data.password, - role: data.role === 'admin' ? 'admin' : data.role === 'developer' ? 'developer' : undefined, - }); - - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to update user'; - return json({ error: message }, { status: 400 }); - } -} - -export async function DELETE({ params, locals }) { - if (!locals.user || locals.user.role !== 'admin') { - return json({ error: 'Forbidden' }, { status: 403 }); - } - - const id = params.id; - - if (id === locals.user.id) { - return json({ error: 'You cannot delete your own account.' }, { status: 400 }); - } - - try { - await userManagementService.deleteUser(locals.user.id, id); - return json({ success: true }); - } catch (error: unknown) { - const message = error instanceof Error ? error.message : 'Failed to delete user'; - return json({ error: message }, { status: 400 }); - } -} \ No newline at end of file diff --git a/src/routes/cluster-settings/orgs/+page.server.ts b/src/routes/cluster-settings/orgs/+page.server.ts new file mode 100644 index 0000000..553e657 --- /dev/null +++ b/src/routes/cluster-settings/orgs/+page.server.ts @@ -0,0 +1,44 @@ +import { fail } from '@sveltejs/kit'; +import { cancanService } from '../../../modules/auth'; +import { organizationService } from '../../../modules/organization'; + +function errorResponse(error: unknown) { + return fail(400, { + error: error instanceof Error ? error.message : 'Organization action failed.', + }); +} + +export async function load() { + const organizations = await organizationService.listOrganizations(); + return { organizations }; +} + +export const actions = { + async createOrganization({ request, locals }) { + if (!cancanService.canAccessAdminArea(locals.user)) return fail(403, { error: 'Forbidden' }); + + try { + const form = await request.formData(); + const organization = await organizationService.createOrganization({ + name: String(form.get('name') ?? ''), + slug: String(form.get('slug') ?? '') || undefined, + description: String(form.get('description') ?? '') || undefined, + }); + return { success: true, organization }; + } catch (error: unknown) { + return errorResponse(error); + } + }, + + async deleteOrganization({ request, locals }) { + if (!cancanService.canAccessAdminArea(locals.user)) return fail(403, { error: 'Forbidden' }); + + try { + const form = await request.formData(); + await organizationService.deleteOrganization(String(form.get('id') ?? '')); + return { success: true }; + } catch (error: unknown) { + return errorResponse(error); + } + }, +}; diff --git a/src/routes/cluster-settings/orgs/+page.svelte b/src/routes/cluster-settings/orgs/+page.svelte index a481aac..0aaa9f1 100644 --- a/src/routes/cluster-settings/orgs/+page.svelte +++ b/src/routes/cluster-settings/orgs/+page.svelte @@ -1,5 +1,6 @@ @@ -130,7 +117,13 @@ {/if} -
+
+

Información

- - -
- - -
+
{/if} @@ -425,38 +405,40 @@
Delete project
- {#if deleteError} -
-
- {deleteError} +
+ + {#if deleteError} +
+
+ {deleteError} +
-
- {/if} + {/if} -
- Are you sure you want to delete {deleteModalProject.name}? This action cannot be undone. -
+
+ Are you sure you want to delete {deleteModalProject.name}? This action cannot be undone. +
-
- - -
+
+ + +
+
{/if} diff --git a/src/routes/org/[org]/settings/storage/+page.server.ts b/src/routes/org/[org]/settings/storage/+page.server.ts index 2b26af8..0a44aac 100644 --- a/src/routes/org/[org]/settings/storage/+page.server.ts +++ b/src/routes/org/[org]/settings/storage/+page.server.ts @@ -2,4 +2,4 @@ import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(302, '/pulumi-state/backends'); -} \ No newline at end of file +} diff --git a/src/routes/org/[org]/settings/storage/+page.svelte b/src/routes/org/[org]/settings/storage/+page.svelte deleted file mode 100644 index e95fcee..0000000 --- a/src/routes/org/[org]/settings/storage/+page.svelte +++ /dev/null @@ -1,483 +0,0 @@ - - -
-
-
-

Storage Backends

-

Configure where your Pulumi state files are retrieved from.

-
- -
- class="btn-ghost p-1.5 text-gray-400 hover:text-red-600 rounded transition-colors" - {#if isLoading} -
- -
- {:else if backends.length === 0} -
- -

No backends configured

-

Add your first S3 or GCS bucket to start syncing states.

- -
- {:else} -
- {#each backends as backend} -
-
-
-

{backend.name}

-

- {backend.provider === 's3' ? 'AWS S3 / Compatible' : 'Google Cloud Storage'} -

-
-
- - -
-
-
-

- Bucket: - {backend.bucket} -

-
-
- {/each} -
- {/if} - - {#if isEditing} - - -
- -
- {/if} - - {#if isDeleteModalOpen} - - -
- -
- {/if} -