From 161c9146f4d4f2edb7e106c1a2162fae4f2df3d8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 28 Feb 2026 20:49:49 +0000 Subject: [PATCH 1/8] fix: add missing dependencies and db export alias - Add @neondatabase/serverless to package.json (was imported but missing) - Add @stack-auth/nextjs local stub for auth mock in local dev - Add pg Pool-based 'db' export to lib/db.ts (used by API routes) - Make 'sql' export use pg Pool for local PostgreSQL compatibility Co-authored-by: DealPatrol --- lib/db.ts | 11 +- package.json | 10 +- pnpm-lock.yaml | 3338 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 3207 insertions(+), 152 deletions(-) diff --git a/lib/db.ts b/lib/db.ts index 8f0c7b3..9a6f9b4 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,5 +1,10 @@ -import { neon } from '@neondatabase/serverless'; +import { Pool } from 'pg'; -const sql = neon(process.env.DATABASE_URL || ''); +const db = new Pool({ connectionString: process.env.DATABASE_URL || '' }); -export { sql }; +async function sql(query: string, params?: any[]): Promise { + const result = await db.query(query, params); + return result.rows; +} + +export { sql, db }; diff --git a/package.json b/package.json index 3633a57..9757817 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ }, "dependencies": { "@hookform/resolvers": "^3.9.1", - "@vercel/analytics": "1.6.1", - "@vercel/blob": "^0.21.0", + "@neondatabase/serverless": "^1.0.2", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-aspect-ratio": "1.1.8", @@ -39,6 +38,9 @@ "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", + "@stack-auth/nextjs": "file:stubs/stack-auth-nextjs", + "@vercel/analytics": "1.6.1", + "@vercel/blob": "^0.21.0", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -62,10 +64,14 @@ "zod": "^3.24.1" }, "devDependencies": { + "@eslint/eslintrc": "^3.3.4", + "@eslint/js": "^9.39.3", "@tailwindcss/postcss": "^4.2.0", "@types/node": "^22", "@types/react": "19.2.14", "@types/react-dom": "19.2.3", + "eslint": "^9.39.3", + "eslint-config-next": "^16.1.6", "postcss": "^8.5", "tailwindcss": "^4.2.0", "tw-animate-css": "1.3.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d726aae..0673408 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@hookform/resolvers': specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.71.2(react@19.2.4)) + '@neondatabase/serverless': + specifier: ^1.0.2 + version: 1.0.2 '@radix-ui/react-accordion': specifier: 1.2.12 version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -92,9 +95,12 @@ importers: '@radix-ui/react-tooltip': specifier: 1.2.8 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@stack-auth/nextjs': + specifier: file:stubs/stack-auth-nextjs + version: file:stubs/stack-auth-nextjs '@vercel/analytics': specifier: 1.6.1 - version: 1.6.1(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 1.6.1(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) '@vercel/blob': specifier: ^0.21.0 version: 0.21.0 @@ -124,7 +130,7 @@ importers: version: 0.564.0(react@19.2.4) next: specifier: 16.1.6 - version: 16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -162,6 +168,12 @@ importers: specifier: ^3.24.1 version: 3.25.76 devDependencies: + '@eslint/eslintrc': + specifier: ^3.3.4 + version: 3.3.4 + '@eslint/js': + specifier: ^9.39.3 + version: 9.39.3 '@tailwindcss/postcss': specifier: ^4.2.0 version: 4.2.0 @@ -174,6 +186,12 @@ importers: '@types/react-dom': specifier: 19.2.3 version: 19.2.3(@types/react@19.2.14) + eslint: + specifier: ^9.39.3 + version: 9.39.3(jiti@2.6.1) + eslint-config-next: + specifier: ^16.1.6 + version: 16.1.6(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) postcss: specifier: ^8.5 version: 8.5.6 @@ -193,16 +211,127 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@date-fns/tz@1.4.1': resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/runtime@1.8.1': resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + 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.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + 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.4': + resolution: {integrity: sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.3': + resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} + 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} + '@fastify/busboy@2.1.1': resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} @@ -227,6 +356,22 @@ packages: peerDependencies: react-hook-form: ^7.0.0 + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + 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'} + '@img/colour@1.0.0': resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} @@ -396,9 +541,19 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@neondatabase/serverless@1.0.2': + resolution: {integrity: sha512-I5sbpSIAHiB+b6UttofhrN/UJXII+4tZPAq1qugzwCwLIL8EZLV7F/JyHUrEIiGgQpEXzpnjlJ+zwcEhheGvCw==} + engines: {node: '>=19.0.0'} + '@next/env@16.1.6': resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} + '@next/eslint-plugin-next@16.1.6': + resolution: {integrity: sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==} + '@next/swc-darwin-arm64@16.1.6': resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==} engines: {node: '>= 10'} @@ -451,6 +606,22 @@ packages: cpu: [x64] os: [win32] + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1107,6 +1278,12 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@stack-auth/nextjs@file:stubs/stack-auth-nextjs': + resolution: {directory: stubs/stack-auth-nextjs, type: directory} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1202,6 +1379,9 @@ packages: '@tailwindcss/postcss@4.2.0': resolution: {integrity: sha512-u6YBacGpOm/ixPfKqfgrJEjMfrYmPD7gEFRoygS/hnQaRtV0VCBdpkx5Ouw9pnaLRwwlgGCuJw8xLpaR0hOrQg==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -1229,9 +1409,21 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/node@22.19.11': resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} + '@types/pg@8.18.0': + resolution: {integrity: sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -1240,6 +1432,168 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + 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.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + 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.0.0' + + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + 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.0.0' + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + '@vercel/analytics@1.6.1': resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==} peerDependencies: @@ -1270,10 +1624,73 @@ packages: resolution: {integrity: sha512-uTVskvD94ZtKsX28M6TSt6GJKe7TOumys4H6GMwa+olBsQt9WGx6+ex1g+OR8bjhnA3EuF8MCehM3ZMtIblUDg==} engines: {node: '>=16.14'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} @@ -1284,11 +1701,41 @@ packages: peerDependencies: postcss: ^8.1.0 + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} + engines: {node: '>=4'} + + 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} + baseline-browser-mapping@2.10.0: resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} engines: {node: '>=6.0.0'} hasBin: true + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1298,9 +1745,29 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001770: resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -1317,6 +1784,23 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + 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==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -1364,15 +1848,58 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + date-fns-jalali@4.1.0-0: resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1380,9 +1907,17 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + electron-to-chromium@1.5.302: resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} @@ -1399,64 +1934,539 @@ packages: embla-carousel@8.6.0: resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} - fast-equals@5.4.0: - resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} - engines: {node: '>=6.0.0'} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} - fraction.js@5.3.4: - resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} + engines: {node: '>= 0.4'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} - input-otp@1.4.2: - resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} - peerDependencies: - react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] + eslint-config-next@16.1.6: + resolution: {integrity: sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + 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.3: + resolution: {integrity: sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.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'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + 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'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + 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.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + 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@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + 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==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + 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.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + input-otp@1.4.2: + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + 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==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.31.1: @@ -1515,6 +2525,13 @@ packages: resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} + 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==} + lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -1522,6 +2539,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.564.0: resolution: {integrity: sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==} peerDependencies: @@ -1530,11 +2550,44 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: @@ -1562,6 +2615,10 @@ packages: sass: optional: true + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -1569,6 +2626,65 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + 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-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + pg-cloudflare@1.3.0: resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} @@ -1606,6 +2722,18 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -1633,9 +2761,20 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-day-picker@9.13.2: resolution: {integrity: sha512-IMPiXfXVIAuR5Yk58DDPBC8QKClrhdXV+Tr/alBrwrHUw0qDDYB1m5zPNuTnnPIr/gmJ4ChMxmtqPdxm8+R4Eg==} engines: {node: '>=18'} @@ -1721,22 +2860,106 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - sharp@0.34.5: + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + sonner@1.7.4: resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} peerDependencies: @@ -1751,6 +2974,44 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -1764,6 +3025,14 @@ packages: babel-plugin-macros: optional: true + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} @@ -1777,17 +3046,65 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tw-animate-css@1.3.3: resolution: {integrity: sha512-tXE2TRWrskc4TU3RDd7T8n8Np/wCfoeH9gz22c7PzYqNPQ9FBGFbWWzwL0JyHcFp+jHozmF76tbHfPAx22ua2Q==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.56.1: + resolution: {integrity: sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==} + 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.0.0' + typescript@5.7.3: resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -1795,12 +3112,18 @@ packages: resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==} engines: {node: '>=14.0'} + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -1835,10 +3158,48 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -1846,15 +3207,172 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/runtime@7.28.6': {} + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@date-fns/tz@1.4.1': {} + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.6.1))': + dependencies: + eslint: 9.39.3(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + 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.4': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.3': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@fastify/busboy@2.1.1': {} '@floating-ui/core@1.7.4': @@ -1878,6 +3396,17 @@ snapshots: dependencies: react-hook-form: 7.71.2(react@19.2.4) + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@img/colour@1.0.0': optional: true @@ -1994,8 +3523,24 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@neondatabase/serverless@1.0.2': + dependencies: + '@types/node': 22.19.11 + '@types/pg': 8.18.0 + '@next/env@16.1.6': {} + '@next/eslint-plugin-next@16.1.6': + dependencies: + fast-glob: 3.3.1 + '@next/swc-darwin-arm64@16.1.6': optional: true @@ -2020,6 +3565,20 @@ snapshots: '@next/swc-win32-x64-msvc@16.1.6': optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -2715,6 +4274,10 @@ snapshots: '@radix-ui/rect@1.1.1': {} + '@rtsao/scc@1.1.0': {} + + '@stack-auth/nextjs@file:stubs/stack-auth-nextjs': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -2788,6 +4351,11 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.2.0 + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -2812,10 +4380,22 @@ snapshots: '@types/d3-timer@3.0.2': {} + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + '@types/node@22.19.11': dependencies: undici-types: 6.21.0 + '@types/pg@8.18.0': + dependencies: + '@types/node': 22.19.11 + pg-protocol: 1.11.0 + pg-types: 2.2.0 + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -2824,164 +4404,1153 @@ snapshots: dependencies: csstype: 3.2.3 - '@vercel/analytics@1.6.1(next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 9.39.3(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + eslint: 9.39.3(jiti@2.6.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.7.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.7.3) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.7.3)': + dependencies: + typescript: 5.7.3 + + '@typescript-eslint/type-utils@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + debug: 4.4.3 + eslint: 9.39.3(jiti@2.6.1) + ts-api-utils: 2.4.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.56.1': {} + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.7.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.7.3) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.7.3) + eslint: 9.39.3(jiti@2.6.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vercel/analytics@1.6.1(next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': optionalDependencies: - next: 16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.1.6(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + + '@vercel/blob@0.21.0': + dependencies: + async-retry: 1.3.3 + bytes: 3.1.2 + undici: 5.28.2 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.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 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + + autoprefixer@10.4.24(postcss@8.5.6): + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001770 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.1: {} + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001770 + electron-to-chromium: 1.5.302 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001770: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + date-fns-jalali@4.1.0-0: {} + + date-fns@4.1.0: {} + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js-light@2.5.1: {} + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.28.6 + csstype: 3.2.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.302: {} + + embla-carousel-react@8.6.0(react@19.2.4): + dependencies: + embla-carousel: 8.6.0 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) + react: 19.2.4 + + embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.19.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.2: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-object-atoms@1.1.1: + 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.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.1.6(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3): + dependencies: + '@next/eslint-plugin-next': 16.1.6 + eslint: 9.39.3(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.3(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.3(jiti@2.6.1)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.3(jiti@2.6.1)) + globals: 16.4.0 + typescript-eslint: 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.3(jiti@2.6.1) + get-tsconfig: 4.13.6 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + eslint: 9.39.3(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.3(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.3(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.3(jiti@2.6.1)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.1 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.3(jiti@2.6.1) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.0.1(eslint@9.39.3(jiti@2.6.1)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + eslint: 9.39.3(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.3(jiti@2.6.1)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.2 + eslint: 9.39.3(jiti@2.6.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.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.3(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.4 + '@eslint/js': 9.39.3 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + 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.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventemitter3@4.0.7: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@5.4.0: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.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.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fraction.js@5.3.4: {} + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + 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.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + input-otp@1.4.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) - '@vercel/blob@0.21.0': + internal-slot@1.1.0: dependencies: - async-retry: 1.3.3 - bytes: 3.1.2 - undici: 5.28.2 + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 - aria-hidden@1.2.6: + internmap@2.0.3: {} + + is-array-buffer@3.0.5: dependencies: - tslib: 2.8.1 + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 - async-retry@1.3.3: + is-async-function@2.1.1: dependencies: - retry: 0.13.1 + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 - autoprefixer@10.4.24(postcss@8.5.6): + is-bigint@1.1.0: dependencies: - browserslist: 4.28.1 - caniuse-lite: 1.0.30001770 - fraction.js: 5.3.4 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 + has-bigints: 1.1.0 - baseline-browser-mapping@2.10.0: {} + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - browserslist@4.28.1: + is-bun-module@2.0.0: dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001770 - electron-to-chromium: 1.5.302 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + semver: 7.7.4 - bytes@3.1.2: {} + is-callable@1.2.7: {} - caniuse-lite@1.0.30001770: {} + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 - class-variance-authority@0.7.1: + is-data-view@1.0.2: dependencies: - clsx: 2.1.1 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 - client-only@0.0.1: {} + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - clsx@2.1.1: {} + is-extglob@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + is-finalizationregistry@1.1.1: dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - csstype@3.2.3: {} + call-bound: 1.0.4 - d3-array@3.2.4: + is-generator-function@1.1.2: dependencies: - internmap: 2.0.3 + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 - d3-color@3.1.0: {} + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 - d3-ease@3.0.1: {} + is-map@2.0.3: {} - d3-format@3.1.2: {} + is-negative-zero@2.0.3: {} - d3-interpolate@3.0.1: + is-number-object@1.1.1: dependencies: - d3-color: 3.1.0 + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - d3-path@3.1.0: {} + is-number@7.0.0: {} - d3-scale@4.0.2: + is-regex@1.2.1: dependencies: - d3-array: 3.2.4 - d3-format: 3.1.2 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 - d3-shape@3.2.0: + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: dependencies: - d3-path: 3.1.0 + call-bound: 1.0.4 - d3-time-format@4.1.0: + is-string@1.1.1: dependencies: - d3-time: 3.1.0 + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - d3-time@3.1.0: + is-symbol@1.1.1: dependencies: - d3-array: 3.2.4 + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - d3-timer@3.0.1: {} + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 - date-fns-jalali@4.1.0-0: {} + is-weakmap@2.0.2: {} - date-fns@4.1.0: {} + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 - decimal.js-light@2.5.1: {} + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 - detect-libc@2.1.2: {} + isarray@2.0.5: {} - detect-node-es@1.1.0: {} + isexe@2.0.0: {} - dom-helpers@5.2.1: + iterator.prototype@1.1.5: dependencies: - '@babel/runtime': 7.28.6 - csstype: 3.2.3 + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 - electron-to-chromium@1.5.302: {} + jiti@2.6.1: {} - embla-carousel-react@8.6.0(react@19.2.4): - dependencies: - embla-carousel: 8.6.0 - embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) - react: 19.2.4 + js-tokens@4.0.0: {} - embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + js-yaml@4.1.1: dependencies: - embla-carousel: 8.6.0 - - embla-carousel@8.6.0: {} + argparse: 2.0.1 - enhanced-resolve@5.19.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 + jsesc@3.1.0: {} - escalade@3.2.0: {} + json-buffer@3.0.1: {} - eventemitter3@4.0.7: {} + json-schema-traverse@0.4.1: {} - fast-equals@5.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} - fraction.js@5.3.4: {} + json5@1.0.2: + dependencies: + minimist: 1.2.8 - get-nonce@1.0.1: {} + json5@2.2.3: {} - graceful-fs@4.2.11: {} + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 - input-otp@1.4.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + keyv@4.5.4: dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + json-buffer: 3.0.1 - internmap@2.0.3: {} + language-subtag-registry@0.3.23: {} - jiti@2.6.1: {} + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 - js-tokens@4.0.0: {} + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 lightningcss-android-arm64@1.31.1: optional: true @@ -3032,12 +5601,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + lodash@4.17.23: {} loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lucide-react@0.564.0(react@19.2.4): dependencies: react: 19.2.4 @@ -3046,14 +5625,39 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + + minimist@1.2.8: {} + + ms@2.1.3: {} + nanoid@3.3.11: {} + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + next-themes@0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - next@16.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.1.6(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -3062,7 +5666,7 @@ snapshots: postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - styled-jsx: 5.1.6(react@19.2.4) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -3077,10 +5681,90 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + node-releases@2.0.27: {} object-assign@4.1.1: {} + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + 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 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + 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-key@3.1.1: {} + + path-parse@1.0.7: {} + pg-cloudflare@1.3.0: optional: true @@ -3118,6 +5802,12 @@ snapshots: picocolors@1.1.1: {} + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -3142,12 +5832,18 @@ snapshots: dependencies: xtend: 4.0.2 + prelude-ls@1.2.1: {} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + react-day-picker@9.13.2(react@19.2.4): dependencies: '@date-fns/tz': 1.4.1 @@ -3236,12 +5932,99 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.9.2 + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + retry@0.13.1: {} + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + scheduler@0.27.0: {} - semver@7.7.4: - optional: true + semver@6.3.1: {} + + semver@7.7.4: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 sharp@0.34.5: dependencies: @@ -3275,6 +6058,40 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + sonner@1.7.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: react: 19.2.4 @@ -3284,10 +6101,79 @@ snapshots: split2@4.2.0: {} - styled-jsx@5.1.6(react@19.2.4): + stable-hash@0.0.5: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4): dependencies: client-only: 0.0.1 react: 19.2.4 + optionalDependencies: + '@babel/core': 7.29.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} tailwind-merge@3.5.0: {} @@ -3297,24 +6183,127 @@ snapshots: tiny-invariant@1.3.3: {} + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.4.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.8.1: {} tw-animate-css@1.3.3: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + '@typescript-eslint/parser': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.7.3) + '@typescript-eslint/utils': 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.7.3) + eslint: 9.39.3(jiti@2.6.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + typescript@5.7.3: {} + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + undici-types@6.21.0: {} undici@5.28.2: dependencies: '@fastify/busboy': 2.1.1 + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.4): dependencies: react: 19.2.4 @@ -3360,6 +6349,61 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + xtend@4.0.2: {} + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} From d152a4814c1c98807e260005687f38c2a99d3972 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 28 Feb 2026 20:49:57 +0000 Subject: [PATCH 2/8] chore: add dev environment setup files - Add @stack-auth/nextjs stub module with server/client exports - Add ESLint flat config (eslint.config.mjs) with basic rules - Add AGENTS.md with Cursor Cloud specific instructions - Add next-env.d.ts (auto-generated by Next.js) - Update tsconfig.json (auto-updated by Next.js) Co-authored-by: DealPatrol --- AGENTS.md | 45 ++++++++++++++++++++++++ eslint.config.mjs | 51 ++++++++++++++++++++++++++++ next-env.d.ts | 6 ++++ stubs/stack-auth-nextjs/index.d.ts | 9 +++++ stubs/stack-auth-nextjs/index.js | 18 ++++++++++ stubs/stack-auth-nextjs/package.json | 12 +++++++ stubs/stack-auth-nextjs/server.js | 16 +++++++++ tsconfig.json | 24 ++++++++++--- 8 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 AGENTS.md create mode 100644 eslint.config.mjs create mode 100644 next-env.d.ts create mode 100644 stubs/stack-auth-nextjs/index.d.ts create mode 100644 stubs/stack-auth-nextjs/index.js create mode 100644 stubs/stack-auth-nextjs/package.json create mode 100644 stubs/stack-auth-nextjs/server.js diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..87b0992 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# AGENTS.md + +## Cursor Cloud specific instructions + +### Overview + +TaskFlow is a Next.js 16 project management SaaS (single app, no monorepo). Tech stack: Next.js 16 + Turbopack, React 19, Tailwind CSS v4, Shadcn UI, PostgreSQL, pnpm. + +### Local database setup + +A local PostgreSQL 16 instance is used in place of Neon's serverless PostgreSQL. The database is pre-seeded with mock `neon_auth` schema tables, a sample organization, user, project, and tasks. RLS is disabled locally. + +- **Start PostgreSQL** (if not running): `sudo pg_ctlcluster 16 main start` +- **Connection**: `postgresql://taskflow:taskflow@localhost:5432/taskflow` +- **Schema**: `scripts/01-create-schema.sql` (requires `neon_auth` schema tables created first) + +### Auth stub + +The `@stack-auth/nextjs` package does not exist on npm. A local stub at `stubs/stack-auth-nextjs/` provides mock `currentUser()` and `useUser()` functions that return a hardcoded dev user (UUID `00000000-0000-0000-0000-000000000002`, org `00000000-0000-0000-0000-000000000001`). The stub uses `react-server` conditional exports to properly separate server/client code. + +### DB module (`lib/db.ts`) + +Exports two database interfaces: +- `sql(query, params)` โ€” function-call style, returns `rows` array (used by `lib/queries.ts`) +- `db` โ€” `pg.Pool` instance with `db.query()` (used by API routes under `app/api/`) + +Both use the local PostgreSQL via the `pg` package. The original `@neondatabase/serverless` neon driver is not used locally. + +### Common commands + +| Task | Command | +|------|---------| +| Install deps | `pnpm install` | +| Dev server | `pnpm dev` (port 3000) | +| Lint | `pnpm lint` | +| Build | `pnpm build` | +| Production | `pnpm start` | + +### Gotchas + +- The `@neondatabase/serverless` package is listed in `package.json` but not used at runtime locally. The `lib/db.ts` module wraps everything through `pg.Pool`. +- Some API routes (activity, analytics, members, attachments) import `{ db }` from `@/lib/db` and use `db.query()`. Other code (in `lib/queries.ts`) imports `{ sql }` and calls `sql()` as a function. Both patterns are supported. +- The ESLint config (`eslint.config.mjs`) uses `@eslint/js` with basic recommended rules. It doesn't include the full `next/core-web-vitals` config due to ESLint 9 compatibility issues. +- `next.config.mjs` has `typescript: { ignoreBuildErrors: true }`, so TypeScript errors won't block builds. +- There is no root page (`app/page.tsx`). The app entry point is `/dashboard`. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3d50890 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,51 @@ +import js from "@eslint/js"; + +export default [ + js.configs.recommended, + { + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + React: "readonly", + JSX: "readonly", + console: "readonly", + process: "readonly", + URL: "readonly", + Request: "readonly", + Response: "readonly", + Headers: "readonly", + FormData: "readonly", + fetch: "readonly", + setTimeout: "readonly", + clearTimeout: "readonly", + Promise: "readonly", + Map: "readonly", + Set: "readonly", + AbortController: "readonly", + Buffer: "readonly", + __dirname: "readonly", + __filename: "readonly", + module: "readonly", + require: "readonly", + exports: "readonly", + }, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + rules: { + "no-unused-vars": "warn", + "no-undef": "off", + }, + }, + { + ignores: [ + ".next/**", + "node_modules/**", + "stubs/**", + ], + }, +]; diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..9edff1c --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/stubs/stack-auth-nextjs/index.d.ts b/stubs/stack-auth-nextjs/index.d.ts new file mode 100644 index 0000000..48269a4 --- /dev/null +++ b/stubs/stack-auth-nextjs/index.d.ts @@ -0,0 +1,9 @@ +interface StackUser { + id: string; + displayName: string; + primaryEmail: string; + activeOrganizationId: string; +} + +export function useUser(): StackUser; +export function currentUser(): Promise; diff --git a/stubs/stack-auth-nextjs/index.js b/stubs/stack-auth-nextjs/index.js new file mode 100644 index 0000000..7f295e8 --- /dev/null +++ b/stubs/stack-auth-nextjs/index.js @@ -0,0 +1,18 @@ +"use client"; + +const mockUser = { + id: "00000000-0000-0000-0000-000000000002", + displayName: "Dev User", + primaryEmail: "dev@taskflow.local", + activeOrganizationId: "00000000-0000-0000-0000-000000000001", +}; + +function useUser() { + return mockUser; +} + +async function currentUser() { + return mockUser; +} + +module.exports = { useUser, currentUser }; diff --git a/stubs/stack-auth-nextjs/package.json b/stubs/stack-auth-nextjs/package.json new file mode 100644 index 0000000..ccc81de --- /dev/null +++ b/stubs/stack-auth-nextjs/package.json @@ -0,0 +1,12 @@ +{ + "name": "@stack-auth/nextjs", + "version": "0.0.1", + "main": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "react-server": "./server.js", + "default": "./index.js" + } + } +} diff --git a/stubs/stack-auth-nextjs/server.js b/stubs/stack-auth-nextjs/server.js new file mode 100644 index 0000000..f746145 --- /dev/null +++ b/stubs/stack-auth-nextjs/server.js @@ -0,0 +1,16 @@ +const mockUser = { + id: "00000000-0000-0000-0000-000000000002", + displayName: "Dev User", + primaryEmail: "dev@taskflow.local", + activeOrganizationId: "00000000-0000-0000-0000-000000000001", +}; + +async function currentUser() { + return mockUser; +} + +function useUser() { + return mockUser; +} + +module.exports = { currentUser, useUser }; diff --git a/tsconfig.json b/tsconfig.json index 4b2dc7b..48d6d82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "target": "ES6", "skipLibCheck": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -19,9 +23,19 @@ } ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } From 2cc9be25bccd1804ad41ac8f99b9fd77564224be Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 28 Feb 2026 22:37:40 +0000 Subject: [PATCH 3/8] chore: add env-agent-finder tool to project Standalone CLI tool that scans codebases for env vars, API routes, and service dependencies. Also published as its own repo at: https://github.com/DealPatrol/env-agent-finder Co-authored-by: DealPatrol --- env-agent-finder/.gitignore | 3 + env-agent-finder/README.md | 138 +++++++++ env-agent-finder/package.json | 27 ++ env-agent-finder/src/cli.js | 67 +++++ env-agent-finder/src/index.js | 22 ++ env-agent-finder/src/reporter.js | 228 +++++++++++++++ env-agent-finder/src/scanners/api-scanner.js | 233 ++++++++++++++++ env-agent-finder/src/scanners/env-scanner.js | 212 ++++++++++++++ env-agent-finder/src/scanners/meta-scanner.js | 75 +++++ .../src/scanners/service-scanner.js | 262 ++++++++++++++++++ 10 files changed, 1267 insertions(+) create mode 100644 env-agent-finder/.gitignore create mode 100644 env-agent-finder/README.md create mode 100644 env-agent-finder/package.json create mode 100755 env-agent-finder/src/cli.js create mode 100644 env-agent-finder/src/index.js create mode 100644 env-agent-finder/src/reporter.js create mode 100644 env-agent-finder/src/scanners/api-scanner.js create mode 100644 env-agent-finder/src/scanners/env-scanner.js create mode 100644 env-agent-finder/src/scanners/meta-scanner.js create mode 100644 env-agent-finder/src/scanners/service-scanner.js diff --git a/env-agent-finder/.gitignore b/env-agent-finder/.gitignore new file mode 100644 index 0000000..aafcb34 --- /dev/null +++ b/env-agent-finder/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.DS_Store +*.log diff --git a/env-agent-finder/README.md b/env-agent-finder/README.md new file mode 100644 index 0000000..7641547 --- /dev/null +++ b/env-agent-finder/README.md @@ -0,0 +1,138 @@ +# env-agent-finder + +A zero-dependency CLI tool that scans any codebase to discover: + +- **Environment variables** โ€” finds every `process.env.*` reference, `.env` file, and maps them to known services +- **API routes** โ€” detects Next.js App/Pages Router, Express, FastAPI, and Flask endpoints +- **Service dependencies** โ€” identifies databases, auth providers, storage, payments, AI, email, and more + +Built for AI agents and developers who need to quickly understand what a project needs to run. + +## Quick Start + +```bash +# Scan current directory +node src/cli.js + +# Scan a specific project +node src/cli.js --target /path/to/project + +# Output as Markdown +node src/cli.js --target ./my-app --format markdown --output report.md + +# Output as JSON +node src/cli.js --format json +``` + +## Installation + +No dependencies required โ€” just clone and run: + +```bash +git clone https://github.com/DealPatrol/env-agent-finder.git +cd env-agent-finder +node src/cli.js --target /path/to/your/project +``` + +Or use npx (after publishing): + +```bash +npx env-agent-finder --target ./my-project +``` + +## Output Example + +``` +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + ENV AGENT FINDER โ€” Scan Report +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +๐Ÿ“ฆ PROJECT INFO +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Name: my-project + Framework: Next.js 16.1.6 + Language: JavaScript/TypeScript + Package Manager: pnpm + +๐Ÿ”‘ ENVIRONMENT VARIABLES (5 found) +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + Variable Service Refs Status + DATABASE_URL Database (PostgreSQL) 3 โŒ Missing + BLOB_READ_WRITE_TOKEN Vercel Blob Storage 1 โšช Optional + STACK_PROJECT_ID Stack Auth 1 โŒ Missing + STACK_PUBLISHED_CLIENT_KEY Stack Auth 1 โŒ Missing + STACK_SECRET_SERVER_KEY Stack Auth 1 โŒ Missing + +๐Ÿ”Œ DETECTED SERVICES (4 found) +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + PostgreSQL + Category: database + Confidence: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘ 80% + Packages: pg, @neondatabase/serverless + Env Vars: DATABASE_URL + + Stack Auth + Category: auth + Confidence: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% + Packages: @stack-auth/nextjs + Env Vars: STACK_PROJECT_ID, STACK_SECRET_SERVER_KEY + +๐ŸŒ API ROUTES (8 found) +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + /api/projects [GET, POST] + /api/projects/[id]/activity [GET, POST] + /api/projects/[id]/analytics [GET] + /api/projects/[id]/members [GET, POST, DELETE] + /api/projects/[id]/tasks [GET, POST, PUT, DELETE] + /api/projects/[id]/tasks/[taskId]/attachments [GET, POST, DELETE] + /api/projects/[id]/tasks/[taskId]/comments [GET, POST, DELETE] + /api/upload [POST] +``` + +## Output Formats + +| Format | Flag | Description | +|--------|------|-------------| +| Terminal | `--format terminal` | Colorful CLI output (default) | +| Markdown | `--format markdown` | Markdown report for docs/PRs | +| JSON | `--format json` | Machine-readable for pipelines | + +## What It Detects + +### Environment Variables +- `process.env.VAR_NAME` (Node.js) +- `os.environ` / `os.getenv()` (Python) +- `import.meta.env.VAR_NAME` (Vite) +- `.env`, `.env.local`, `.env.example` files +- 50+ well-known service variable mappings + +### API Routes +- Next.js App Router (`app/api/**/route.ts`) +- Next.js Pages Router (`pages/api/**/*.ts`) +- Express / Fastify (`app.get()`, `router.post()`, etc.) +- FastAPI / Flask (`@app.get()`, `@router.post()`, etc.) + +### Services (30+ supported) +| Category | Services | +|----------|----------| +| Database | PostgreSQL, MySQL, MongoDB, Supabase, Firebase | +| Auth | NextAuth, Clerk, Stack Auth, Auth.js | +| Storage | Vercel Blob, AWS S3, Cloudinary | +| Payments | Stripe | +| AI | OpenAI, Anthropic | +| Email | SendGrid, Resend, SMTP | +| Monitoring | Sentry | +| Cache | Redis, Upstash | +| Messaging | Twilio | + +## Use Cases + +- **AI Agents**: Automatically discover what env vars and services a project needs before setting up +- **Onboarding**: Generate a setup checklist for new developers +- **CI/CD**: Validate that all required secrets are configured +- **Documentation**: Auto-generate env var docs for your README +- **Security Audits**: Find all external service dependencies + +## License + +MIT diff --git a/env-agent-finder/package.json b/env-agent-finder/package.json new file mode 100644 index 0000000..e28a115 --- /dev/null +++ b/env-agent-finder/package.json @@ -0,0 +1,27 @@ +{ + "name": "env-agent-finder", + "version": "1.0.0", + "description": "CLI tool that scans any codebase to discover required environment variables, API endpoints, and service dependencies.", + "main": "src/index.js", + "bin": { + "env-agent-finder": "src/cli.js" + }, + "scripts": { + "start": "node src/cli.js", + "scan": "node src/cli.js", + "test": "node src/cli.js --target ." + }, + "keywords": [ + "env", + "environment", + "api", + "scanner", + "agent", + "devops", + "cli" + ], + "license": "MIT", + "engines": { + "node": ">=18" + } +} diff --git a/env-agent-finder/src/cli.js b/env-agent-finder/src/cli.js new file mode 100755 index 0000000..1507645 --- /dev/null +++ b/env-agent-finder/src/cli.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node + +const path = require("path"); +const { scanProject } = require("./index"); +const { renderReport } = require("./reporter"); + +const args = process.argv.slice(2); + +let targetDir = process.cwd(); +let outputFormat = "terminal"; +let outputFile = null; + +for (let i = 0; i < args.length; i++) { + if (args[i] === "--target" && args[i + 1]) { + targetDir = path.resolve(args[i + 1]); + i++; + } else if (args[i] === "--format" && args[i + 1]) { + outputFormat = args[i + 1]; + i++; + } else if (args[i] === "--output" && args[i + 1]) { + outputFile = path.resolve(args[i + 1]); + i++; + } else if (args[i] === "--help" || args[i] === "-h") { + console.log(` +env-agent-finder โ€” Scan a codebase for env vars, APIs, and service dependencies. + +Usage: + env-agent-finder [options] + +Options: + --target Directory to scan (default: current directory) + --format Output format: terminal, json, markdown (default: terminal) + --output Write output to file instead of stdout + -h, --help Show this help message + +Examples: + env-agent-finder --target ./my-project + env-agent-finder --target ./my-project --format markdown --output report.md + env-agent-finder --format json +`); + process.exit(0); + } else if (!args[i].startsWith("--")) { + targetDir = path.resolve(args[i]); + } +} + +async function main() { + console.log(`\n๐Ÿ” Scanning: ${targetDir}\n`); + + try { + const report = await scanProject(targetDir); + const output = renderReport(report, outputFormat); + + if (outputFile) { + const fs = require("fs"); + fs.writeFileSync(outputFile, output, "utf-8"); + console.log(`\nโœ… Report written to: ${outputFile}`); + } else { + console.log(output); + } + } catch (err) { + console.error(`\nโŒ Error scanning project: ${err.message}`); + process.exit(1); + } +} + +main(); diff --git a/env-agent-finder/src/index.js b/env-agent-finder/src/index.js new file mode 100644 index 0000000..6f9cab5 --- /dev/null +++ b/env-agent-finder/src/index.js @@ -0,0 +1,22 @@ +const { scanEnvVars } = require("./scanners/env-scanner"); +const { scanApiRoutes } = require("./scanners/api-scanner"); +const { scanServices } = require("./scanners/service-scanner"); +const { scanProjectMeta } = require("./scanners/meta-scanner"); + +async function scanProject(targetDir) { + const meta = await scanProjectMeta(targetDir); + const envVars = await scanEnvVars(targetDir); + const apiRoutes = await scanApiRoutes(targetDir); + const services = await scanServices(targetDir, envVars); + + return { + targetDir, + scannedAt: new Date().toISOString(), + meta, + envVars, + apiRoutes, + services, + }; +} + +module.exports = { scanProject }; diff --git a/env-agent-finder/src/reporter.js b/env-agent-finder/src/reporter.js new file mode 100644 index 0000000..d772930 --- /dev/null +++ b/env-agent-finder/src/reporter.js @@ -0,0 +1,228 @@ +function renderTerminal(report) { + const lines = []; + const { meta, envVars, apiRoutes, services } = report; + + lines.push("โ•".repeat(60)); + lines.push(" ENV AGENT FINDER โ€” Scan Report"); + lines.push("โ•".repeat(60)); + lines.push(""); + + // Project meta + lines.push("๐Ÿ“ฆ PROJECT INFO"); + lines.push("โ”€".repeat(40)); + if (meta.name) lines.push(` Name: ${meta.name}`); + if (meta.framework) lines.push(` Framework: ${meta.framework}`); + if (meta.language) lines.push(` Language: ${meta.language}`); + if (meta.packageManager) lines.push(` Package Manager: ${meta.packageManager}`); + lines.push(` Docker: ${meta.hasDocker ? "Yes" : "No"}`); + lines.push(` CI/CD: ${meta.hasCI ? "Yes" : "No"}`); + lines.push(""); + + // Env vars + lines.push(`๐Ÿ”‘ ENVIRONMENT VARIABLES (${envVars.length} found)`); + lines.push("โ”€".repeat(40)); + if (envVars.length === 0) { + lines.push(" No environment variables detected."); + } else { + const maxName = Math.max(...envVars.map((v) => v.name.length), 10); + lines.push( + ` ${"Variable".padEnd(maxName)} ${"Service".padEnd(25)} ${"Refs".padEnd(4)} Status` + ); + lines.push(` ${"โ”€".repeat(maxName)} ${"โ”€".repeat(25)} ${"โ”€".repeat(4)} โ”€โ”€โ”€โ”€โ”€โ”€`); + for (const v of envVars) { + const status = v.hasValue ? "โœ… Set" : v.required === false ? "โšช Optional" : "โŒ Missing"; + const service = (v.service || "โ€”").substring(0, 25); + lines.push( + ` ${v.name.padEnd(maxName)} ${service.padEnd(25)} ${String(v.references.length).padEnd(4)} ${status}` + ); + } + } + lines.push(""); + + // Services + lines.push(`๐Ÿ”Œ DETECTED SERVICES (${services.length} found)`); + lines.push("โ”€".repeat(40)); + if (services.length === 0) { + lines.push(" No external services detected."); + } else { + for (const svc of services) { + const bar = "โ–ˆ".repeat(Math.round(svc.confidence / 10)) + + "โ–‘".repeat(10 - Math.round(svc.confidence / 10)); + lines.push(` ${svc.name}`); + lines.push(` Category: ${svc.category}`); + lines.push(` Confidence: ${bar} ${svc.confidence}%`); + if (svc.matchedPackages.length > 0) { + lines.push(` Packages: ${svc.matchedPackages.join(", ")}`); + } + if (svc.matchedEnvVars.length > 0) { + lines.push(` Env Vars: ${svc.matchedEnvVars.join(", ")}`); + } + if (svc.matchedFiles.length > 0) { + lines.push(` Files: ${svc.matchedFiles.join(", ")}`); + } + lines.push(` Docs: ${svc.docs}`); + lines.push(""); + } + } + + // API routes + lines.push(`๐ŸŒ API ROUTES (${apiRoutes.length} found)`); + lines.push("โ”€".repeat(40)); + if (apiRoutes.length === 0) { + lines.push(" No API routes detected."); + } else { + const maxPath = Math.max(...apiRoutes.map((r) => r.path.length), 10); + for (const route of apiRoutes) { + const methods = route.methods.join(", "); + lines.push(` ${route.path.padEnd(maxPath)} [${methods}]`); + } + } + lines.push(""); + + // .env template + const missingVars = envVars.filter((v) => !v.hasValue); + if (missingVars.length > 0) { + lines.push("๐Ÿ“‹ SUGGESTED .env.local TEMPLATE"); + lines.push("โ”€".repeat(40)); + lines.push(""); + let currentService = null; + for (const v of missingVars) { + const svc = v.service || "Other"; + if (svc !== currentService) { + if (currentService !== null) lines.push(""); + lines.push(` # ${svc}`); + currentService = svc; + } + lines.push(` ${v.name}=`); + } + lines.push(""); + } + + lines.push("โ•".repeat(60)); + lines.push(` Scanned: ${report.targetDir}`); + lines.push(` Time: ${report.scannedAt}`); + lines.push("โ•".repeat(60)); + + return lines.join("\n"); +} + +function renderJson(report) { + return JSON.stringify(report, null, 2); +} + +function renderMarkdown(report) { + const lines = []; + const { meta, envVars, apiRoutes, services } = report; + + lines.push("# Environment & API Scan Report"); + lines.push(""); + lines.push(`> Scanned: \`${report.targetDir}\` `); + lines.push(`> Time: ${report.scannedAt}`); + lines.push(""); + + // Meta + lines.push("## Project Info"); + lines.push(""); + lines.push("| Property | Value |"); + lines.push("|----------|-------|"); + if (meta.name) lines.push(`| Name | ${meta.name} |`); + if (meta.framework) lines.push(`| Framework | ${meta.framework} |`); + if (meta.language) lines.push(`| Language | ${meta.language} |`); + if (meta.packageManager) lines.push(`| Package Manager | ${meta.packageManager} |`); + lines.push(`| Docker | ${meta.hasDocker ? "Yes" : "No"} |`); + lines.push(`| CI/CD | ${meta.hasCI ? "Yes" : "No"} |`); + lines.push(""); + + // Env vars + lines.push(`## Environment Variables (${envVars.length})`); + lines.push(""); + if (envVars.length > 0) { + lines.push("| Variable | Service | References | Status |"); + lines.push("|----------|---------|------------|--------|"); + for (const v of envVars) { + const status = v.hasValue ? "Set" : v.required === false ? "Optional" : "**Missing**"; + lines.push( + `| \`${v.name}\` | ${v.service || "โ€”"} | ${v.references.length} | ${status} |` + ); + } + } else { + lines.push("No environment variables detected."); + } + lines.push(""); + + // Services + lines.push(`## Detected Services (${services.length})`); + lines.push(""); + if (services.length > 0) { + for (const svc of services) { + lines.push(`### ${svc.name} (${svc.category})`); + lines.push(`- **Confidence:** ${svc.confidence}%`); + if (svc.matchedPackages.length > 0) { + lines.push(`- **Packages:** ${svc.matchedPackages.map((p) => "`" + p + "`").join(", ")}`); + } + if (svc.matchedEnvVars.length > 0) { + lines.push(`- **Env Vars:** ${svc.matchedEnvVars.map((v) => "`" + v + "`").join(", ")}`); + } + if (svc.requiredEnvVars.length > 0) { + lines.push(`- **Required Env Vars:** ${svc.requiredEnvVars.map((v) => "`" + v + "`").join(", ")}`); + } + lines.push(`- **Docs:** ${svc.docs}`); + lines.push(""); + } + } else { + lines.push("No external services detected."); + lines.push(""); + } + + // API routes + lines.push(`## API Routes (${apiRoutes.length})`); + lines.push(""); + if (apiRoutes.length > 0) { + lines.push("| Path | Methods | Framework | File |"); + lines.push("|------|---------|-----------|------|"); + for (const route of apiRoutes) { + lines.push( + `| \`${route.path}\` | ${route.methods.join(", ")} | ${route.framework} | \`${route.file}\` |` + ); + } + } else { + lines.push("No API routes detected."); + } + lines.push(""); + + // .env template + const missingVars = envVars.filter((v) => !v.hasValue); + if (missingVars.length > 0) { + lines.push("## Suggested `.env.local` Template"); + lines.push(""); + lines.push("```bash"); + let currentService = null; + for (const v of missingVars) { + const svc = v.service || "Other"; + if (svc !== currentService) { + if (currentService !== null) lines.push(""); + lines.push(`# ${svc}`); + currentService = svc; + } + lines.push(`${v.name}=`); + } + lines.push("```"); + } + + return lines.join("\n"); +} + +function renderReport(report, format) { + switch (format) { + case "json": + return renderJson(report); + case "markdown": + case "md": + return renderMarkdown(report); + case "terminal": + default: + return renderTerminal(report); + } +} + +module.exports = { renderReport }; diff --git a/env-agent-finder/src/scanners/api-scanner.js b/env-agent-finder/src/scanners/api-scanner.js new file mode 100644 index 0000000..ddac1e1 --- /dev/null +++ b/env-agent-finder/src/scanners/api-scanner.js @@ -0,0 +1,233 @@ +const fs = require("fs"); +const path = require("path"); + +const SKIP_DIRS = new Set([ + "node_modules", ".next", ".nuxt", "dist", "build", ".git", + "coverage", "__pycache__", ".venv", "venv", "vendor", "target", + ".turbo", ".cache", ".output", "stubs", +]); + +function walkDirs(dir, results = []) { + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + return results; + } + for (const entry of entries) { + if (SKIP_DIRS.has(entry.name)) continue; + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + results.push(fullPath); + walkDirs(fullPath, results); + } + } + return results; +} + +function scanNextJsRoutes(targetDir) { + const routes = []; + const appDir = path.join(targetDir, "app"); + const apiDir = path.join(appDir, "api"); + + if (!fs.existsSync(apiDir)) return routes; + + const dirs = walkDirs(apiDir); + dirs.unshift(apiDir); + + for (const dir of dirs) { + const routeFile = ["route.ts", "route.js"].find((f) => + fs.existsSync(path.join(dir, f)) + ); + if (!routeFile) continue; + + const filePath = path.join(dir, routeFile); + const relativePath = path.relative(appDir, dir); + const routePath = "/" + relativePath.replace(/\\/g, "/"); + + let content; + try { + content = fs.readFileSync(filePath, "utf-8"); + } catch { + continue; + } + + const methods = []; + const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]; + for (const method of httpMethods) { + const pattern = new RegExp( + `export\\s+(?:async\\s+)?function\\s+${method}\\b`, + ); + if (pattern.test(content)) { + methods.push(method); + } + } + + if (methods.length > 0) { + routes.push({ + path: routePath, + methods, + file: path.relative(targetDir, filePath), + framework: "Next.js App Router", + }); + } + } + + return routes; +} + +function scanExpressRoutes(targetDir) { + const routes = []; + const srcDirs = [ + targetDir, + path.join(targetDir, "src"), + path.join(targetDir, "routes"), + path.join(targetDir, "src", "routes"), + path.join(targetDir, "api"), + path.join(targetDir, "src", "api"), + ]; + + for (const dir of srcDirs) { + if (!fs.existsSync(dir)) continue; + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + continue; + } + + for (const entry of entries) { + if (!entry.isFile()) continue; + const ext = path.extname(entry.name); + if (![".js", ".ts", ".mjs"].includes(ext)) continue; + + const filePath = path.join(dir, entry.name); + let content; + try { + content = fs.readFileSync(filePath, "utf-8"); + } catch { + continue; + } + + const routePattern = + /(?:app|router|server)\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi; + let match; + while ((match = routePattern.exec(content)) !== null) { + const method = match[1].toUpperCase(); + const routePath = match[2]; + const existing = routes.find((r) => r.path === routePath); + if (existing) { + if (!existing.methods.includes(method)) existing.methods.push(method); + } else { + routes.push({ + path: routePath, + methods: [method], + file: path.relative(targetDir, filePath), + framework: "Express/Fastify", + }); + } + } + } + } + + return routes; +} + +function scanPagesApiRoutes(targetDir) { + const routes = []; + const pagesApiDir = path.join(targetDir, "pages", "api"); + if (!fs.existsSync(pagesApiDir)) return routes; + + const dirs = walkDirs(pagesApiDir); + dirs.unshift(pagesApiDir); + + for (const dir of dirs) { + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + continue; + } + + for (const entry of entries) { + if (!entry.isFile()) continue; + const ext = path.extname(entry.name); + if (![".ts", ".js", ".tsx", ".jsx"].includes(ext)) continue; + + const filePath = path.join(dir, entry.name); + const relativePath = path.relative(pagesApiDir, filePath); + const routeName = relativePath + .replace(/\\/g, "/") + .replace(/\.(ts|js|tsx|jsx)$/, "") + .replace(/\/index$/, ""); + const routePath = "/api/" + routeName; + + routes.push({ + path: routePath, + methods: ["handler"], + file: path.relative(targetDir, filePath), + framework: "Next.js Pages Router", + }); + } + } + + return routes; +} + +function scanFastApiRoutes(targetDir) { + const routes = []; + const pyFiles = []; + + function findPyFiles(dir) { + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + return; + } + for (const entry of entries) { + if (SKIP_DIRS.has(entry.name)) continue; + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) findPyFiles(fullPath); + else if (entry.isFile() && entry.name.endsWith(".py")) pyFiles.push(fullPath); + } + } + + findPyFiles(targetDir); + + for (const filePath of pyFiles) { + let content; + try { + content = fs.readFileSync(filePath, "utf-8"); + } catch { + continue; + } + + const pattern = + /@(?:app|router)\.(get|post|put|patch|delete)\s*\(\s*['"]([^'"]+)['"]/gi; + let match; + while ((match = pattern.exec(content)) !== null) { + routes.push({ + path: match[2], + methods: [match[1].toUpperCase()], + file: path.relative(targetDir, filePath), + framework: "FastAPI/Flask", + }); + } + } + + return routes; +} + +async function scanApiRoutes(targetDir) { + const allRoutes = [ + ...scanNextJsRoutes(targetDir), + ...scanPagesApiRoutes(targetDir), + ...scanExpressRoutes(targetDir), + ...scanFastApiRoutes(targetDir), + ]; + + return allRoutes.sort((a, b) => a.path.localeCompare(b.path)); +} + +module.exports = { scanApiRoutes }; diff --git a/env-agent-finder/src/scanners/env-scanner.js b/env-agent-finder/src/scanners/env-scanner.js new file mode 100644 index 0000000..8856a58 --- /dev/null +++ b/env-agent-finder/src/scanners/env-scanner.js @@ -0,0 +1,212 @@ +const fs = require("fs"); +const path = require("path"); + +const SKIP_DIRS = new Set([ + "node_modules", ".next", ".nuxt", "dist", "build", ".git", + "coverage", "__pycache__", ".venv", "venv", "vendor", "target", + ".turbo", ".cache", ".output", "stubs", +]); + +const CODE_EXTENSIONS = new Set([ + ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", + ".py", ".go", ".rs", ".rb", ".java", ".kt", + ".env", ".env.local", ".env.example", ".env.development", + ".env.production", ".env.test", + ".yml", ".yaml", ".toml", ".json", ".tf", +]); + +const WELL_KNOWN_SERVICES = { + DATABASE_URL: { service: "Database (PostgreSQL/MySQL)", required: true }, + POSTGRES_URL: { service: "PostgreSQL", required: true }, + MYSQL_URL: { service: "MySQL", required: true }, + MONGODB_URI: { service: "MongoDB", required: true }, + REDIS_URL: { service: "Redis", required: false }, + BLOB_READ_WRITE_TOKEN: { service: "Vercel Blob Storage", required: false }, + NEXT_PUBLIC_SUPABASE_URL: { service: "Supabase", required: true }, + SUPABASE_URL: { service: "Supabase", required: true }, + SUPABASE_SERVICE_ROLE_KEY: { service: "Supabase", required: true }, + STRIPE_SECRET_KEY: { service: "Stripe", required: true }, + STRIPE_PUBLISHABLE_KEY: { service: "Stripe", required: true }, + STRIPE_WEBHOOK_SECRET: { service: "Stripe Webhooks", required: false }, + OPENAI_API_KEY: { service: "OpenAI", required: true }, + ANTHROPIC_API_KEY: { service: "Anthropic", required: true }, + AWS_ACCESS_KEY_ID: { service: "AWS", required: true }, + AWS_SECRET_ACCESS_KEY: { service: "AWS", required: true }, + AWS_REGION: { service: "AWS", required: false }, + S3_BUCKET: { service: "AWS S3", required: false }, + GOOGLE_CLIENT_ID: { service: "Google OAuth", required: false }, + GOOGLE_CLIENT_SECRET: { service: "Google OAuth", required: false }, + GITHUB_CLIENT_ID: { service: "GitHub OAuth", required: false }, + GITHUB_CLIENT_SECRET: { service: "GitHub OAuth", required: false }, + NEXTAUTH_SECRET: { service: "NextAuth.js", required: true }, + NEXTAUTH_URL: { service: "NextAuth.js", required: false }, + AUTH_SECRET: { service: "Auth.js", required: true }, + JWT_SECRET: { service: "JWT Auth", required: true }, + SENDGRID_API_KEY: { service: "SendGrid Email", required: false }, + RESEND_API_KEY: { service: "Resend Email", required: false }, + SMTP_HOST: { service: "SMTP Email", required: false }, + SENTRY_DSN: { service: "Sentry Error Tracking", required: false }, + VERCEL_URL: { service: "Vercel Platform", required: false }, + CLERK_SECRET_KEY: { service: "Clerk Auth", required: true }, + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: { service: "Clerk Auth", required: true }, + STACK_PROJECT_ID: { service: "Stack Auth", required: true }, + STACK_PUBLISHED_CLIENT_KEY: { service: "Stack Auth", required: true }, + STACK_SECRET_SERVER_KEY: { service: "Stack Auth", required: true }, + TWILIO_ACCOUNT_SID: { service: "Twilio", required: false }, + TWILIO_AUTH_TOKEN: { service: "Twilio", required: false }, + CLOUDINARY_URL: { service: "Cloudinary", required: false }, + UPSTASH_REDIS_REST_URL: { service: "Upstash Redis", required: false }, + UPSTASH_REDIS_REST_TOKEN: { service: "Upstash Redis", required: false }, + FIREBASE_API_KEY: { service: "Firebase", required: true }, + FIREBASE_PROJECT_ID: { service: "Firebase", required: true }, +}; + +function walkFiles(dir, files = []) { + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + return files; + } + + for (const entry of entries) { + if (SKIP_DIRS.has(entry.name)) continue; + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + walkFiles(fullPath, files); + } else if (entry.isFile()) { + const ext = path.extname(entry.name).toLowerCase(); + const basename = entry.name.toLowerCase(); + if (CODE_EXTENSIONS.has(ext) || basename.startsWith(".env")) { + files.push(fullPath); + } + } + } + return files; +} + +function extractEnvReferences(content, filePath) { + const refs = []; + + const patterns = [ + /process\.env\.([A-Z_][A-Z0-9_]*)/g, + /process\.env\[['"]([A-Z_][A-Z0-9_]*)['"]\]/g, + /os\.environ(?:\.get)?\(?['"]([A-Z_][A-Z0-9_]*)['"]/g, + /os\.getenv\(['"]([A-Z_][A-Z0-9_]*)['"]\)/g, + /env(?:ironment)?\.([A-Z_][A-Z0-9_]*)/g, + /\$\{([A-Z_][A-Z0-9_]*)\}/g, + /import\.meta\.env\.([A-Z_][A-Z0-9_]*)/g, + ]; + + for (const pattern of patterns) { + let match; + while ((match = pattern.exec(content)) !== null) { + refs.push({ + name: match[1], + file: filePath, + line: content.substring(0, match.index).split("\n").length, + }); + } + } + + return refs; +} + +function parseEnvFile(filePath) { + const vars = []; + try { + const content = fs.readFileSync(filePath, "utf-8"); + const lines = content.split("\n"); + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + if (!line || line.startsWith("#")) continue; + const eqIdx = line.indexOf("="); + if (eqIdx > 0) { + const name = line.substring(0, eqIdx).trim(); + const value = line.substring(eqIdx + 1).trim(); + if (/^[A-Z_][A-Z0-9_]*$/.test(name)) { + vars.push({ + name, + hasValue: value.length > 0 && value !== '""' && value !== "''", + file: filePath, + line: i + 1, + }); + } + } + } + } catch {} + return vars; +} + +async function scanEnvVars(targetDir) { + const files = walkFiles(targetDir); + const allRefs = []; + const envFileVars = []; + + for (const filePath of files) { + const basename = path.basename(filePath).toLowerCase(); + + if (basename.startsWith(".env")) { + envFileVars.push(...parseEnvFile(filePath)); + continue; + } + + try { + const content = fs.readFileSync(filePath, "utf-8"); + const relativePath = path.relative(targetDir, filePath); + const refs = extractEnvReferences(content, relativePath); + allRefs.push(...refs); + } catch {} + } + + const varMap = new Map(); + + for (const ref of allRefs) { + if (!varMap.has(ref.name)) { + const known = WELL_KNOWN_SERVICES[ref.name]; + varMap.set(ref.name, { + name: ref.name, + references: [], + service: known?.service || null, + required: known?.required ?? null, + hasValue: false, + }); + } + varMap.get(ref.name).references.push({ file: ref.file, line: ref.line }); + } + + for (const envVar of envFileVars) { + if (!varMap.has(envVar.name)) { + const known = WELL_KNOWN_SERVICES[envVar.name]; + varMap.set(envVar.name, { + name: envVar.name, + references: [{ file: path.relative(targetDir, envVar.file), line: envVar.line }], + service: known?.service || null, + required: known?.required ?? null, + hasValue: envVar.hasValue, + }); + } else { + const existing = varMap.get(envVar.name); + existing.hasValue = existing.hasValue || envVar.hasValue; + existing.references.push({ file: path.relative(targetDir, envVar.file), line: envVar.line }); + } + } + + const NODE_INTERNALS = new Set([ + "NODE_ENV", "PORT", "HOST", "HOSTNAME", "HOME", "PATH", "PWD", + "LANG", "TERM", "SHELL", "USER", "TZ", "CI", "DEBUG", + "VERCEL", "VERCEL_ENV", "VERCEL_URL", + ]); + + const results = Array.from(varMap.values()) + .filter((v) => !NODE_INTERNALS.has(v.name)) + .sort((a, b) => { + if (a.required && !b.required) return -1; + if (!a.required && b.required) return 1; + return a.name.localeCompare(b.name); + }); + + return results; +} + +module.exports = { scanEnvVars }; diff --git a/env-agent-finder/src/scanners/meta-scanner.js b/env-agent-finder/src/scanners/meta-scanner.js new file mode 100644 index 0000000..e6e7c7e --- /dev/null +++ b/env-agent-finder/src/scanners/meta-scanner.js @@ -0,0 +1,75 @@ +const fs = require("fs"); +const path = require("path"); + +async function scanProjectMeta(targetDir) { + const meta = { + name: null, + framework: null, + language: null, + packageManager: null, + hasDocker: false, + hasCI: false, + }; + + const pkgPath = path.join(targetDir, "package.json"); + if (fs.existsSync(pkgPath)) { + try { + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); + meta.name = pkg.name || null; + meta.language = "JavaScript/TypeScript"; + + if (pkg.dependencies?.next || pkg.devDependencies?.next) { + meta.framework = `Next.js ${pkg.dependencies?.next || pkg.devDependencies?.next}`; + } else if (pkg.dependencies?.nuxt || pkg.devDependencies?.nuxt) { + meta.framework = `Nuxt ${pkg.dependencies?.nuxt || pkg.devDependencies?.nuxt}`; + } else if (pkg.dependencies?.react || pkg.devDependencies?.react) { + meta.framework = `React ${pkg.dependencies?.react || pkg.devDependencies?.react}`; + } else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) { + meta.framework = `Vue ${pkg.dependencies?.vue || pkg.devDependencies?.vue}`; + } else if (pkg.dependencies?.express || pkg.devDependencies?.express) { + meta.framework = `Express ${pkg.dependencies?.express || pkg.devDependencies?.express}`; + } else if (pkg.dependencies?.fastify || pkg.devDependencies?.fastify) { + meta.framework = `Fastify ${pkg.dependencies?.fastify || pkg.devDependencies?.fastify}`; + } + } catch {} + } + + if (fs.existsSync(path.join(targetDir, "requirements.txt")) || + fs.existsSync(path.join(targetDir, "pyproject.toml")) || + fs.existsSync(path.join(targetDir, "setup.py"))) { + meta.language = "Python"; + if (fs.existsSync(path.join(targetDir, "pyproject.toml"))) { + try { + const content = fs.readFileSync(path.join(targetDir, "pyproject.toml"), "utf-8"); + if (content.includes("django")) meta.framework = "Django"; + else if (content.includes("fastapi")) meta.framework = "FastAPI"; + else if (content.includes("flask")) meta.framework = "Flask"; + } catch {} + } + } + + if (fs.existsSync(path.join(targetDir, "go.mod"))) { + meta.language = "Go"; + } + + if (fs.existsSync(path.join(targetDir, "Cargo.toml"))) { + meta.language = "Rust"; + } + + if (fs.existsSync(path.join(targetDir, "pnpm-lock.yaml"))) meta.packageManager = "pnpm"; + else if (fs.existsSync(path.join(targetDir, "yarn.lock"))) meta.packageManager = "yarn"; + else if (fs.existsSync(path.join(targetDir, "bun.lockb"))) meta.packageManager = "bun"; + else if (fs.existsSync(path.join(targetDir, "package-lock.json"))) meta.packageManager = "npm"; + + meta.hasDocker = fs.existsSync(path.join(targetDir, "Dockerfile")) || + fs.existsSync(path.join(targetDir, "docker-compose.yml")) || + fs.existsSync(path.join(targetDir, "docker-compose.yaml")); + + meta.hasCI = fs.existsSync(path.join(targetDir, ".github", "workflows")) || + fs.existsSync(path.join(targetDir, ".gitlab-ci.yml")) || + fs.existsSync(path.join(targetDir, ".circleci")); + + return meta; +} + +module.exports = { scanProjectMeta }; diff --git a/env-agent-finder/src/scanners/service-scanner.js b/env-agent-finder/src/scanners/service-scanner.js new file mode 100644 index 0000000..3ce1d51 --- /dev/null +++ b/env-agent-finder/src/scanners/service-scanner.js @@ -0,0 +1,262 @@ +const fs = require("fs"); +const path = require("path"); + +const SERVICE_SIGNATURES = [ + { + name: "PostgreSQL", + category: "database", + indicators: { + packages: ["pg", "@neondatabase/serverless", "postgres", "knex", "prisma", "drizzle-orm", "typeorm", "sequelize"], + envVars: ["DATABASE_URL", "POSTGRES_URL", "PG_CONNECTION_STRING", "PGHOST"], + files: ["prisma/schema.prisma", "drizzle.config.ts", "drizzle.config.js"], + }, + docs: "https://www.postgresql.org/docs/", + }, + { + name: "MySQL", + category: "database", + indicators: { + packages: ["mysql2", "mysql"], + envVars: ["MYSQL_URL", "MYSQL_HOST", "MYSQL_DATABASE"], + }, + docs: "https://dev.mysql.com/doc/", + }, + { + name: "MongoDB", + category: "database", + indicators: { + packages: ["mongodb", "mongoose"], + envVars: ["MONGODB_URI", "MONGO_URL"], + }, + docs: "https://www.mongodb.com/docs/", + }, + { + name: "Redis", + category: "cache", + indicators: { + packages: ["redis", "ioredis", "@upstash/redis"], + envVars: ["REDIS_URL", "UPSTASH_REDIS_REST_URL"], + }, + docs: "https://redis.io/docs/", + }, + { + name: "Supabase", + category: "baas", + indicators: { + packages: ["@supabase/supabase-js", "@supabase/ssr"], + envVars: ["SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY"], + }, + docs: "https://supabase.com/docs", + }, + { + name: "Firebase", + category: "baas", + indicators: { + packages: ["firebase", "firebase-admin"], + envVars: ["FIREBASE_API_KEY", "FIREBASE_PROJECT_ID"], + files: ["firebase.json", ".firebaserc"], + }, + docs: "https://firebase.google.com/docs", + }, + { + name: "Stripe", + category: "payments", + indicators: { + packages: ["stripe", "@stripe/stripe-js"], + envVars: ["STRIPE_SECRET_KEY", "STRIPE_PUBLISHABLE_KEY"], + }, + docs: "https://stripe.com/docs", + }, + { + name: "Auth.js / NextAuth", + category: "auth", + indicators: { + packages: ["next-auth", "@auth/core"], + envVars: ["NEXTAUTH_SECRET", "AUTH_SECRET", "NEXTAUTH_URL"], + }, + docs: "https://authjs.dev/", + }, + { + name: "Clerk", + category: "auth", + indicators: { + packages: ["@clerk/nextjs", "@clerk/clerk-sdk-node"], + envVars: ["CLERK_SECRET_KEY", "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"], + }, + docs: "https://clerk.com/docs", + }, + { + name: "Stack Auth", + category: "auth", + indicators: { + packages: ["@stackframe/stack", "@stack-auth/nextjs"], + envVars: ["STACK_PROJECT_ID", "STACK_SECRET_SERVER_KEY"], + }, + docs: "https://docs.stack-auth.com/", + }, + { + name: "Vercel Blob", + category: "storage", + indicators: { + packages: ["@vercel/blob"], + envVars: ["BLOB_READ_WRITE_TOKEN"], + }, + docs: "https://vercel.com/docs/storage/vercel-blob", + }, + { + name: "AWS S3", + category: "storage", + indicators: { + packages: ["@aws-sdk/client-s3", "aws-sdk"], + envVars: ["AWS_ACCESS_KEY_ID", "S3_BUCKET"], + }, + docs: "https://docs.aws.amazon.com/s3/", + }, + { + name: "Cloudinary", + category: "storage", + indicators: { + packages: ["cloudinary"], + envVars: ["CLOUDINARY_URL", "CLOUDINARY_API_KEY"], + }, + docs: "https://cloudinary.com/documentation", + }, + { + name: "OpenAI", + category: "ai", + indicators: { + packages: ["openai", "@langchain/openai"], + envVars: ["OPENAI_API_KEY"], + }, + docs: "https://platform.openai.com/docs", + }, + { + name: "Anthropic", + category: "ai", + indicators: { + packages: ["@anthropic-ai/sdk"], + envVars: ["ANTHROPIC_API_KEY"], + }, + docs: "https://docs.anthropic.com/", + }, + { + name: "SendGrid", + category: "email", + indicators: { + packages: ["@sendgrid/mail"], + envVars: ["SENDGRID_API_KEY"], + }, + docs: "https://docs.sendgrid.com/", + }, + { + name: "Resend", + category: "email", + indicators: { + packages: ["resend"], + envVars: ["RESEND_API_KEY"], + }, + docs: "https://resend.com/docs", + }, + { + name: "Sentry", + category: "monitoring", + indicators: { + packages: ["@sentry/nextjs", "@sentry/node", "@sentry/browser"], + envVars: ["SENTRY_DSN"], + }, + docs: "https://docs.sentry.io/", + }, + { + name: "Twilio", + category: "messaging", + indicators: { + packages: ["twilio"], + envVars: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], + }, + docs: "https://www.twilio.com/docs", + }, + { + name: "Vercel Analytics", + category: "analytics", + indicators: { + packages: ["@vercel/analytics"], + envVars: [], + }, + docs: "https://vercel.com/docs/analytics", + }, + { + name: "Docker", + category: "infrastructure", + indicators: { + packages: [], + envVars: [], + files: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", ".dockerignore"], + }, + docs: "https://docs.docker.com/", + }, +]; + +async function scanServices(targetDir, envVars) { + const detectedServices = []; + + let packageDeps = new Set(); + const pkgPath = path.join(targetDir, "package.json"); + if (fs.existsSync(pkgPath)) { + try { + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); + const allDeps = { + ...pkg.dependencies, + ...pkg.devDependencies, + ...pkg.peerDependencies, + }; + packageDeps = new Set(Object.keys(allDeps)); + } catch {} + } + + const reqPath = path.join(targetDir, "requirements.txt"); + if (fs.existsSync(reqPath)) { + try { + const content = fs.readFileSync(reqPath, "utf-8"); + content.split("\n").forEach((line) => { + const pkg = line.trim().split(/[>= v.name)); + + for (const sig of SERVICE_SIGNATURES) { + const matchedPackages = sig.indicators.packages?.filter((p) => + packageDeps.has(p) + ) || []; + const matchedEnvVars = sig.indicators.envVars?.filter((v) => + envVarNames.has(v) + ) || []; + const matchedFiles = sig.indicators.files?.filter((f) => + fs.existsSync(path.join(targetDir, f)) + ) || []; + + const confidence = + matchedPackages.length * 40 + + matchedEnvVars.length * 30 + + matchedFiles.length * 30; + + if (confidence > 0) { + detectedServices.push({ + name: sig.name, + category: sig.category, + confidence: Math.min(confidence, 100), + matchedPackages, + matchedEnvVars, + matchedFiles, + docs: sig.docs, + requiredEnvVars: sig.indicators.envVars || [], + }); + } + } + + return detectedServices.sort((a, b) => b.confidence - a.confidence); +} + +module.exports = { scanServices }; From db00691cea4f7113cc09973a35e6293e365c8044 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 1 Mar 2026 23:26:11 +0000 Subject: [PATCH 4/8] feat(env-agent-finder): add --show-values and --unmask flags - Scanner now captures actual values from .env files - Also reads live process.env values at scan time - --show-values shows masked values (first 4 + last 4 chars visible) - --unmask reveals full raw values - Values section and source file shown in all formats (terminal, json, md) - Default mode unchanged (summary table, no values) Co-authored-by: DealPatrol --- env-agent-finder/README.md | 98 +++++------ env-agent-finder/src/cli.js | 13 +- env-agent-finder/src/reporter.js | 162 ++++++++++++++++--- env-agent-finder/src/scanners/env-scanner.js | 49 +++++- 4 files changed, 242 insertions(+), 80 deletions(-) diff --git a/env-agent-finder/README.md b/env-agent-finder/README.md index 7641547..70b24fc 100644 --- a/env-agent-finder/README.md +++ b/env-agent-finder/README.md @@ -17,11 +17,17 @@ node src/cli.js # Scan a specific project node src/cli.js --target /path/to/project +# Show env values (masked for security) +node src/cli.js --target ./my-app --show-values + +# Show full unmasked values +node src/cli.js --target ./my-app --unmask + # Output as Markdown node src/cli.js --target ./my-app --format markdown --output report.md -# Output as JSON -node src/cli.js --format json +# Output as JSON (with values) +node src/cli.js --format json --show-values ``` ## Installation @@ -40,62 +46,56 @@ Or use npx (after publishing): npx env-agent-finder --target ./my-project ``` -## Output Example +## Output Modes +### Default โ€” summary table ``` -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - ENV AGENT FINDER โ€” Scan Report -โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - -๐Ÿ“ฆ PROJECT INFO -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - Name: my-project - Framework: Next.js 16.1.6 - Language: JavaScript/TypeScript - Package Manager: pnpm - ๐Ÿ”‘ ENVIRONMENT VARIABLES (5 found) -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Variable Service Refs Status - DATABASE_URL Database (PostgreSQL) 3 โŒ Missing + DATABASE_URL Database (PostgreSQL) 3 โœ… Set BLOB_READ_WRITE_TOKEN Vercel Blob Storage 1 โšช Optional STACK_PROJECT_ID Stack Auth 1 โŒ Missing - STACK_PUBLISHED_CLIENT_KEY Stack Auth 1 โŒ Missing - STACK_SECRET_SERVER_KEY Stack Auth 1 โŒ Missing - -๐Ÿ”Œ DETECTED SERVICES (4 found) -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - PostgreSQL - Category: database - Confidence: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘ 80% - Packages: pg, @neondatabase/serverless - Env Vars: DATABASE_URL - - Stack Auth - Category: auth - Confidence: โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 100% - Packages: @stack-auth/nextjs - Env Vars: STACK_PROJECT_ID, STACK_SECRET_SERVER_KEY - -๐ŸŒ API ROUTES (8 found) -โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - /api/projects [GET, POST] - /api/projects/[id]/activity [GET, POST] - /api/projects/[id]/analytics [GET] - /api/projects/[id]/members [GET, POST, DELETE] - /api/projects/[id]/tasks [GET, POST, PUT, DELETE] - /api/projects/[id]/tasks/[taskId]/attachments [GET, POST, DELETE] - /api/projects/[id]/tasks/[taskId]/comments [GET, POST, DELETE] - /api/upload [POST] + + ๐Ÿ’ก Use --show-values to see values, or --unmask for full values. +``` + +### `--show-values` โ€” masked values with source +``` + โœ… DATABASE_URL + Service: Database (PostgreSQL/MySQL) + Refs: 3 reference(s) in code + Value: postโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขflow + Source: .env.local + + โŒ BLOB_READ_WRITE_TOKEN + Service: Vercel Blob Storage + Refs: 1 reference(s) in code + Value: (not set) +``` + +### `--unmask` โ€” full raw values +``` + โœ… DATABASE_URL + Service: Database (PostgreSQL/MySQL) + Refs: 3 reference(s) in code + Value: postgresql://user:pass@host:5432/mydb + Source: .env.local + +๐Ÿ“„ CURRENT .env VALUES + DATABASE_URL=postgresql://user:pass@host:5432/mydb + STACK_PROJECT_ID=proj_abc123 ``` -## Output Formats +## Options -| Format | Flag | Description | -|--------|------|-------------| -| Terminal | `--format terminal` | Colorful CLI output (default) | -| Markdown | `--format markdown` | Markdown report for docs/PRs | -| JSON | `--format json` | Machine-readable for pipelines | +| Flag | Description | +|------|-------------| +| `--target ` | Directory to scan (default: current directory) | +| `--show-values`, `-v` | Show env variable values (masked by default) | +| `--unmask` | Show full unmasked values (implies `--show-values`) | +| `--format ` | Output format: `terminal`, `json`, `markdown` (default: `terminal`) | +| `--output ` | Write output to file instead of stdout | +| `-h`, `--help` | Show help message | ## What It Detects diff --git a/env-agent-finder/src/cli.js b/env-agent-finder/src/cli.js index 1507645..7a265cc 100755 --- a/env-agent-finder/src/cli.js +++ b/env-agent-finder/src/cli.js @@ -9,6 +9,8 @@ const args = process.argv.slice(2); let targetDir = process.cwd(); let outputFormat = "terminal"; let outputFile = null; +let showValues = false; +let unmask = false; for (let i = 0; i < args.length; i++) { if (args[i] === "--target" && args[i + 1]) { @@ -20,6 +22,11 @@ for (let i = 0; i < args.length; i++) { } else if (args[i] === "--output" && args[i + 1]) { outputFile = path.resolve(args[i + 1]); i++; + } else if (args[i] === "--show-values" || args[i] === "-v") { + showValues = true; + } else if (args[i] === "--unmask") { + showValues = true; + unmask = true; } else if (args[i] === "--help" || args[i] === "-h") { console.log(` env-agent-finder โ€” Scan a codebase for env vars, APIs, and service dependencies. @@ -31,10 +38,14 @@ Options: --target Directory to scan (default: current directory) --format Output format: terminal, json, markdown (default: terminal) --output Write output to file instead of stdout + --show-values, -v Show env variable values (masked by default) + --unmask Show full unmasked values (implies --show-values) -h, --help Show this help message Examples: env-agent-finder --target ./my-project + env-agent-finder --target ./my-project --show-values + env-agent-finder --target ./my-project --unmask env-agent-finder --target ./my-project --format markdown --output report.md env-agent-finder --format json `); @@ -49,7 +60,7 @@ async function main() { try { const report = await scanProject(targetDir); - const output = renderReport(report, outputFormat); + const output = renderReport(report, outputFormat, { showValues, unmask }); if (outputFile) { const fs = require("fs"); diff --git a/env-agent-finder/src/reporter.js b/env-agent-finder/src/reporter.js index d772930..660ce9b 100644 --- a/env-agent-finder/src/reporter.js +++ b/env-agent-finder/src/reporter.js @@ -1,15 +1,27 @@ -function renderTerminal(report) { +function maskValue(val) { + if (!val || val.length === 0) return "(empty)"; + if (val.length <= 8) return val.substring(0, 2) + "โ€ข".repeat(val.length - 2); + return val.substring(0, 4) + "โ€ข".repeat(val.length - 8) + val.substring(val.length - 4); +} + +function displayValue(val, source, opts) { + if (!val) return null; + if (opts.unmask) return val; + return maskValue(val); +} + +function renderTerminal(report, opts) { const lines = []; const { meta, envVars, apiRoutes, services } = report; - lines.push("โ•".repeat(60)); + lines.push("โ•".repeat(70)); lines.push(" ENV AGENT FINDER โ€” Scan Report"); - lines.push("โ•".repeat(60)); + lines.push("โ•".repeat(70)); lines.push(""); // Project meta lines.push("๐Ÿ“ฆ PROJECT INFO"); - lines.push("โ”€".repeat(40)); + lines.push("โ”€".repeat(50)); if (meta.name) lines.push(` Name: ${meta.name}`); if (meta.framework) lines.push(` Framework: ${meta.framework}`); if (meta.language) lines.push(` Language: ${meta.language}`); @@ -20,9 +32,32 @@ function renderTerminal(report) { // Env vars lines.push(`๐Ÿ”‘ ENVIRONMENT VARIABLES (${envVars.length} found)`); - lines.push("โ”€".repeat(40)); + lines.push("โ”€".repeat(50)); + if (envVars.length === 0) { lines.push(" No environment variables detected."); + } else if (opts.showValues) { + for (const v of envVars) { + const status = v.hasValue ? "โœ…" : v.required === false ? "โšช" : "โŒ"; + lines.push(""); + lines.push(` ${status} ${v.name}`); + if (v.service) lines.push(` Service: ${v.service}`); + lines.push(` Refs: ${v.references.length} reference(s) in code`); + + if (v.values && v.values.length > 0) { + for (const entry of v.values) { + const shown = displayValue(entry.value, entry.source, opts); + lines.push(` Value: ${shown}`); + lines.push(` Source: ${entry.source}`); + } + } else if (v.hasValue && v.value) { + const shown = displayValue(v.value, v.valueSource, opts); + lines.push(` Value: ${shown}`); + if (v.valueSource) lines.push(` Source: ${v.valueSource}`); + } else { + lines.push(` Value: (not set)`); + } + } } else { const maxName = Math.max(...envVars.map((v) => v.name.length), 10); lines.push( @@ -36,12 +71,14 @@ function renderTerminal(report) { ` ${v.name.padEnd(maxName)} ${service.padEnd(25)} ${String(v.references.length).padEnd(4)} ${status}` ); } + lines.push(""); + lines.push(" ๐Ÿ’ก Use --show-values to see values, or --unmask for full values."); } lines.push(""); // Services lines.push(`๐Ÿ”Œ DETECTED SERVICES (${services.length} found)`); - lines.push("โ”€".repeat(40)); + lines.push("โ”€".repeat(50)); if (services.length === 0) { lines.push(" No external services detected."); } else { @@ -67,7 +104,7 @@ function renderTerminal(report) { // API routes lines.push(`๐ŸŒ API ROUTES (${apiRoutes.length} found)`); - lines.push("โ”€".repeat(40)); + lines.push("โ”€".repeat(50)); if (apiRoutes.length === 0) { lines.push(" No API routes detected."); } else { @@ -79,11 +116,11 @@ function renderTerminal(report) { } lines.push(""); - // .env template + // .env template for missing vars const missingVars = envVars.filter((v) => !v.hasValue); if (missingVars.length > 0) { lines.push("๐Ÿ“‹ SUGGESTED .env.local TEMPLATE"); - lines.push("โ”€".repeat(40)); + lines.push("โ”€".repeat(50)); lines.push(""); let currentService = null; for (const v of missingVars) { @@ -98,19 +135,54 @@ function renderTerminal(report) { lines.push(""); } - lines.push("โ•".repeat(60)); + // Current .env snapshot (when --show-values is used) + const setVars = envVars.filter((v) => v.hasValue && v.values && v.values.length > 0); + if (opts.showValues && setVars.length > 0) { + lines.push("๐Ÿ“„ CURRENT .env VALUES"); + lines.push("โ”€".repeat(50)); + lines.push(""); + for (const v of setVars) { + const val = opts.unmask ? v.values[0].value : maskValue(v.values[0].value); + lines.push(` ${v.name}=${val}`); + } + lines.push(""); + if (!opts.unmask) { + lines.push(" ๐Ÿ”’ Values are masked. Use --unmask to reveal full values."); + lines.push(""); + } + } + + lines.push("โ•".repeat(70)); lines.push(` Scanned: ${report.targetDir}`); lines.push(` Time: ${report.scannedAt}`); - lines.push("โ•".repeat(60)); + lines.push("โ•".repeat(70)); return lines.join("\n"); } -function renderJson(report) { - return JSON.stringify(report, null, 2); +function renderJson(report, opts) { + const output = { ...report }; + + if (!opts.showValues) { + output.envVars = output.envVars.map((v) => { + const { value, values, ...rest } = v; + return rest; + }); + } else if (!opts.unmask) { + output.envVars = output.envVars.map((v) => ({ + ...v, + value: v.value ? maskValue(v.value) : null, + values: (v.values || []).map((entry) => ({ + ...entry, + value: maskValue(entry.value), + })), + })); + } + + return JSON.stringify(output, null, 2); } -function renderMarkdown(report) { +function renderMarkdown(report, opts) { const lines = []; const { meta, envVars, apiRoutes, services } = report; @@ -136,14 +208,35 @@ function renderMarkdown(report) { // Env vars lines.push(`## Environment Variables (${envVars.length})`); lines.push(""); + if (envVars.length > 0) { - lines.push("| Variable | Service | References | Status |"); - lines.push("|----------|---------|------------|--------|"); - for (const v of envVars) { - const status = v.hasValue ? "Set" : v.required === false ? "Optional" : "**Missing**"; - lines.push( - `| \`${v.name}\` | ${v.service || "โ€”"} | ${v.references.length} | ${status} |` - ); + if (opts.showValues) { + lines.push("| Variable | Service | Value | Source | Status |"); + lines.push("|----------|---------|-------|--------|--------|"); + for (const v of envVars) { + const status = v.hasValue ? "Set" : v.required === false ? "Optional" : "**Missing**"; + let val = "(not set)"; + let source = "โ€”"; + if (v.values && v.values.length > 0) { + val = opts.unmask ? v.values[0].value : maskValue(v.values[0].value); + source = v.values[0].source; + } else if (v.hasValue && v.value) { + val = opts.unmask ? v.value : maskValue(v.value); + source = v.valueSource || "โ€”"; + } + lines.push( + `| \`${v.name}\` | ${v.service || "โ€”"} | \`${val}\` | ${source} | ${status} |` + ); + } + } else { + lines.push("| Variable | Service | References | Status |"); + lines.push("|----------|---------|------------|--------|"); + for (const v of envVars) { + const status = v.hasValue ? "Set" : v.required === false ? "Optional" : "**Missing**"; + lines.push( + `| \`${v.name}\` | ${v.service || "โ€”"} | ${v.references.length} | ${status} |` + ); + } } } else { lines.push("No environment variables detected."); @@ -209,19 +302,38 @@ function renderMarkdown(report) { lines.push("```"); } + // Current .env snapshot + const setVars = envVars.filter((v) => v.hasValue && v.values && v.values.length > 0); + if (opts.showValues && setVars.length > 0) { + lines.push(""); + lines.push("## Current `.env` Values"); + lines.push(""); + lines.push("```bash"); + for (const v of setVars) { + const val = opts.unmask ? v.values[0].value : maskValue(v.values[0].value); + lines.push(`${v.name}=${val}`); + } + lines.push("```"); + if (!opts.unmask) { + lines.push(""); + lines.push("> Values are masked. Use `--unmask` to reveal full values."); + } + } + return lines.join("\n"); } -function renderReport(report, format) { +function renderReport(report, format, opts = {}) { + const options = { showValues: false, unmask: false, ...opts }; switch (format) { case "json": - return renderJson(report); + return renderJson(report, options); case "markdown": case "md": - return renderMarkdown(report); + return renderMarkdown(report, options); case "terminal": default: - return renderTerminal(report); + return renderTerminal(report, options); } } diff --git a/env-agent-finder/src/scanners/env-scanner.js b/env-agent-finder/src/scanners/env-scanner.js index 8856a58..997b080 100644 --- a/env-agent-finder/src/scanners/env-scanner.js +++ b/env-agent-finder/src/scanners/env-scanner.js @@ -112,6 +112,14 @@ function extractEnvReferences(content, filePath) { return refs; } +function stripQuotes(val) { + if ((val.startsWith('"') && val.endsWith('"')) || + (val.startsWith("'") && val.endsWith("'"))) { + return val.slice(1, -1); + } + return val; +} + function parseEnvFile(filePath) { const vars = []; try { @@ -123,11 +131,13 @@ function parseEnvFile(filePath) { const eqIdx = line.indexOf("="); if (eqIdx > 0) { const name = line.substring(0, eqIdx).trim(); - const value = line.substring(eqIdx + 1).trim(); + const rawValue = line.substring(eqIdx + 1).trim(); + const value = stripQuotes(rawValue); if (/^[A-Z_][A-Z0-9_]*$/.test(name)) { vars.push({ name, - hasValue: value.length > 0 && value !== '""' && value !== "''", + value: value || null, + hasValue: value.length > 0, file: filePath, line: i + 1, }); @@ -170,25 +180,54 @@ async function scanEnvVars(targetDir) { service: known?.service || null, required: known?.required ?? null, hasValue: false, + value: null, + valueSource: null, + values: [], }); } varMap.get(ref.name).references.push({ file: ref.file, line: ref.line }); } for (const envVar of envFileVars) { + const relFile = path.relative(targetDir, envVar.file); if (!varMap.has(envVar.name)) { const known = WELL_KNOWN_SERVICES[envVar.name]; varMap.set(envVar.name, { name: envVar.name, - references: [{ file: path.relative(targetDir, envVar.file), line: envVar.line }], + references: [{ file: relFile, line: envVar.line }], service: known?.service || null, required: known?.required ?? null, hasValue: envVar.hasValue, + value: envVar.value, + valueSource: envVar.hasValue ? relFile : null, + values: envVar.hasValue + ? [{ value: envVar.value, source: relFile }] + : [], }); } else { const existing = varMap.get(envVar.name); - existing.hasValue = existing.hasValue || envVar.hasValue; - existing.references.push({ file: path.relative(targetDir, envVar.file), line: envVar.line }); + existing.references.push({ file: relFile, line: envVar.line }); + if (envVar.hasValue) { + existing.values.push({ value: envVar.value, source: relFile }); + if (!existing.hasValue) { + existing.hasValue = true; + existing.value = envVar.value; + existing.valueSource = relFile; + } + } + } + } + + // Also check live process.env for any vars we found in code + for (const [name, entry] of varMap) { + const liveValue = process.env[name]; + if (liveValue !== undefined && liveValue.length > 0) { + entry.values.push({ value: liveValue, source: "process.env (runtime)" }); + if (!entry.hasValue) { + entry.hasValue = true; + entry.value = liveValue; + entry.valueSource = "process.env (runtime)"; + } } } From 3d6f0e2e1001a252092ace345dfa2b7a2c200ddf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 1 Mar 2026 23:55:14 +0000 Subject: [PATCH 5/8] feat(env-agent-finder): add --setup provisioner that builds .env.local New provisioner module that: - Scans project to discover all needed env vars from code + dependencies - Auto-generates secrets (NEXTAUTH_SECRET, JWT_SECRET, etc.) - Auto-generates local database URLs (PostgreSQL, MySQL, MongoDB, Redis) - Auto-generates app URLs for local dev - Preserves existing .env values (never overwrites) - Interactive mode (--setup): walks through each service with step-by-step signup instructions and prompts for API keys - Non-interactive mode (--auto): auto-generates only, skips prompts - Writes final grouped .env.local with service-labeled sections - Supports 17+ services with signup guides (Stripe, OpenAI, etc.) Co-authored-by: DealPatrol --- env-agent-finder/README.md | 85 ++++- env-agent-finder/src/cli.js | 43 ++- env-agent-finder/src/provisioner.js | 499 ++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+), 17 deletions(-) create mode 100644 env-agent-finder/src/provisioner.js diff --git a/env-agent-finder/README.md b/env-agent-finder/README.md index 70b24fc..1ae7154 100644 --- a/env-agent-finder/README.md +++ b/env-agent-finder/README.md @@ -125,13 +125,88 @@ npx env-agent-finder --target ./my-project | Cache | Redis, Upstash | | Messaging | Twilio | +## Setup Mode โ€” Auto-Provision Your `.env` + +The killer feature: point it at any project and it **builds your `.env.local` for you**. + +### `--setup` (Interactive) + +Scans the project, auto-generates secrets and DB URLs, then walks you through each external service with step-by-step signup instructions and prompts you to paste your keys: + +```bash +node src/cli.js --target ./my-project --setup +``` + +``` +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + ENV AGENT FINDER โ€” Setup Mode +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + + Found 8 environment variable(s) needed. + Detected 4 service(s): PostgreSQL, Stripe, OpenAI, Auth.js + +โšก AUTO-GENERATING values... +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โœ… DATABASE_URL = postgresql://postgres:postgres@localhost:5432/app_dev + โœ… NEXTAUTH_SECRET = a8Kx9mP2qR7... + โœ… NEXTAUTH_URL = http://localhost:PORT + +๐Ÿ”‘ STRIPE +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + How to get your keys: + 1. Go to https://dashboard.stripe.com/register and create an account + 2. Go to https://dashboard.stripe.com/apikeys + 3. Copy your Publishable key โ†’ STRIPE_PUBLISHABLE_KEY + 4. Copy your Secret key โ†’ STRIPE_SECRET_KEY + + Enter STRIPE_SECRET_KEY (or press Enter to skip): sk_test_abc123 + โœ… STRIPE_SECRET_KEY saved + +๐Ÿ”‘ OPENAI +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + How to get your keys: + 1. Go to https://platform.openai.com/api-keys + 2. Click 'Create new secret key' + + Enter OPENAI_API_KEY (or press Enter to skip): sk-proj-abc123 + โœ… OPENAI_API_KEY saved + +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + SETUP COMPLETE โ€” Written to: ./my-project/.env.local +โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +``` + +### `--auto` (Non-Interactive) + +Auto-generates everything it can (secrets, database URLs) without prompting. Perfect for CI or AI agents: + +```bash +node src/cli.js --target ./my-project --auto +``` + +### What Gets Auto-Generated vs What You Need to Provide + +| Type | Example Variables | How It's Handled | +|------|-------------------|------------------| +| **Secrets** | `NEXTAUTH_SECRET`, `JWT_SECRET`, `AUTH_SECRET` | Auto-generated (random 48-char string) | +| **Database URLs** | `DATABASE_URL`, `POSTGRES_URL`, `MONGODB_URI` | Auto-generated (local dev defaults) | +| **App URLs** | `NEXTAUTH_URL`, `APP_URL` | Auto-generated (localhost dev URL) | +| **API Keys** | `STRIPE_SECRET_KEY`, `OPENAI_API_KEY` | Interactive prompt with signup instructions | +| **OAuth Creds** | `GOOGLE_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | Interactive prompt with setup guide | +| **Existing Values** | Any var already in `.env` | Kept as-is (never overwritten) | + +### Supported Services (with signup guides) + +Stripe, OpenAI, Anthropic, Clerk, Stack Auth, Supabase, Firebase, Vercel Blob, AWS S3, SendGrid, Resend, Sentry, Google OAuth, GitHub OAuth, Twilio, Cloudinary, Upstash Redis + ## Use Cases -- **AI Agents**: Automatically discover what env vars and services a project needs before setting up -- **Onboarding**: Generate a setup checklist for new developers -- **CI/CD**: Validate that all required secrets are configured -- **Documentation**: Auto-generate env var docs for your README -- **Security Audits**: Find all external service dependencies +- **New project setup**: Clone a repo, run `--setup`, get a working `.env.local` in 60 seconds +- **AI Agents**: Run `--auto` to provision env vars without human interaction +- **Onboarding**: New developer? Run `--setup` and follow the guided prompts +- **CI/CD**: Validate that all required secrets are configured with scan mode +- **Documentation**: Auto-generate env var docs with `--format markdown` +- **Security Audits**: Find all external service dependencies and API key usage ## License diff --git a/env-agent-finder/src/cli.js b/env-agent-finder/src/cli.js index 7a265cc..984f505 100755 --- a/env-agent-finder/src/cli.js +++ b/env-agent-finder/src/cli.js @@ -3,6 +3,7 @@ const path = require("path"); const { scanProject } = require("./index"); const { renderReport } = require("./reporter"); +const { provisionEnv } = require("./provisioner"); const args = process.argv.slice(2); @@ -11,6 +12,8 @@ let outputFormat = "terminal"; let outputFile = null; let showValues = false; let unmask = false; +let setupMode = false; +let autoOnly = false; for (let i = 0; i < args.length; i++) { if (args[i] === "--target" && args[i + 1]) { @@ -27,6 +30,11 @@ for (let i = 0; i < args.length; i++) { } else if (args[i] === "--unmask") { showValues = true; unmask = true; + } else if (args[i] === "--setup" || args[i] === "-s") { + setupMode = true; + } else if (args[i] === "--auto") { + setupMode = true; + autoOnly = true; } else if (args[i] === "--help" || args[i] === "-h") { console.log(` env-agent-finder โ€” Scan a codebase for env vars, APIs, and service dependencies. @@ -34,20 +42,26 @@ env-agent-finder โ€” Scan a codebase for env vars, APIs, and service dependencie Usage: env-agent-finder [options] -Options: +SCAN MODE (default): --target Directory to scan (default: current directory) --format Output format: terminal, json, markdown (default: terminal) --output Write output to file instead of stdout --show-values, -v Show env variable values (masked by default) --unmask Show full unmasked values (implies --show-values) + +SETUP MODE (writes .env.local): + --setup, -s Interactive setup: auto-generates what it can, prompts for the rest + --auto Non-interactive: only auto-generate secrets and DB URLs, skip prompts + +OTHER: -h, --help Show this help message Examples: - env-agent-finder --target ./my-project - env-agent-finder --target ./my-project --show-values - env-agent-finder --target ./my-project --unmask - env-agent-finder --target ./my-project --format markdown --output report.md - env-agent-finder --format json + env-agent-finder --target ./my-project # Scan only + env-agent-finder --target ./my-project --show-values # Scan with values + env-agent-finder --target ./my-project --setup # Interactive setup + env-agent-finder --target ./my-project --auto # Auto-generate only + env-agent-finder --target ./my-project --format markdown -v # Markdown with values `); process.exit(0); } else if (!args[i].startsWith("--")) { @@ -60,14 +74,19 @@ async function main() { try { const report = await scanProject(targetDir); - const output = renderReport(report, outputFormat, { showValues, unmask }); - if (outputFile) { - const fs = require("fs"); - fs.writeFileSync(outputFile, output, "utf-8"); - console.log(`\nโœ… Report written to: ${outputFile}`); + if (setupMode) { + await provisionEnv(targetDir, report, { interactive: !autoOnly, autoOnly }); } else { - console.log(output); + const output = renderReport(report, outputFormat, { showValues, unmask }); + + if (outputFile) { + const fs = require("fs"); + fs.writeFileSync(outputFile, output, "utf-8"); + console.log(`\nโœ… Report written to: ${outputFile}`); + } else { + console.log(output); + } } } catch (err) { console.error(`\nโŒ Error scanning project: ${err.message}`); diff --git a/env-agent-finder/src/provisioner.js b/env-agent-finder/src/provisioner.js new file mode 100644 index 0000000..99e34a6 --- /dev/null +++ b/env-agent-finder/src/provisioner.js @@ -0,0 +1,499 @@ +const fs = require("fs"); +const path = require("path"); +const crypto = require("crypto"); +const readline = require("readline"); + +function generateSecret(length = 64) { + return crypto.randomBytes(length).toString("base64url").substring(0, length); +} + +const AUTO_GENERATORS = { + NEXTAUTH_SECRET: () => generateSecret(48), + AUTH_SECRET: () => generateSecret(48), + JWT_SECRET: () => generateSecret(48), + NEXTAUTH_URL: () => "http://localhost:3000", // pragma: allowlist secret + NEXT_PUBLIC_APP_URL: () => "http://localhost:3000", // pragma: allowlist secret + APP_URL: () => "http://localhost:3000", // pragma: allowlist secret + APP_SECRET: () => generateSecret(48), + ENCRYPTION_KEY: () => generateSecret(32), + SESSION_SECRET: () => generateSecret(48), + COOKIE_SECRET: () => generateSecret(48), + SECRET_KEY: () => generateSecret(48), + API_SECRET: () => generateSecret(48), +}; + +const LOCAL_DB_GENERATORS = { + DATABASE_URL: (ctx) => { + if (ctx.services.has("PostgreSQL")) return "postgresql://postgres:postgres@localhost:5432/app_dev"; + if (ctx.services.has("MySQL")) return "mysql://root:root@localhost:3306/app_dev"; + if (ctx.services.has("MongoDB")) return "mongodb://localhost:27017/app_dev"; + return "postgresql://postgres:postgres@localhost:5432/app_dev"; + }, + POSTGRES_URL: () => "postgresql://postgres:postgres@localhost:5432/app_dev", + PG_CONNECTION_STRING: () => "postgresql://postgres:postgres@localhost:5432/app_dev", + MYSQL_URL: () => "mysql://root:root@localhost:3306/app_dev", + MYSQL_HOST: () => "localhost", + MYSQL_DATABASE: () => "app_dev", + MONGODB_URI: () => "mongodb://localhost:27017/app_dev", + MONGO_URL: () => "mongodb://localhost:27017/app_dev", + REDIS_URL: () => "redis://localhost:6379", +}; + +const SERVICE_SIGNUP_INFO = { + "Stripe": { + vars: ["STRIPE_SECRET_KEY", "STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"], + signupUrl: "https://dashboard.stripe.com/register", + keysUrl: "https://dashboard.stripe.com/apikeys", + steps: [ + "1. Go to https://dashboard.stripe.com/register and create an account", + "2. Go to https://dashboard.stripe.com/apikeys", + "3. Copy your Publishable key โ†’ STRIPE_PUBLISHABLE_KEY", + "4. Copy your Secret key โ†’ STRIPE_SECRET_KEY", + "5. For webhooks: go to Developers > Webhooks > Add endpoint", + "6. Copy the Signing secret โ†’ STRIPE_WEBHOOK_SECRET", + ], + }, + "OpenAI": { + vars: ["OPENAI_API_KEY"], + signupUrl: "https://platform.openai.com/signup", + keysUrl: "https://platform.openai.com/api-keys", + steps: [ + "1. Go to https://platform.openai.com/signup and create an account", + "2. Go to https://platform.openai.com/api-keys", + "3. Click 'Create new secret key'", + "4. Copy the key โ†’ OPENAI_API_KEY", + ], + }, + "Anthropic": { + vars: ["ANTHROPIC_API_KEY"], + signupUrl: "https://console.anthropic.com/", + keysUrl: "https://console.anthropic.com/settings/keys", + steps: [ + "1. Go to https://console.anthropic.com/ and create an account", + "2. Go to https://console.anthropic.com/settings/keys", + "3. Click 'Create Key'", + "4. Copy the key โ†’ ANTHROPIC_API_KEY", + ], + }, + "Clerk": { + vars: ["CLERK_SECRET_KEY", "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"], + signupUrl: "https://dashboard.clerk.com/sign-up", + keysUrl: "https://dashboard.clerk.com/ โ†’ your app โ†’ API Keys", + steps: [ + "1. Go to https://dashboard.clerk.com/sign-up and create an account", + "2. Create an application", + "3. Go to API Keys in the sidebar", + "4. Copy Publishable key โ†’ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", + "5. Copy Secret key โ†’ CLERK_SECRET_KEY", + ], + }, + "Stack Auth": { + vars: ["STACK_PROJECT_ID", "STACK_PUBLISHED_CLIENT_KEY", "STACK_SECRET_SERVER_KEY"], + signupUrl: "https://app.stack-auth.com/", + keysUrl: "https://app.stack-auth.com/ โ†’ your project โ†’ Settings", + steps: [ + "1. Go to https://app.stack-auth.com/ and create an account", + "2. Create a project", + "3. Go to project Settings โ†’ API Keys", + "4. Copy Project ID โ†’ STACK_PROJECT_ID", + "5. Copy Publishable Client Key โ†’ STACK_PUBLISHED_CLIENT_KEY", + "6. Copy Secret Server Key โ†’ STACK_SECRET_SERVER_KEY", + ], + }, + "Supabase": { + vars: ["SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_URL", "SUPABASE_SERVICE_ROLE_KEY", "SUPABASE_ANON_KEY", "NEXT_PUBLIC_SUPABASE_ANON_KEY"], + signupUrl: "https://supabase.com/dashboard", + keysUrl: "https://supabase.com/dashboard โ†’ your project โ†’ Settings โ†’ API", + steps: [ + "1. Go to https://supabase.com/dashboard and create an account", + "2. Create a new project", + "3. Go to Settings โ†’ API", + "4. Copy Project URL โ†’ SUPABASE_URL / NEXT_PUBLIC_SUPABASE_URL", + "5. Copy anon/public key โ†’ SUPABASE_ANON_KEY / NEXT_PUBLIC_SUPABASE_ANON_KEY", + "6. Copy service_role key โ†’ SUPABASE_SERVICE_ROLE_KEY", + ], + }, + "Firebase": { + vars: ["FIREBASE_API_KEY", "FIREBASE_PROJECT_ID", "FIREBASE_AUTH_DOMAIN", "FIREBASE_STORAGE_BUCKET"], + signupUrl: "https://console.firebase.google.com/", + keysUrl: "https://console.firebase.google.com/ โ†’ Project settings", + steps: [ + "1. Go to https://console.firebase.google.com/ and create a project", + "2. Go to Project settings (gear icon)", + "3. Scroll to 'Your apps' โ†’ Add a web app", + "4. Copy the config values into your env vars", + ], + }, + "Vercel Blob": { + vars: ["BLOB_READ_WRITE_TOKEN"], + signupUrl: "https://vercel.com/signup", + keysUrl: "https://vercel.com/dashboard/stores", + steps: [ + "1. Go to https://vercel.com/signup and create an account", + "2. Go to Storage โ†’ Create a Blob Store", + "3. Copy the BLOB_READ_WRITE_TOKEN from the store settings", + ], + }, + "AWS S3": { + vars: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "S3_BUCKET"], + signupUrl: "https://aws.amazon.com/", + keysUrl: "https://console.aws.amazon.com/iam/home#/security_credentials", + steps: [ + "1. Go to https://aws.amazon.com/ and create an account", + "2. Go to IAM โ†’ Security credentials โ†’ Create access key", + "3. Copy Access key ID โ†’ AWS_ACCESS_KEY_ID", + "4. Copy Secret access key โ†’ AWS_SECRET_ACCESS_KEY", + "5. Set AWS_REGION (e.g. us-east-1)", + "6. Create an S3 bucket and set S3_BUCKET to its name", + ], + }, + "SendGrid": { + vars: ["SENDGRID_API_KEY"], + signupUrl: "https://signup.sendgrid.com/", + keysUrl: "https://app.sendgrid.com/settings/api_keys", + steps: [ + "1. Go to https://signup.sendgrid.com/ and create an account", + "2. Go to Settings โ†’ API Keys โ†’ Create API Key", + "3. Copy the key โ†’ SENDGRID_API_KEY", + ], + }, + "Resend": { + vars: ["RESEND_API_KEY"], + signupUrl: "https://resend.com/signup", + keysUrl: "https://resend.com/api-keys", + steps: [ + "1. Go to https://resend.com/signup and create an account", + "2. Go to API Keys โ†’ Create API Key", + "3. Copy the key โ†’ RESEND_API_KEY", + ], + }, + "Sentry": { + vars: ["SENTRY_DSN", "SENTRY_AUTH_TOKEN"], + signupUrl: "https://sentry.io/signup/", + keysUrl: "https://sentry.io โ†’ your project โ†’ Settings โ†’ Client Keys (DSN)", + steps: [ + "1. Go to https://sentry.io/signup/ and create an account", + "2. Create a project for your platform", + "3. Copy the DSN โ†’ SENTRY_DSN", + ], + }, + "Google OAuth": { + vars: ["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"], + signupUrl: "https://console.cloud.google.com/", + keysUrl: "https://console.cloud.google.com/apis/credentials", + steps: [ + "1. Go to https://console.cloud.google.com/ and create a project", + "2. Go to APIs & Services โ†’ Credentials โ†’ Create OAuth 2.0 Client", + "3. Set authorized redirect URIs", + "4. Copy Client ID โ†’ GOOGLE_CLIENT_ID", + "5. Copy Client Secret โ†’ GOOGLE_CLIENT_SECRET", + ], + }, + "GitHub OAuth": { + vars: ["GITHUB_CLIENT_ID", "GITHUB_CLIENT_SECRET"], + signupUrl: "https://github.com/settings/developers", + keysUrl: "https://github.com/settings/developers", + steps: [ + "1. Go to https://github.com/settings/developers", + "2. Click 'New OAuth App'", + "3. Set the callback URL (e.g. http://localhost:PORT/api/auth/callback/github)", // pragma: allowlist secret + "4. Copy Client ID โ†’ GITHUB_CLIENT_ID", + "5. Copy Client Secret โ†’ GITHUB_CLIENT_SECRET", + ], + }, + "Twilio": { + vars: ["TWILIO_ACCOUNT_SID", "TWILIO_AUTH_TOKEN"], + signupUrl: "https://www.twilio.com/try-twilio", + keysUrl: "https://console.twilio.com/", + steps: [ + "1. Go to https://www.twilio.com/try-twilio and create an account", + "2. Your Account SID and Auth Token are on the Console dashboard", + "3. Copy Account SID โ†’ TWILIO_ACCOUNT_SID", + "4. Copy Auth Token โ†’ TWILIO_AUTH_TOKEN", + ], + }, + "Cloudinary": { + vars: ["CLOUDINARY_URL", "CLOUDINARY_API_KEY", "CLOUDINARY_API_SECRET", "CLOUDINARY_CLOUD_NAME"], + signupUrl: "https://cloudinary.com/users/register_free", + keysUrl: "https://console.cloudinary.com/settings/api-keys", + steps: [ + "1. Go to https://cloudinary.com/users/register_free and create an account", + "2. Go to Settings โ†’ API Keys", + "3. Copy your credentials into the env vars", + ], + }, + "Upstash Redis": { + vars: ["UPSTASH_REDIS_REST_URL", "UPSTASH_REDIS_REST_TOKEN"], + signupUrl: "https://console.upstash.com/", + keysUrl: "https://console.upstash.com/ โ†’ your database โ†’ REST API", + steps: [ + "1. Go to https://console.upstash.com/ and create an account", + "2. Create a Redis database", + "3. Go to the REST API tab", + "4. Copy URL โ†’ UPSTASH_REDIS_REST_URL", + "5. Copy Token โ†’ UPSTASH_REDIS_REST_TOKEN", + ], + }, +}; + +function createReadlineInterface() { + return readline.createInterface({ + input: process.stdin, + output: process.stderr, + }); +} + +function ask(rl, question) { + return new Promise((resolve) => { + rl.question(question, (answer) => resolve(answer.trim())); + }); +} + +async function provisionEnv(targetDir, scanReport, options = {}) { + const { interactive = true, autoOnly = false } = options; + const { envVars, services } = scanReport; + + const serviceNames = new Set(services.map((s) => s.name)); + const ctx = { services: serviceNames }; + + const result = { + generated: [], + prompted: [], + skipped: [], + written: false, + envFilePath: null, + }; + + const finalEnv = {}; + const existingValues = {}; + + for (const v of envVars) { + if (v.hasValue && v.value) { + existingValues[v.name] = v.value; + } + } + + const allNeededVars = new Set(); + + for (const v of envVars) { + allNeededVars.add(v.name); + } + for (const svc of services) { + for (const envVar of svc.requiredEnvVars || []) { + allNeededVars.add(envVar); + } + } + + const NODE_INTERNALS = new Set([ + "NODE_ENV", "PORT", "HOST", "HOSTNAME", "HOME", "PATH", "PWD", + "LANG", "TERM", "SHELL", "USER", "TZ", "CI", "DEBUG", + "VERCEL", "VERCEL_ENV", "VERCEL_URL", + ]); + + const varsToProvision = Array.from(allNeededVars).filter( + (name) => !NODE_INTERNALS.has(name) + ); + + console.log(""); + console.log("โ•".repeat(60)); + console.log(" ENV AGENT FINDER โ€” Setup Mode"); + console.log("โ•".repeat(60)); + console.log(""); + console.log(` Found ${varsToProvision.length} environment variable(s) needed.`); + console.log(` Detected ${services.length} service(s): ${services.map((s) => s.name).join(", ") || "none"}`); + console.log(""); + + // Phase 1: Keep existing values + for (const name of varsToProvision) { + if (existingValues[name]) { + finalEnv[name] = existingValues[name]; + result.generated.push({ name, value: existingValues[name], source: "existing" }); + } + } + + // Phase 2: Auto-generate secrets and local DB URLs + console.log("โšก AUTO-GENERATING values..."); + console.log("โ”€".repeat(40)); + + for (const name of varsToProvision) { + if (finalEnv[name]) continue; + + if (AUTO_GENERATORS[name]) { + const value = AUTO_GENERATORS[name](); + finalEnv[name] = value; + result.generated.push({ name, value, source: "auto-generated" }); + console.log(` โœ… ${name} = ${value.substring(0, 30)}${value.length > 30 ? "..." : ""}`); + } else if (LOCAL_DB_GENERATORS[name]) { + const value = LOCAL_DB_GENERATORS[name](ctx); + finalEnv[name] = value; + result.generated.push({ name, value, source: "local-dev-default" }); + console.log(` โœ… ${name} = ${value}`); + } + } + console.log(""); + + // Phase 3: Interactive prompts for external services + const remainingVars = varsToProvision.filter((name) => !finalEnv[name]); + + if (remainingVars.length > 0 && interactive && !autoOnly) { + const serviceVarMap = new Map(); + + for (const name of remainingVars) { + let matched = false; + for (const [serviceName, info] of Object.entries(SERVICE_SIGNUP_INFO)) { + if (info.vars.includes(name)) { + if (!serviceVarMap.has(serviceName)) { + serviceVarMap.set(serviceName, { info, vars: [] }); + } + serviceVarMap.get(serviceName).vars.push(name); + matched = true; + break; + } + } + if (!matched) { + if (!serviceVarMap.has("__unknown__")) { + serviceVarMap.set("__unknown__", { info: null, vars: [] }); + } + serviceVarMap.get("__unknown__").vars.push(name); + } + } + + const rl = createReadlineInterface(); + + for (const [serviceName, { info, vars }] of serviceVarMap) { + if (serviceName === "__unknown__") continue; + + console.log(`๐Ÿ”‘ ${serviceName.toUpperCase()}`); + console.log("โ”€".repeat(40)); + if (info) { + console.log(" How to get your keys:"); + for (const step of info.steps) { + console.log(` ${step}`); + } + console.log(""); + } + + for (const name of vars) { + const answer = await ask(rl, ` Enter ${name} (or press Enter to skip): `); + if (answer) { + finalEnv[name] = answer; + result.prompted.push({ name, source: serviceName }); + console.log(` โœ… ${name} saved`); + } else { + result.skipped.push({ name, service: serviceName, signupUrl: info?.signupUrl }); + console.log(` โญ๏ธ ${name} skipped`); + } + } + console.log(""); + } + + const unknownEntry = serviceVarMap.get("__unknown__"); + if (unknownEntry && unknownEntry.vars.length > 0) { + console.log("โ“ OTHER VARIABLES"); + console.log("โ”€".repeat(40)); + + for (const name of unknownEntry.vars) { + const answer = await ask(rl, ` Enter ${name} (or press Enter to skip): `); + if (answer) { + finalEnv[name] = answer; + result.prompted.push({ name, source: "manual" }); + console.log(` โœ… ${name} saved`); + } else { + result.skipped.push({ name, service: null, signupUrl: null }); + console.log(` โญ๏ธ ${name} skipped`); + } + } + console.log(""); + } + + rl.close(); + } else if (remainingVars.length > 0) { + for (const name of remainingVars) { + let serviceName = null; + let signupUrl = null; + for (const [svcName, info] of Object.entries(SERVICE_SIGNUP_INFO)) { + if (info.vars.includes(name)) { + serviceName = svcName; + signupUrl = info.signupUrl; + break; + } + } + result.skipped.push({ name, service: serviceName, signupUrl }); + } + } + + // Phase 4: Write .env.local + const envFilePath = path.join(targetDir, ".env.local"); + const envLines = []; + + envLines.push("# Generated by env-agent-finder --setup"); + envLines.push(`# ${new Date().toISOString()}`); + envLines.push(""); + + const groupedByService = new Map(); + const varServiceMap = {}; + + for (const svc of services) { + for (const envVar of svc.requiredEnvVars || []) { + varServiceMap[envVar] = svc.name; + } + } + for (const [serviceName, info] of Object.entries(SERVICE_SIGNUP_INFO)) { + for (const v of info.vars) { + if (!varServiceMap[v]) varServiceMap[v] = serviceName; + } + } + + for (const name of varsToProvision) { + const svc = varServiceMap[name] || "Other"; + if (!groupedByService.has(svc)) groupedByService.set(svc, []); + groupedByService.get(svc).push(name); + } + + for (const [service, vars] of groupedByService) { + envLines.push(`# ${service}`); + for (const name of vars) { + const value = finalEnv[name] || ""; + envLines.push(`${name}=${value}`); + } + envLines.push(""); + } + + fs.writeFileSync(envFilePath, envLines.join("\n"), "utf-8"); + result.written = true; + result.envFilePath = envFilePath; + + // Summary + console.log("โ•".repeat(60)); + console.log(" SETUP COMPLETE"); + console.log("โ•".repeat(60)); + console.log(""); + console.log(` ๐Ÿ“„ Written to: ${envFilePath}`); + console.log(""); + + const existingCount = result.generated.filter((g) => g.source === "existing").length; + const autoCount = result.generated.filter((g) => g.source !== "existing").length; + + if (existingCount > 0) console.log(` โ™ป๏ธ ${existingCount} value(s) kept from existing .env`); + if (autoCount > 0) console.log(` โšก ${autoCount} value(s) auto-generated (secrets, DB URLs)`); + if (result.prompted.length > 0) console.log(` ๐Ÿ”‘ ${result.prompted.length} value(s) provided by you`); + + if (result.skipped.length > 0) { + console.log(` โญ๏ธ ${result.skipped.length} value(s) still need to be filled in:`); + console.log(""); + for (const skip of result.skipped) { + const where = skip.signupUrl ? ` โ†’ ${skip.signupUrl}` : ""; + console.log(` โŒ ${skip.name}${skip.service ? ` (${skip.service})` : ""}${where}`); + } + } else { + console.log(""); + console.log(" ๐ŸŽ‰ All environment variables are configured!"); + } + + console.log(""); + console.log("โ•".repeat(60)); + + return result; +} + +module.exports = { provisionEnv }; From 2a050ddab429cce43bf3fdd15a6093ccb58fb77d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 2 Mar 2026 00:23:36 +0000 Subject: [PATCH 6/8] feat(env-agent-finder): add web application UI - Node.js http server (zero deps) on port 4800 - Single-page dark-themed app (app.html) - Scan any project by entering its path - Visual display of env vars, services, API routes - Auto-fill secrets button generates values in-browser - Download .env.local or save directly to project - New 'npm start' / 'npm run app' script to launch Co-authored-by: DealPatrol --- env-agent-finder/package.json | 3 +- env-agent-finder/src/app.html | 738 +++++++++++++++++++++++++++++++++ env-agent-finder/src/server.js | 164 ++++++++ 3 files changed, 904 insertions(+), 1 deletion(-) create mode 100644 env-agent-finder/src/app.html create mode 100644 env-agent-finder/src/server.js diff --git a/env-agent-finder/package.json b/env-agent-finder/package.json index e28a115..205928e 100644 --- a/env-agent-finder/package.json +++ b/env-agent-finder/package.json @@ -7,7 +7,8 @@ "env-agent-finder": "src/cli.js" }, "scripts": { - "start": "node src/cli.js", + "start": "node src/server.js", + "app": "node src/server.js", "scan": "node src/cli.js", "test": "node src/cli.js --target ." }, diff --git a/env-agent-finder/src/app.html b/env-agent-finder/src/app.html new file mode 100644 index 0000000..8905306 --- /dev/null +++ b/env-agent-finder/src/app.html @@ -0,0 +1,738 @@ + + + + + +env-agent-finder + + + + +
+
+

env-agent-finder

+

Scan any project. Find every env variable. Fill in the values. Get your .env.local.

+
+ +
+ +
+ + +
+
+ + + +
+ +
+
+

๐Ÿ“ฆ Project Info

+
+
+
+
+
+ + +
+
+

๐Ÿ”‘ Environment Variables 0

+
+
+
+ + +
+
+

๐Ÿ”Œ Detected Services 0

+
+
+
+ + +
+
+

๐ŸŒ API Routes 0

+
+
+
+ + +
+ + + +
+
+
+ +
+ + + + + diff --git a/env-agent-finder/src/server.js b/env-agent-finder/src/server.js new file mode 100644 index 0000000..fcc1a01 --- /dev/null +++ b/env-agent-finder/src/server.js @@ -0,0 +1,164 @@ +#!/usr/bin/env node + +const http = require("http"); +const fs = require("fs"); +const path = require("path"); +const url = require("url"); +const { scanProject } = require("./index"); + +const PORT = process.env.EAF_PORT || 4800; + +function parseBody(req) { + return new Promise((resolve, reject) => { + let body = ""; + req.on("data", (chunk) => (body += chunk)); + req.on("end", () => { + try { + resolve(JSON.parse(body)); + } catch { + resolve({}); + } + }); + req.on("error", reject); + }); +} + +function json(res, data, status = 200) { + res.writeHead(status, { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }); + res.end(JSON.stringify(data)); +} + +function serveFile(res, filePath, contentType) { + try { + const content = fs.readFileSync(filePath, "utf-8"); + res.writeHead(200, { "Content-Type": contentType }); + res.end(content); + } catch { + res.writeHead(404); + res.end("Not found"); + } +} + +function generateEnvFile(envVars, services, userValues = {}) { + const lines = []; + lines.push("# Generated by env-agent-finder"); + lines.push(`# ${new Date().toISOString()}`); + lines.push(""); + + const varServiceMap = {}; + for (const svc of services) { + for (const v of svc.requiredEnvVars || []) { + varServiceMap[v] = svc.name; + } + } + + const grouped = new Map(); + for (const v of envVars) { + const svc = varServiceMap[v.name] || v.service || "Other"; + if (!grouped.has(svc)) grouped.set(svc, []); + grouped.get(svc).push(v); + } + + for (const [service, vars] of grouped) { + lines.push(`# ${service}`); + for (const v of vars) { + const value = userValues[v.name] || v.value || ""; + lines.push(`${v.name}=${value}`); + } + lines.push(""); + } + + return lines.join("\n"); +} + +const server = http.createServer(async (req, res) => { + const parsed = url.parse(req.url, true); + const pathname = parsed.pathname; + + if (req.method === "OPTIONS") { + res.writeHead(204, { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", + }); + res.end(); + return; + } + + if (pathname === "/" && req.method === "GET") { + serveFile(res, path.join(__dirname, "app.html"), "text/html"); + return; + } + + if (pathname === "/api/scan" && req.method === "POST") { + const body = await parseBody(req); + const targetDir = body.path; + + if (!targetDir) { + return json(res, { error: "Missing 'path' field" }, 400); + } + + const resolved = path.resolve(targetDir); + if (!fs.existsSync(resolved)) { + return json(res, { error: `Directory not found: ${resolved}` }, 404); + } + + try { + const report = await scanProject(resolved); + return json(res, report); + } catch (err) { + return json(res, { error: err.message }, 500); + } + } + + if (pathname === "/api/generate" && req.method === "POST") { + const body = await parseBody(req); + const { envVars, services, userValues } = body; + + if (!envVars) { + return json(res, { error: "Missing scan data" }, 400); + } + + const content = generateEnvFile(envVars, services || [], userValues || {}); + return json(res, { content, filename: ".env.local" }); + } + + if (pathname === "/api/save" && req.method === "POST") { + const body = await parseBody(req); + const { targetDir, envVars, services, userValues } = body; + + if (!targetDir || !envVars) { + return json(res, { error: "Missing required fields" }, 400); + } + + const resolved = path.resolve(targetDir); + const envPath = path.join(resolved, ".env.local"); + const content = generateEnvFile(envVars, services || [], userValues || {}); + + try { + fs.writeFileSync(envPath, content, "utf-8"); + return json(res, { success: true, path: envPath }); + } catch (err) { + return json(res, { error: err.message }, 500); + } + } + + res.writeHead(404); + res.end("Not found"); +}); + +server.listen(PORT, () => { + console.log(""); + console.log("โ•".repeat(50)); + console.log(" ENV AGENT FINDER โ€” Web App"); + console.log("โ•".repeat(50)); + console.log(""); + console.log(` ๐ŸŒ Open in browser: http://localhost:${PORT}`); + console.log(""); + console.log(" Press Ctrl+C to stop"); + console.log("โ•".repeat(50)); + console.log(""); +}); From af9444c5c6016a952b916760a786d03b4d600620 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 2 Mar 2026 17:49:51 +0000 Subject: [PATCH 7/8] feat(env-agent-finder): add Vercel-deployable static web app - public/index.html: client-side scanner with file upload + GitHub URL modes - vercel.json: static site deploy config - Works entirely in browser, no server needed Co-authored-by: DealPatrol --- env-agent-finder/public/index.html | 690 +++++++++++++++++++++++++++++ env-agent-finder/vercel.json | 8 + 2 files changed, 698 insertions(+) create mode 100644 env-agent-finder/public/index.html create mode 100644 env-agent-finder/vercel.json diff --git a/env-agent-finder/public/index.html b/env-agent-finder/public/index.html new file mode 100644 index 0000000..134650d --- /dev/null +++ b/env-agent-finder/public/index.html @@ -0,0 +1,690 @@ + + + + + +env-agent-finder โ€” Discover env vars for any project + + + + + +
+
+

env-agent-finder

+

Drop your project files or paste a GitHub URL to discover every env variable, API route, and service dependency.

+
+ +
+ + +
+ + +
+
+
๐Ÿ“‚
+
Drop your project files here
+
or click to browse
+
package.json, .env, .env.local, .env.example โ€” or drag an entire folder
+
+ +
+
+ +
+
+ + +
+
+ + +
+
Fetches package.json and .env.example from the repo's default branch via the GitHub API.
+
+ +
+
+
Analyzing project...
+
+ +
+
+

๐Ÿ“ฆ Project Info

+
+
+
+

๐Ÿ”‘ Environment Variables 0

+
+
+
+

๐Ÿ”Œ Detected Services 0

+
+
+
+

๐ŸŒ API Routes 0

+
+
+
+ + + +
+
+
+ +
+ + + + diff --git a/env-agent-finder/vercel.json b/env-agent-finder/vercel.json new file mode 100644 index 0000000..245b0d1 --- /dev/null +++ b/env-agent-finder/vercel.json @@ -0,0 +1,8 @@ +{ + "buildCommand": "", + "outputDirectory": "public", + "framework": null, + "rewrites": [ + { "source": "/(.*)", "destination": "/index.html" } + ] +} From df6885f465d7a6c9d4145839fb762848022e38eb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 2 Mar 2026 18:36:33 +0000 Subject: [PATCH 8/8] feat(env-agent-finder): add browser grab mode + license system - --grab mode: opens real browser for each service, navigates to API keys page, extracts values or guides user through getting them - Grabbers for: Stripe, OpenAI, Anthropic, Supabase, Vercel, Clerk - License key system: --generate-key, --activate, --deactivate, --license - Grab mode is Pro-only (requires license), scan/setup remain free - Puppeteer as optional dependency (only needed for --grab) Co-authored-by: DealPatrol --- env-agent-finder/package.json | 14 +- env-agent-finder/src/cli.js | 229 ++++++++++++++++++--- env-agent-finder/src/grabbers/anthropic.js | 35 ++++ env-agent-finder/src/grabbers/browser.js | 105 ++++++++++ env-agent-finder/src/grabbers/clerk.js | 64 ++++++ env-agent-finder/src/grabbers/index.js | 32 +++ env-agent-finder/src/grabbers/openai.js | 44 ++++ env-agent-finder/src/grabbers/stripe.js | 68 ++++++ env-agent-finder/src/grabbers/supabase.js | 89 ++++++++ env-agent-finder/src/grabbers/vercel.js | 35 ++++ env-agent-finder/src/license.js | 75 +++++++ 11 files changed, 757 insertions(+), 33 deletions(-) create mode 100644 env-agent-finder/src/grabbers/anthropic.js create mode 100644 env-agent-finder/src/grabbers/browser.js create mode 100644 env-agent-finder/src/grabbers/clerk.js create mode 100644 env-agent-finder/src/grabbers/index.js create mode 100644 env-agent-finder/src/grabbers/openai.js create mode 100644 env-agent-finder/src/grabbers/stripe.js create mode 100644 env-agent-finder/src/grabbers/supabase.js create mode 100644 env-agent-finder/src/grabbers/vercel.js create mode 100644 env-agent-finder/src/license.js diff --git a/env-agent-finder/package.json b/env-agent-finder/package.json index 205928e..fc9cb1e 100644 --- a/env-agent-finder/package.json +++ b/env-agent-finder/package.json @@ -1,7 +1,7 @@ { "name": "env-agent-finder", - "version": "1.0.0", - "description": "CLI tool that scans any codebase to discover required environment variables, API endpoints, and service dependencies.", + "version": "2.0.0", + "description": "CLI + Web tool that scans projects, discovers env vars, auto-generates secrets, and grabs API keys from service dashboards via browser automation.", "main": "src/index.js", "bin": { "env-agent-finder": "src/cli.js" @@ -10,8 +10,14 @@ "start": "node src/server.js", "app": "node src/server.js", "scan": "node src/cli.js", + "grab": "node src/cli.js --grab", + "setup": "node src/cli.js --setup", + "generate-key": "node src/cli.js --generate-key", "test": "node src/cli.js --target ." }, + "optionalDependencies": { + "puppeteer": "^24.0.0" + }, "keywords": [ "env", "environment", @@ -19,7 +25,9 @@ "scanner", "agent", "devops", - "cli" + "cli", + "browser-automation", + "api-keys" ], "license": "MIT", "engines": { diff --git a/env-agent-finder/src/cli.js b/env-agent-finder/src/cli.js index 984f505..911989d 100755 --- a/env-agent-finder/src/cli.js +++ b/env-agent-finder/src/cli.js @@ -1,9 +1,11 @@ #!/usr/bin/env node const path = require("path"); +const fs = require("fs"); const { scanProject } = require("./index"); const { renderReport } = require("./reporter"); const { provisionEnv } = require("./provisioner"); +const { isLicensed, activateLicense, deactivateLicense, getLicenseInfo, generateLicenseKey } = require("./license"); const args = process.argv.slice(2); @@ -14,54 +16,65 @@ let showValues = false; let unmask = false; let setupMode = false; let autoOnly = false; +let grabMode = false; +let licenseCmd = null; +let licenseKey = null; for (let i = 0; i < args.length; i++) { if (args[i] === "--target" && args[i + 1]) { - targetDir = path.resolve(args[i + 1]); - i++; + targetDir = path.resolve(args[i + 1]); i++; } else if (args[i] === "--format" && args[i + 1]) { - outputFormat = args[i + 1]; - i++; + outputFormat = args[i + 1]; i++; } else if (args[i] === "--output" && args[i + 1]) { - outputFile = path.resolve(args[i + 1]); - i++; + outputFile = path.resolve(args[i + 1]); i++; } else if (args[i] === "--show-values" || args[i] === "-v") { showValues = true; } else if (args[i] === "--unmask") { - showValues = true; - unmask = true; + showValues = true; unmask = true; } else if (args[i] === "--setup" || args[i] === "-s") { setupMode = true; } else if (args[i] === "--auto") { - setupMode = true; - autoOnly = true; + setupMode = true; autoOnly = true; + } else if (args[i] === "--grab" || args[i] === "-g") { + grabMode = true; + } else if (args[i] === "--activate" && args[i + 1]) { + licenseCmd = "activate"; licenseKey = args[i + 1]; i++; + } else if (args[i] === "--deactivate") { + licenseCmd = "deactivate"; + } else if (args[i] === "--license") { + licenseCmd = "info"; + } else if (args[i] === "--generate-key") { + licenseCmd = "generate"; } else if (args[i] === "--help" || args[i] === "-h") { console.log(` -env-agent-finder โ€” Scan a codebase for env vars, APIs, and service dependencies. +env-agent-finder โ€” Scan, setup, and grab env vars for any project. -Usage: - env-agent-finder [options] - -SCAN MODE (default): +SCAN MODE (default โ€” free): --target Directory to scan (default: current directory) - --format Output format: terminal, json, markdown (default: terminal) - --output Write output to file instead of stdout - --show-values, -v Show env variable values (masked by default) - --unmask Show full unmasked values (implies --show-values) + --format Output: terminal, json, markdown + --output Write to file + --show-values, -v Show values (masked) + --unmask Show full values + +SETUP MODE (free): + --setup, -s Interactive setup + auto-generate secrets + --auto Non-interactive auto-generate only -SETUP MODE (writes .env.local): - --setup, -s Interactive setup: auto-generates what it can, prompts for the rest - --auto Non-interactive: only auto-generate secrets and DB URLs, skip prompts +GRAB MODE (Pro โ€” requires license): + --grab, -g Open browser, log into services, grab API keys automatically + Scans project โ†’ identifies services โ†’ opens each dashboard โ†’ + extracts or guides you through getting each API key -OTHER: - -h, --help Show this help message +LICENSE: + --activate Activate a license key + --deactivate Remove license + --license Show license info Examples: - env-agent-finder --target ./my-project # Scan only - env-agent-finder --target ./my-project --show-values # Scan with values - env-agent-finder --target ./my-project --setup # Interactive setup - env-agent-finder --target ./my-project --auto # Auto-generate only - env-agent-finder --target ./my-project --format markdown -v # Markdown with values + env-agent-finder --target ./my-project # Free scan + env-agent-finder --target ./my-project --setup # Free interactive setup + env-agent-finder --target ./my-project --grab # Pro: browser grab + env-agent-finder --activate EAF-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX `); process.exit(0); } else if (!args[i].startsWith("--")) { @@ -69,7 +82,164 @@ Examples: } } +async function handleLicense() { + if (licenseCmd === "activate") { + const result = activateLicense(licenseKey); + if (result.success) { + console.log("\n โœ… License activated! You now have access to --grab mode.\n"); + } else { + console.log(`\n โŒ ${result.error}\n`); + } + process.exit(0); + } + + if (licenseCmd === "deactivate") { + deactivateLicense(); + console.log("\n โœ… License deactivated.\n"); + process.exit(0); + } + + if (licenseCmd === "info") { + const info = getLicenseInfo(); + if (info) { + console.log(`\n ๐Ÿ”‘ License: ${info.key}`); + console.log(` ๐Ÿ“… Activated: ${info.activated}\n`); + } else { + console.log("\n No license activated. Use --activate to activate.\n"); + } + process.exit(0); + } + + if (licenseCmd === "generate") { + const key = generateLicenseKey(); + console.log(`\n ๐Ÿ”‘ Generated license key: ${key}\n`); + console.log(" Give this key to your customer. They activate it with:"); + console.log(` env-agent-finder --activate ${key}\n`); + process.exit(0); + } +} + +async function runGrab() { + if (!isLicensed()) { + console.log(""); + console.log(" โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•"); + console.log(" โšก GRAB MODE requires a Pro license"); + console.log(" โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•"); + console.log(""); + console.log(" Grab mode opens a real browser, logs into each service,"); + console.log(" and extracts API keys directly from the dashboards."); + console.log(""); + console.log(" ๐Ÿ›’ Get a license at: https://your-store-url.com"); + console.log(""); + console.log(" Already have a key? Activate it:"); + console.log(" env-agent-finder --activate EAF-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX"); + console.log(""); + process.exit(1); + } + + const { findGrabbersForServices } = require("./grabbers"); + const { closeBrowser } = require("./grabbers/browser"); + + console.log(`\n๐Ÿ” Scanning: ${targetDir}\n`); + const report = await scanProject(targetDir); + + const missingVars = report.envVars.filter((v) => !v.hasValue && v.required !== false); + const neededGrabbers = findGrabbersForServices(report.services, missingVars); + + if (neededGrabbers.length === 0) { + console.log(" โœ… No missing env vars that require browser automation."); + console.log(" All required variables are already set or can be auto-generated."); + console.log(" Run --setup to auto-fill the rest.\n"); + return; + } + + console.log("โ•".repeat(60)); + console.log(" ENV AGENT FINDER โ€” Grab Mode (Pro)"); + console.log("โ•".repeat(60)); + console.log(""); + console.log(` Found ${missingVars.length} missing variable(s) across ${neededGrabbers.length} service(s):`); + for (const { grabber, relevantVars } of neededGrabbers) { + console.log(` โ€ข ${grabber.name}: ${relevantVars.join(", ")}`); + } + console.log(""); + console.log(" A browser will open for each service. Log in, and the tool"); + console.log(" will navigate to the right page and help you grab the keys."); + console.log(""); + + const allGrabbed = {}; + + for (const { grabber } of neededGrabbers) { + console.log(`\n${"โ”€".repeat(50)}`); + console.log(` ๐Ÿ”Œ ${grabber.name.toUpperCase()}`); + console.log(`${"โ”€".repeat(50)}`); + + try { + const grabbed = await grabber.grab(); + Object.assign(allGrabbed, grabbed); + } catch (err) { + console.log(` โŒ Error with ${grabber.name}: ${err.message}`); + } + } + + await closeBrowser(); + + // Merge grabbed values with existing + const existingEnv = {}; + for (const v of report.envVars) { + if (v.value) existingEnv[v.name] = v.value; + } + + const finalValues = { ...existingEnv, ...allGrabbed }; + + // Run the provisioner with grabbed values pre-filled + console.log(""); + console.log("โ•".repeat(60)); + console.log(" WRITING .env.local"); + console.log("โ•".repeat(60)); + + // Inject grabbed values into the report + for (const v of report.envVars) { + if (allGrabbed[v.name]) { + v.value = allGrabbed[v.name]; + v.hasValue = true; + } + } + + await provisionEnv(targetDir, report, { interactive: false, autoOnly: true }); + + // Overwrite with the grabbed values + const envPath = path.join(targetDir, ".env.local"); + if (fs.existsSync(envPath)) { + let content = fs.readFileSync(envPath, "utf-8"); + for (const [key, val] of Object.entries(allGrabbed)) { + const regex = new RegExp(`^${key}=.*$`, "m"); + if (regex.test(content)) { + content = content.replace(regex, `${key}=${val}`); + } else { + content += `\n${key}=${val}`; + } + } + fs.writeFileSync(envPath, content, "utf-8"); + } + + const grabbedCount = Object.keys(allGrabbed).length; + console.log(""); + console.log(` ๐ŸŽฏ Grabbed ${grabbedCount} value(s) from browser sessions.`); + console.log(` ๐Ÿ“„ Saved to: ${envPath}`); + console.log(""); +} + async function main() { + if (licenseCmd) { + await handleLicense(); + return; + } + + if (grabMode) { + await runGrab(); + return; + } + console.log(`\n๐Ÿ” Scanning: ${targetDir}\n`); try { @@ -81,7 +251,6 @@ async function main() { const output = renderReport(report, outputFormat, { showValues, unmask }); if (outputFile) { - const fs = require("fs"); fs.writeFileSync(outputFile, output, "utf-8"); console.log(`\nโœ… Report written to: ${outputFile}`); } else { diff --git a/env-agent-finder/src/grabbers/anthropic.js b/env-agent-finder/src/grabbers/anthropic.js new file mode 100644 index 0000000..8e89bb0 --- /dev/null +++ b/env-agent-finder/src/grabbers/anthropic.js @@ -0,0 +1,35 @@ +const { openPage, waitForUserLogin, askUser } = require("./browser"); + +module.exports = { + name: "Anthropic", + vars: ["ANTHROPIC_API_KEY"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening Anthropic console..."); + const page = await openPage("https://console.anthropic.com/login"); + + await waitForUserLogin(page, null, "Anthropic"); + + console.log(" ๐Ÿ“ Navigating to API keys..."); + await page.goto("https://console.anthropic.com/settings/keys", { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + console.log(""); + console.log(" ๐Ÿ’ก In the browser window:"); + console.log(" 1. Click 'Create Key'"); + console.log(" 2. Copy the key that appears"); + console.log(" 3. Paste it below"); + console.log(""); + + const key = await askUser(" Paste your ANTHROPIC_API_KEY (or Enter to skip): "); + if (key) { + results.ANTHROPIC_API_KEY = key; + console.log(" โœ… ANTHROPIC_API_KEY saved"); + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/grabbers/browser.js b/env-agent-finder/src/grabbers/browser.js new file mode 100644 index 0000000..ae16fe6 --- /dev/null +++ b/env-agent-finder/src/grabbers/browser.js @@ -0,0 +1,105 @@ +const puppeteer = require("puppeteer"); +const readline = require("readline"); + +let browser = null; + +async function launchBrowser() { + if (browser) return browser; + + browser = await puppeteer.launch({ + headless: false, + defaultViewport: { width: 1280, height: 800 }, + args: [ + "--no-sandbox", + "--disable-setuid-sandbox", + "--window-size=1280,800", + ], + }); + + browser.on("disconnected", () => { browser = null; }); + return browser; +} + +async function closeBrowser() { + if (browser) { + await browser.close(); + browser = null; + } +} + +async function openPage(url) { + const b = await launchBrowser(); + const page = await b.newPage(); + await page.goto(url, { waitUntil: "networkidle2", timeout: 30000 }); + return page; +} + +async function waitForUserLogin(page, checkSelector, serviceName) { + const rl = readline.createInterface({ input: process.stdin, output: process.stderr }); + + console.log(""); + console.log(` ๐Ÿ” ${serviceName}: Please log in using the browser window.`); + console.log(` Once you're logged in, press ENTER here to continue...`); + console.log(""); + + await new Promise((resolve) => { + rl.question(" Press ENTER when logged in > ", () => { + rl.close(); + resolve(); + }); + }); + + if (checkSelector) { + try { + await page.waitForSelector(checkSelector, { timeout: 5000 }); + } catch { + // User said they're logged in, trust them + } + } +} + +async function extractText(page, selector, timeout = 5000) { + try { + await page.waitForSelector(selector, { timeout }); + return await page.$eval(selector, (el) => el.textContent.trim()); + } catch { + return null; + } +} + +async function extractInputValue(page, selector, timeout = 5000) { + try { + await page.waitForSelector(selector, { timeout }); + return await page.$eval(selector, (el) => el.value || el.textContent?.trim()); + } catch { + return null; + } +} + +async function clickAndWait(page, selector, waitMs = 2000) { + try { + await page.click(selector); + await page.waitForTimeout(waitMs); + } catch {} +} + +async function askUser(question) { + const rl = readline.createInterface({ input: process.stdin, output: process.stderr }); + return new Promise((resolve) => { + rl.question(question, (answer) => { + rl.close(); + resolve(answer.trim()); + }); + }); +} + +module.exports = { + launchBrowser, + closeBrowser, + openPage, + waitForUserLogin, + extractText, + extractInputValue, + clickAndWait, + askUser, +}; diff --git a/env-agent-finder/src/grabbers/clerk.js b/env-agent-finder/src/grabbers/clerk.js new file mode 100644 index 0000000..0382700 --- /dev/null +++ b/env-agent-finder/src/grabbers/clerk.js @@ -0,0 +1,64 @@ +const { openPage, waitForUserLogin, askUser } = require("./browser"); + +module.exports = { + name: "Clerk", + vars: ["CLERK_SECRET_KEY", "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening Clerk dashboard..."); + const page = await openPage("https://dashboard.clerk.com/sign-in"); + + await waitForUserLogin(page, null, "Clerk"); + + console.log(" ๐Ÿ“ Looking for API keys..."); + await page.waitForTimeout(2000); + + // Try to navigate to API keys + try { + const extracted = await page.evaluate(() => { + const text = document.body.innerText; + const result = {}; + + const pkMatch = text.match(/(pk_(?:test|live)_[A-Za-z0-9]+)/); + if (pkMatch) result.publishable = pkMatch[1]; + + const skMatch = text.match(/(sk_(?:test|live)_[A-Za-z0-9]+)/); + if (skMatch) result.secret = skMatch[1]; + + return result; + }); + + if (extracted.publishable) { + results.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY = extracted.publishable; + console.log(` โœ… NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY = ${extracted.publishable.substring(0, 20)}...`); + } + + if (extracted.secret) { + results.CLERK_SECRET_KEY = extracted.secret; + console.log(` โœ… CLERK_SECRET_KEY = ${extracted.secret.substring(0, 15)}...`); + } + } catch {} + + console.log(""); + console.log(" ๐Ÿ’ก In the Clerk dashboard:"); + console.log(" 1. Select your application"); + console.log(" 2. Go to 'API Keys' in the sidebar"); + console.log(" 3. Copy the keys shown"); + console.log(""); + + if (!results.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) { + const pk = await askUser(" Paste NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY (or Enter to skip): "); + if (pk) results.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY = pk; + } + + if (!results.CLERK_SECRET_KEY) { + const sk = await askUser(" Paste CLERK_SECRET_KEY (or Enter to skip): "); + if (sk) results.CLERK_SECRET_KEY = sk; + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/grabbers/index.js b/env-agent-finder/src/grabbers/index.js new file mode 100644 index 0000000..18a4fe3 --- /dev/null +++ b/env-agent-finder/src/grabbers/index.js @@ -0,0 +1,32 @@ +const stripe = require("./stripe"); +const openai = require("./openai"); +const supabase = require("./supabase"); +const vercel = require("./vercel"); +const clerk = require("./clerk"); +const anthropic = require("./anthropic"); + +const ALL_GRABBERS = [stripe, openai, anthropic, supabase, vercel, clerk]; + +function findGrabbersForServices(services, missingVars) { + const missingSet = new Set(missingVars.map((v) => v.name)); + const needed = []; + + for (const grabber of ALL_GRABBERS) { + const hasOverlap = grabber.vars.some((v) => missingSet.has(v)); + const serviceDetected = services.some( + (s) => s.name.toLowerCase().includes(grabber.name.toLowerCase()) || + grabber.name.toLowerCase().includes(s.name.toLowerCase()) + ); + + if (hasOverlap || serviceDetected) { + const relevantVars = grabber.vars.filter((v) => missingSet.has(v)); + if (relevantVars.length > 0) { + needed.push({ grabber, relevantVars }); + } + } + } + + return needed; +} + +module.exports = { ALL_GRABBERS, findGrabbersForServices }; diff --git a/env-agent-finder/src/grabbers/openai.js b/env-agent-finder/src/grabbers/openai.js new file mode 100644 index 0000000..bb0a646 --- /dev/null +++ b/env-agent-finder/src/grabbers/openai.js @@ -0,0 +1,44 @@ +const { openPage, waitForUserLogin, askUser } = require("./browser"); + +module.exports = { + name: "OpenAI", + vars: ["OPENAI_API_KEY"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening OpenAI dashboard..."); + const page = await openPage("https://platform.openai.com/login"); + + await waitForUserLogin(page, null, "OpenAI"); + + console.log(" ๐Ÿ“ Navigating to API keys..."); + await page.goto("https://platform.openai.com/api-keys", { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + console.log(" ๐Ÿ“ Looking for 'Create new secret key' button..."); + + try { + const createBtn = await page.$('button:has-text("Create new secret key"), [data-testid="create-api-key-button"]'); + if (createBtn) { + console.log(""); + console.log(" ๐Ÿ’ก To grab a key automatically:"); + console.log(" 1. Click 'Create new secret key' in the browser"); + console.log(" 2. Give it a name (e.g. 'env-agent-finder')"); + console.log(" 3. Copy the key that appears"); + console.log(" 4. Paste it below"); + console.log(""); + } + } catch {} + + // OpenAI never shows existing keys in full, so we always need the user to create/paste + const key = await askUser(" Paste your OPENAI_API_KEY (or Enter to skip): "); + if (key) { + results.OPENAI_API_KEY = key; + console.log(" โœ… OPENAI_API_KEY saved"); + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/grabbers/stripe.js b/env-agent-finder/src/grabbers/stripe.js new file mode 100644 index 0000000..c8b77be --- /dev/null +++ b/env-agent-finder/src/grabbers/stripe.js @@ -0,0 +1,68 @@ +const { openPage, waitForUserLogin, extractText, clickAndWait, askUser } = require("./browser"); + +module.exports = { + name: "Stripe", + vars: ["STRIPE_SECRET_KEY", "STRIPE_PUBLISHABLE_KEY", "STRIPE_WEBHOOK_SECRET"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening Stripe dashboard..."); + const page = await openPage("https://dashboard.stripe.com/login"); + + await waitForUserLogin(page, '[data-testid="developers-nav-item"]', "Stripe"); + + console.log(" ๐Ÿ“ Navigating to API keys..."); + await page.goto("https://dashboard.stripe.com/test/apikeys", { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + // Try to extract publishable key + try { + const pkKey = await page.evaluate(() => { + const rows = document.querySelectorAll('[class*="KeyRow"], tr, [data-testid]'); + for (const row of rows) { + const text = row.textContent || ""; + if (text.includes("pk_test_") || text.includes("pk_live_")) { + const match = text.match(/(pk_(?:test|live)_[A-Za-z0-9]+)/); + if (match) return match[1]; + } + } + const all = document.body.innerText; + const m = all.match(/(pk_(?:test|live)_[A-Za-z0-9]+)/); + return m ? m[1] : null; + }); + + if (pkKey) { + results.STRIPE_PUBLISHABLE_KEY = pkKey; + console.log(` โœ… STRIPE_PUBLISHABLE_KEY = ${pkKey.substring(0, 20)}...`); + } + } catch {} + + // Secret key needs to be revealed + try { + const skKey = await page.evaluate(() => { + const all = document.body.innerText; + const m = all.match(/(sk_(?:test|live)_[A-Za-z0-9]+)/); + return m ? m[1] : null; + }); + + if (skKey) { + results.STRIPE_SECRET_KEY = skKey; + console.log(` โœ… STRIPE_SECRET_KEY = ${skKey.substring(0, 15)}...`); + } else { + console.log(" โš ๏ธ Secret key is hidden. Click 'Reveal test key' in the browser, then:"); + const manual = await askUser(" Paste your STRIPE_SECRET_KEY (or Enter to skip): "); + if (manual) results.STRIPE_SECRET_KEY = manual; + } + } catch {} + + // Publishable key fallback + if (!results.STRIPE_PUBLISHABLE_KEY) { + const manual = await askUser(" Paste your STRIPE_PUBLISHABLE_KEY (or Enter to skip): "); + if (manual) results.STRIPE_PUBLISHABLE_KEY = manual; + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/grabbers/supabase.js b/env-agent-finder/src/grabbers/supabase.js new file mode 100644 index 0000000..227ffc0 --- /dev/null +++ b/env-agent-finder/src/grabbers/supabase.js @@ -0,0 +1,89 @@ +const { openPage, waitForUserLogin, askUser } = require("./browser"); + +module.exports = { + name: "Supabase", + vars: ["SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_URL", "SUPABASE_ANON_KEY", "NEXT_PUBLIC_SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening Supabase dashboard..."); + const page = await openPage("https://supabase.com/dashboard/sign-in"); + + await waitForUserLogin(page, null, "Supabase"); + + console.log(" ๐Ÿ“ Navigating to projects..."); + await page.goto("https://supabase.com/dashboard/projects", { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + // Ask which project to use + console.log(""); + console.log(" ๐Ÿ’ก Select your project in the browser window, then:"); + const projectRef = await askUser(" Paste your Supabase project URL (e.g. https://supabase.com/dashboard/project/abc123) or project ref: "); + + let ref = projectRef; + if (projectRef.includes("/project/")) { + ref = projectRef.split("/project/")[1].split(/[/?#]/)[0]; + } + + if (ref) { + console.log(` ๐Ÿ“ Opening project settings for: ${ref}`); + await page.goto(`https://supabase.com/dashboard/project/${ref}/settings/api`, { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + // Try to extract values from the API settings page + try { + const extracted = await page.evaluate(() => { + const text = document.body.innerText; + const result = {}; + + // Project URL + const urlMatch = text.match(/(https:\/\/[a-z0-9]+\.supabase\.co)/); + if (urlMatch) result.url = urlMatch[1]; + + // Anon key (starts with eyJ) + const anonMatch = text.match(/(eyJ[A-Za-z0-9_-]{100,})/); + if (anonMatch) result.anonKey = anonMatch[1]; + + return result; + }); + + if (extracted.url) { + results.SUPABASE_URL = extracted.url; + results.NEXT_PUBLIC_SUPABASE_URL = extracted.url; + console.log(` โœ… SUPABASE_URL = ${extracted.url}`); + } + + if (extracted.anonKey) { + results.SUPABASE_ANON_KEY = extracted.anonKey; + results.NEXT_PUBLIC_SUPABASE_ANON_KEY = extracted.anonKey; + console.log(` โœ… SUPABASE_ANON_KEY = ${extracted.anonKey.substring(0, 30)}...`); + } + } catch {} + + // Service role key is usually hidden โ€” ask user to reveal and paste + if (!results.SUPABASE_SERVICE_ROLE_KEY) { + console.log(""); + console.log(" ๐Ÿ’ก For the service_role key, click 'Reveal' next to it in the browser, then:"); + const srKey = await askUser(" Paste your SUPABASE_SERVICE_ROLE_KEY (or Enter to skip): "); + if (srKey) { + results.SUPABASE_SERVICE_ROLE_KEY = srKey; + console.log(" โœ… SUPABASE_SERVICE_ROLE_KEY saved"); + } + } + } + + // Fallbacks + if (!results.SUPABASE_URL) { + const url = await askUser(" Paste your SUPABASE_URL (or Enter to skip): "); + if (url) { results.SUPABASE_URL = url; results.NEXT_PUBLIC_SUPABASE_URL = url; } + } + if (!results.SUPABASE_ANON_KEY) { + const key = await askUser(" Paste your SUPABASE_ANON_KEY (or Enter to skip): "); + if (key) { results.SUPABASE_ANON_KEY = key; results.NEXT_PUBLIC_SUPABASE_ANON_KEY = key; } + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/grabbers/vercel.js b/env-agent-finder/src/grabbers/vercel.js new file mode 100644 index 0000000..15770f6 --- /dev/null +++ b/env-agent-finder/src/grabbers/vercel.js @@ -0,0 +1,35 @@ +const { openPage, waitForUserLogin, askUser } = require("./browser"); + +module.exports = { + name: "Vercel", + vars: ["BLOB_READ_WRITE_TOKEN"], + + async grab() { + const results = {}; + + console.log(" ๐Ÿ“ Opening Vercel dashboard..."); + const page = await openPage("https://vercel.com/login"); + + await waitForUserLogin(page, null, "Vercel"); + + console.log(" ๐Ÿ“ Navigating to Blob stores..."); + await page.goto("https://vercel.com/dashboard/stores", { waitUntil: "networkidle2", timeout: 30000 }); + await page.waitForTimeout(3000); + + console.log(""); + console.log(" ๐Ÿ’ก In the browser window:"); + console.log(" 1. Click on your Blob store (or create one)"); + console.log(" 2. Go to the store settings"); + console.log(" 3. Find and copy the BLOB_READ_WRITE_TOKEN"); + console.log(""); + + const token = await askUser(" Paste your BLOB_READ_WRITE_TOKEN (or Enter to skip): "); + if (token) { + results.BLOB_READ_WRITE_TOKEN = token; + console.log(" โœ… BLOB_READ_WRITE_TOKEN saved"); + } + + await page.close(); + return results; + }, +}; diff --git a/env-agent-finder/src/license.js b/env-agent-finder/src/license.js new file mode 100644 index 0000000..808a2e4 --- /dev/null +++ b/env-agent-finder/src/license.js @@ -0,0 +1,75 @@ +const crypto = require("crypto"); +const fs = require("fs"); +const path = require("path"); +const os = require("os"); + +const LICENSE_FILE = path.join(os.homedir(), ".env-agent-finder-license"); +const PUBLIC_PREFIX = "EAF"; + +function generateLicenseKey() { + const seg = () => crypto.randomBytes(4).toString("hex").toUpperCase(); + return `${PUBLIC_PREFIX}-${seg()}-${seg()}-${seg()}-${seg()}`; +} + +function validateKeyFormat(key) { + return /^EAF-[A-F0-9]{8}-[A-F0-9]{8}-[A-F0-9]{8}-[A-F0-9]{8}$/.test(key); +} + +function hashKey(key) { + return crypto.createHash("sha256").update(key + "env-agent-finder-salt-2026").digest("hex"); +} + +function saveLicense(key) { + fs.writeFileSync(LICENSE_FILE, JSON.stringify({ key, hash: hashKey(key), activated: new Date().toISOString() }), "utf-8"); +} + +function loadLicense() { + try { + if (fs.existsSync(LICENSE_FILE)) { + const data = JSON.parse(fs.readFileSync(LICENSE_FILE, "utf-8")); + if (data.key && validateKeyFormat(data.key) && data.hash === hashKey(data.key)) { + return data; + } + } + } catch {} + return null; +} + +function isLicensed() { + return !!loadLicense(); +} + +function activateLicense(key) { + if (!validateKeyFormat(key)) { + return { success: false, error: "Invalid license key format. Expected: EAF-XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX" }; + } + saveLicense(key); + return { success: true }; +} + +function deactivateLicense() { + try { + if (fs.existsSync(LICENSE_FILE)) fs.unlinkSync(LICENSE_FILE); + return { success: true }; + } catch (err) { + return { success: false, error: err.message }; + } +} + +function getLicenseInfo() { + const lic = loadLicense(); + if (!lic) return null; + return { + key: lic.key.substring(0, 12) + "..." + lic.key.substring(lic.key.length - 8), + activated: lic.activated, + }; +} + +module.exports = { + generateLicenseKey, + validateKeyFormat, + isLicensed, + activateLicense, + deactivateLicense, + getLicenseInfo, +};