From 7da38c778c4e12828045cf3f75333fa84e241f5a Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:02:06 -0300 Subject: [PATCH 1/9] ci: add GitHub Actions workflows (CI + Release) --- .github/workflows/ci.yml | 78 +++++++++++++++++++++++++++++++ .github/workflows/release.yml | 88 +++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac4a56c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +# Esteira CI: lint, test e build em todo push e PR +name: CI + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +defaults: + run: + working-directory: . + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install + run: npm ci + + - name: Lint + run: npm run lint + + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install + run: npm ci + + - name: Test + run: npm run test + + build: + name: Build + runs-on: ubuntu-latest + needs: [lint, test] + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install + run: npm ci + + - name: Build + run: npm run build + env: + # Build sem env sensíveis; variáveis vazias para não quebrar + NEXT_PUBLIC_VERCEL_URL: "" + + - name: Upload build + uses: actions/upload-artifact@v4 + with: + name: build + path: build/ + retention-days: 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8b4b889 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +# Esteira Release: build + artefato + deploy (falha se config ausente) +name: Release + +on: + release: + types: [published] + push: + tags: + - "v*" + +jobs: + build-and-bundle: + name: Build & Bundle + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install + run: npm ci + + - name: Get version + id: version + run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + + - name: Build + run: npm run build + env: + NEXT_PUBLIC_VERCEL_URL: "" + + - name: Bundle artifact + run: node scripts/bundle-artifact.js --no-archive + + - name: Create archive + run: | + VERSION=${{ steps.version.outputs.version }} + FOLDER=$(ls -d dist/darshan-* 2>/dev/null | head -1) + if [ -z "$FOLDER" ]; then echo "Pasta do artefato não encontrada"; exit 1; fi + BASENAME=$(basename "$FOLDER") + tar czf "dist/darshan-${VERSION}.tar.gz" -C dist "$BASENAME" + echo "ARTIFACT_PATH=dist/darshan-${VERSION}.tar.gz" >> $GITHUB_ENV + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: release-artifact + path: dist/darshan-*.tar.gz + retention-days: 30 + + # Deploy falha propositalmente se secrets não configurados (Vercel, etc.) + deploy: + name: Deploy + runs-on: ubuntu-latest + needs: build-and-bundle + if: success() + steps: + - name: Check deploy config + id: check + run: | + if [ -z "${{ secrets.VERCEL_TOKEN }}" ] && [ -z "${{ secrets.VERCEL_ORG_ID }}" ]; then + echo "Deploy não configurado: defina VERCEL_TOKEN e VERCEL_ORG_ID (e VERCEL_PROJECT_ID) nos secrets do repositório para deploy automático." + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + + - name: Deploy (Vercel) + if: steps.check.outputs.skip != 'true' + uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: ./ + vercel-args: "--prod" + + - name: Deploy skipped (config ausente) + if: steps.check.outputs.skip == 'true' + run: | + echo "::error title=Deploy não configurado::Defina VERCEL_TOKEN, VERCEL_ORG_ID e VERCEL_PROJECT_ID nos secrets do repositório (Settings > Secrets) para habilitar deploy automático." + exit 1 From 01f7ae7bb8931982948c6da7a2f2ffcceadcba0b Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:04:41 -0300 Subject: [PATCH 2/9] chore: add package.json and package-lock.json for CI --- package-lock.json | 6173 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 48 + 2 files changed, 6221 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3766782 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6173 @@ +{ + "name": "darshan", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "darshan", + "version": "0.1.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.32.1", + "@google/generative-ai": "^0.21.0", + "@supabase/supabase-js": "^2.93.3", + "framer-motion": "^11.11.17", + "mhah-panchang": "^1.2.0", + "next": "15.5.11", + "openai": "^4.73.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "stripe": "^17.4.0", + "suncalc": "^1.9.0" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.9.0", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.1", + "@types/suncalc": "^1.9.2", + "autoprefixer": "^10.4.20", + "jest": "^29.7.0", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.15", + "typescript": "^5.6.3" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.32.1.tgz", + "integrity": "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==", + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@next/env": { + "version": "15.5.11", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.11.tgz", + "integrity": "sha512-g9s5SS9gC7GJCEOR3OV3zqs7C5VddqxP9X+/6BpMbdXRkqsWfFf2CJPBZNvNEtAkKTNuRgRXAgNxSAXzfLdaTg==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", + "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", + "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", + "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", + "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", + "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", + "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", + "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", + "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@supabase/auth-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.93.3.tgz", + "integrity": "sha512-JdnkHZPKexVGSNONtu89RHU4bxz3X9kxx+f5ZnR5osoCIX+vs/MckwWRPZEybAEvlJXt5xjomDb3IB876QCxWQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.93.3.tgz", + "integrity": "sha512-qWO0gHNDm/5jRjROv/nv9L6sYabCWS1kzorOLUv3kqCwRvEJLYZga93ppJPrZwOgoZfXmJzvpjY8fODA4HQfBw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.93.3.tgz", + "integrity": "sha512-+iJ96g94skO2e4clsRSmEXg22NUOjh9BziapsJSAvnB1grOBf/BA8vGtCHjNOA+Z6lvKXL1jwBqcL9+fS1W/Lg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.93.3.tgz", + "integrity": "sha512-gnYpcFzwy8IkezRP4CDbT5I8jOsiOjrWrqTY1B+7jIriXsnpifmlM6RRjLBm9oD7OwPG0/WksniGPdKW67sXOA==", + "license": "MIT", + "dependencies": { + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.93.3.tgz", + "integrity": "sha512-cw4qXiLrx3apglDM02Tx/w/stvFlrkKocC6vCvuFAz3JtVEl1zH8MUfDQDTH59kJAQVaVdbewrMWSoBob7REnA==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.93.3.tgz", + "integrity": "sha512-paUqEqdBI9ztr/4bbMoCgeJ6M8ZTm2fpfjSOlzarPuzYveKFM20ZfDZqUpi9CFfYagYj5Iv3m3ztUjaI9/tM1w==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.93.3", + "@supabase/functions-js": "2.93.3", + "@supabase/postgrest-js": "2.93.3", + "@supabase/realtime-js": "2.93.3", + "@supabase/storage-js": "2.93.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", + "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/suncalc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/suncalc/-/suncalc-1.9.2.tgz", + "integrity": "sha512-ATAGBHHfA1TlE2tjfidLyTcysjoT2JHHEAmWRULh73SU9UTn++j5fqHEW16X6Y/2Li87jEQXzgu4R/OOdlDqzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.282", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.282.tgz", + "integrity": "sha512-FCPkJtpst28UmFzd903iU7PdeVTfY0KAeJy+Lk0GLZRwgwYHn/irRcaCbQQOmr5Vytc/7rcavsYLvTM8RiHYhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mhah-panchang": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mhah-panchang/-/mhah-panchang-1.2.0.tgz", + "integrity": "sha512-Z9oxFmeZXb+OE8NsE3PDHdXZlEA2fJeMQXZBk9bt1cY+2crR+12hKPo/wF48Gj2usCdEEv4kq9WYu93ZFSa81w==", + "license": "MPL-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "15.5.11", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.11.tgz", + "integrity": "sha512-L2KPiKmqTDpRdeVDdPjhf43g2/VPe0NCNndq7OKDCgOLWtxe1kbr/zXGIZtYY7kZEAjRf7Bj/mwUFSr+tYC2Yg==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.11", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.7", + "@next/swc-darwin-x64": "15.5.7", + "@next/swc-linux-arm64-gnu": "15.5.7", + "@next/swc-linux-arm64-musl": "15.5.7", + "@next/swc-linux-x64-gnu": "15.5.7", + "@next/swc-linux-x64-musl": "15.5.7", + "@next/swc-win32-arm64-msvc": "15.5.7", + "@next/swc-win32-x64-msvc": "15.5.7", + "sharp": "^0.34.3" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-17.7.0.tgz", + "integrity": "sha512-aT2BU9KkizY9SATf14WhhYVv2uOapBWX0OFWF4xvcj1mPaNotlSc2CsxpS4DS46ZueSppmCF5BX1sNYBtwBvfw==", + "license": "MIT", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/suncalc": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz", + "integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5455d9c --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "darshan", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "prebuild": "node -e \"try { require('fs').rmSync('build', { recursive: true }) } catch (e) { }\"", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "npx jest", + "test:coverage": "npx jest --coverage", + "version:patch": "node scripts/bump-version.js patch", + "version:minor": "node scripts/bump-version.js minor", + "version:major": "node scripts/bump-version.js major", + "version:patch:tag": "node scripts/bump-version.js patch --tag", + "version:minor:tag": "node scripts/bump-version.js minor --tag", + "version:major:tag": "node scripts/bump-version.js major --tag", + "bundle": "npm run build", + "bundle:artifact": "npm run build && node scripts/bundle-artifact.js" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.32.1", + "@google/generative-ai": "^0.21.0", + "@supabase/supabase-js": "^2.93.3", + "framer-motion": "^11.11.17", + "mhah-panchang": "^1.2.0", + "next": "15.5.11", + "openai": "^4.73.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "mercadopago": "^2.0.0", + "stripe": "^17.4.0", + "suncalc": "^1.9.0" + }, + "devDependencies": { + "@types/jest": "^29.5.14", + "@types/node": "^22.9.0", + "@types/react": "^19.0.1", + "@types/react-dom": "^19.0.1", + "@types/suncalc": "^1.9.2", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "jest": "^29.7.0", + "tailwindcss": "^3.4.15", + "typescript": "^5.6.3" + } +} From fce7c9f8eaf574fa553446575f949cb020eda79d Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:05:34 -0300 Subject: [PATCH 3/9] chore: sync package-lock.json (mercadopago, uuid) --- package-lock.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/package-lock.json b/package-lock.json index 3766782..c437d5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@google/generative-ai": "^0.21.0", "@supabase/supabase-js": "^2.93.3", "framer-motion": "^11.11.17", + "mercadopago": "^2.0.0", "mhah-panchang": "^1.2.0", "next": "15.5.11", "openai": "^4.73.0", @@ -4369,6 +4370,15 @@ "node": ">= 0.4" } }, + "node_modules/mercadopago": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/mercadopago/-/mercadopago-2.12.0.tgz", + "integrity": "sha512-9S+ZB/Fltd4BV9/U79r7U/+LrYJP844kxxvtAlVbbeVmhOE9rZt0YhPy1GXO3Yf4XyQaHwZ/SCyL2kebAicaLw==", + "dependencies": { + "node-fetch": "^2.7.0", + "uuid": "^9.0.0" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5984,6 +5994,19 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", From 2154f1071dc0608f355d2c16fae2430d7b939ee5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:07:12 -0300 Subject: [PATCH 4/9] ci: add ESLint config so next lint runs non-interactively in CI --- .eslintrc.json | 3 + package-lock.json | 4726 +++++++++++++++++++++++++++++++++++++++------ package.json | 2 + 3 files changed, 4101 insertions(+), 630 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..957cd15 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals"] +} diff --git a/package-lock.json b/package-lock.json index c437d5f..d445aa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,8 @@ "@types/react-dom": "^19.0.1", "@types/suncalc": "^1.9.2", "autoprefixer": "^10.4.20", + "eslint": "^8.57.0", + "eslint-config-next": "15.5.11", "jest": "^29.7.0", "postcss": "^8.4.49", "tailwindcss": "^3.4.15", @@ -593,6 +595,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", @@ -603,6 +617,100 @@ "tslib": "^2.4.0" } }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@google/generative-ai": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", @@ -612,6 +720,44 @@ "node": ">=18.0.0" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", @@ -1493,12 +1639,65 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@next/env": { "version": "15.5.11", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.11.tgz", "integrity": "sha512-g9s5SS9gC7GJCEOR3OV3zqs7C5VddqxP9X+/6BpMbdXRkqsWfFf2CJPBZNvNEtAkKTNuRgRXAgNxSAXzfLdaTg==", "license": "MIT" }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.5.11", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.11.tgz", + "integrity": "sha512-tS/HYQOjIoX9ZNDQitba/baS8sTvo3ekY6Vgdx5lmhN4jov082bdApIChXr94qhMZHvEciz9DZglFFnhguQp/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@next/swc-darwin-arm64": { "version": "15.5.7", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", @@ -1665,6 +1864,30 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "dev": true, + "license": "MIT" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1781,6 +2004,17 @@ "tslib": "^2.8.0" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1874,6 +2108,13 @@ "pretty-format": "^29.0.0" } }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", @@ -1959,405 +2200,1715 @@ "dev": true, "license": "MIT" }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "dev": true, "license": "MIT", "dependencies": { - "event-target-shim": "^5.0.0" + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=6.5" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, "engines": { - "node": ">= 8.0.0" + "node": ">= 4" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "node_modules/@typescript-eslint/parser": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">= 8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "node_modules/@typescript-eslint/types": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", "dev": true, "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "funding": [ - { + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "funding": [ + { "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" }, "bin": { - "autoprefixer": "bin/autoprefixer" + "create-jest": "bin/create-jest.js" }, "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-jest": { + "node_modules/create-jest/node_modules/jest-config": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "slash": "^3.0.0" + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" + "@types/node": "*", + "ts-node": ">=9.0.0" }, - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">= 8" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } + "license": "MIT" }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/balanced-match": { + "node_modules/data-view-byte-length": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/inspect-js" } }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "ms": "^2.1.3" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } + "license": "MIT" }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { + "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -2366,552 +3917,805 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001766", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", - "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=6.0.0" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/electron-to-chromium": { + "version": "1.5.282", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.282.tgz", + "integrity": "sha512-FCPkJtpst28UmFzd903iU7PdeVTfY0KAeJy+Lk0GLZRwgwYHn/irRcaCbQQOmr5Vytc/7rcavsYLvTM8RiHYhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, "engines": { - "node": ">= 8.10.0" + "node": ">=12" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "is-arrayish": "^0.2.1" } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", + "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.19" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/es-iterator-helpers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "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" }, "engines": { - "node": ">=12" + "node": ">= 0.4" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": ">= 0.4" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/convert-source-map": { + "node_modules/escape-string-regexp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { - "create-jest": "bin/create-jest.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/create-jest/node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "node_modules/eslint-config-next": { + "version": "15.5.11", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.11.tgz", + "integrity": "sha512-RQNY69VUv0BzXkLEKDh/OPUzA+krFOnYRxO0JA3UsW429ovLa2nXx8kZuXCl18P27PyJBdS3qgJJkIhi9H8SuQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "@next/eslint-plugin-next": "15.5.11", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" }, "peerDependenciesMeta": { - "@types/node": { + "eslint-plugin-import": { "optional": true }, - "ts-node": { + "eslint-plugin-import-x": { "optional": true } } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "debug": "^3.2.7" }, "engines": { - "node": ">= 8" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "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-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "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" }, "engines": { "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "esutils": "^2.0.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "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.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "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.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, "license": "Apache-2.0", - "optional": true, + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "MIT" - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.282", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.282.tgz", - "integrity": "sha512-FCPkJtpst28UmFzd903iU7PdeVTfY0KAeJy+Lk0GLZRwgwYHn/irRcaCbQQOmr5Vytc/7rcavsYLvTM8RiHYhQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT" + "license": "Python-2.0" }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -2928,6 +4732,52 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -2987,6 +4837,13 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3024,6 +4881,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -3044,6 +4908,19 @@ "bser": "2.1.1" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3051,24 +4928,62 @@ "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=8" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "is-callable": "^1.2.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/form-data": { @@ -3178,6 +5093,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3258,6 +5214,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -3293,6 +5280,52 @@ "node": ">=10.13.0" } }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3312,6 +5345,26 @@ "dev": true, "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3322,6 +5375,35 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -3396,6 +5478,43 @@ "node": ">=20.0.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -3445,6 +5564,39 @@ "dev": true, "license": "ISC" }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3452,6 +5604,42 @@ "dev": true, "license": "MIT" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3459,20 +5647,95 @@ "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3491,6 +5754,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3511,6 +5790,26 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3524,6 +5823,32 @@ "node": ">=0.10.0" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3534,6 +5859,81 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -3547,6 +5947,110 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3625,6 +6129,24 @@ "node": ">=8" } }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -4252,6 +6774,13 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -4259,6 +6788,20 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4272,6 +6815,32 @@ "node": ">=6" } }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -4282,6 +6851,26 @@ "node": ">=6" } }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -4292,6 +6881,20 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -4325,6 +6928,26 @@ "node": ">=8" } }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4463,6 +7086,16 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/motion-dom": { "version": "11.18.1", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", @@ -4514,6 +7147,22 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4710,6 +7359,106 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4781,6 +7530,42 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4836,6 +7621,19 @@ "node": ">=6" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -4944,6 +7742,16 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -5107,6 +7915,16 @@ "dev": true, "license": "MIT" }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -5149,6 +7967,35 @@ "node": ">= 6" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -5253,6 +8100,50 @@ "node": ">=8.10.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5307,6 +8198,16 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", @@ -5328,6 +8229,23 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5352,6 +8270,61 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -5371,6 +8344,55 @@ "node": ">=10" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/sharp": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", @@ -5572,6 +8594,13 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -5585,6 +8614,20 @@ "node": ">=10" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5614,6 +8657,119 @@ "node": ">=8" } }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -5803,6 +8959,13 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -5900,6 +9063,19 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -5907,12 +9083,61 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -5936,6 +9161,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -5950,12 +9253,66 @@ "node": ">=14.17" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "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" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -5987,6 +9344,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6073,6 +9440,105 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 5455d9c..607b120 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "@types/suncalc": "^1.9.2", "autoprefixer": "^10.4.20", "postcss": "^8.4.49", + "eslint": "^8.57.0", + "eslint-config-next": "15.5.11", "jest": "^29.7.0", "tailwindcss": "^3.4.15", "typescript": "^5.6.3" From 1fe7b7d586e3b6f981dd037ba590fbda0d52cd54 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:08:19 -0300 Subject: [PATCH 5/9] ci: add __tests__ and jest.config.mjs so CI runs tests --- __tests__/lib/adminAuth.test.ts | 64 ++++ __tests__/lib/audit.test.ts | 43 +++ __tests__/lib/auth.test.ts | 49 +++ __tests__/lib/credits.test.ts | 82 +++++ __tests__/lib/darshan.test.ts | 11 + __tests__/lib/dateFormatBr.test.ts | 77 +++++ __tests__/lib/finance/costEstimator.test.ts | 101 ++++++ __tests__/lib/finance/creditsManager.test.ts | 326 +++++++++++++++++++ __tests__/lib/finance/exportCsv.test.ts | 91 ++++++ __tests__/lib/finance/platformFee.test.ts | 67 ++++ __tests__/lib/finance/usageLogger.test.ts | 155 +++++++++ __tests__/lib/logger.test.ts | 67 ++++ __tests__/lib/stripe.test.ts | 45 +++ __tests__/lib/supabase.test.ts | 46 +++ __tests__/lib/usageLimits.test.ts | 84 +++++ jest.config.mjs | 37 +++ 16 files changed, 1345 insertions(+) create mode 100644 __tests__/lib/adminAuth.test.ts create mode 100644 __tests__/lib/audit.test.ts create mode 100644 __tests__/lib/auth.test.ts create mode 100644 __tests__/lib/credits.test.ts create mode 100644 __tests__/lib/darshan.test.ts create mode 100644 __tests__/lib/dateFormatBr.test.ts create mode 100644 __tests__/lib/finance/costEstimator.test.ts create mode 100644 __tests__/lib/finance/creditsManager.test.ts create mode 100644 __tests__/lib/finance/exportCsv.test.ts create mode 100644 __tests__/lib/finance/platformFee.test.ts create mode 100644 __tests__/lib/finance/usageLogger.test.ts create mode 100644 __tests__/lib/logger.test.ts create mode 100644 __tests__/lib/stripe.test.ts create mode 100644 __tests__/lib/supabase.test.ts create mode 100644 __tests__/lib/usageLimits.test.ts create mode 100644 jest.config.mjs diff --git a/__tests__/lib/adminAuth.test.ts b/__tests__/lib/adminAuth.test.ts new file mode 100644 index 0000000..b6d18b0 --- /dev/null +++ b/__tests__/lib/adminAuth.test.ts @@ -0,0 +1,64 @@ +import { getSecretFromRequest, checkAdminAuth } from "@/lib/adminAuth"; + +const originalEnv = process.env; + +describe("lib/adminAuth", () => { + beforeEach(() => { + process.env = { ...originalEnv }; + }); + afterEach(() => { + process.env = originalEnv; + }); + + describe("getSecretFromRequest", () => { + it("lê do header x-config-key", () => { + const req = new Request("https://x.com", { + headers: { "x-config-key": "secret123" }, + }); + expect(getSecretFromRequest(req)).toBe("secret123"); + }); + it("lê do Authorization Bearer", () => { + const req = new Request("https://x.com", { + headers: { authorization: "Bearer token456" }, + }); + expect(getSecretFromRequest(req)).toBe("token456"); + }); + it("lê do query key", () => { + const req = new Request("https://x.com/admin?key=q789"); + expect(getSecretFromRequest(req)).toBe("q789"); + }); + it("retorna null quando nenhum presente", () => { + const req = new Request("https://x.com"); + expect(getSecretFromRequest(req)).toBeNull(); + }); + }); + + describe("checkAdminAuth", () => { + it("retorna 503 quando CONFIG_SECRET não está definido", () => { + delete process.env.CONFIG_SECRET; + const req = new Request("https://x.com"); + const result = checkAdminAuth(req); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.status).toBe(503); + } + }); + it("retorna 401 quando secret não confere", () => { + process.env.CONFIG_SECRET = "correct"; + const req = new Request("https://x.com", { + headers: { "x-config-key": "wrong" }, + }); + const result = checkAdminAuth(req); + expect(result.ok).toBe(false); + if (!result.ok) expect(result.status).toBe(401); + }); + it("retorna ok quando secret confere", () => { + process.env.CONFIG_SECRET = "correct"; + const req = new Request("https://x.com", { + headers: { "x-config-key": "correct" }, + }); + const result = checkAdminAuth(req); + expect(result.ok).toBe(true); + }); + }); +}); diff --git a/__tests__/lib/audit.test.ts b/__tests__/lib/audit.test.ts new file mode 100644 index 0000000..f813523 --- /dev/null +++ b/__tests__/lib/audit.test.ts @@ -0,0 +1,43 @@ +import { audit } from "@/lib/audit"; + +const fs = require("fs"); +jest.mock("fs", () => ({ + existsSync: jest.fn(() => false), + mkdirSync: jest.fn(), + appendFileSync: jest.fn(), +})); + +describe("lib/audit", () => { + beforeEach(() => { + jest.clearAllMocks(); + (fs.existsSync as jest.Mock).mockReturnValue(false); + }); + + it("grava linha no audit.log com event e subject", () => { + audit("login_email", "a@b.com"); + expect(fs.mkdirSync).toHaveBeenCalled(); + expect(fs.appendFileSync).toHaveBeenCalled(); + const [path, line] = (fs.appendFileSync as jest.Mock).mock.calls[0]; + expect(path).toContain("audit.log"); + expect(line).toContain("login_email"); + expect(line).toContain("a@b.com"); + }); + + it("inclui details quando passado", () => { + audit("credits_add", "u@x.com", { amount: 50, balanceAfter: 150 }); + const [, line] = (fs.appendFileSync as jest.Mock).mock.calls[0]; + expect(line).toContain("credits_add"); + expect(line).toContain("50"); + expect(line).toContain("150"); + }); + + it("não quebra quando appendFileSync lança", () => { + (fs.appendFileSync as jest.Mock).mockImplementationOnce(() => { + throw new Error("disk full"); + }); + const spy = jest.spyOn(console, "error").mockImplementation(); + expect(() => audit("logout", "a@b.com")).not.toThrow(); + expect(spy).toHaveBeenCalledWith("[audit] write failed:", expect.any(Error)); + spy.mockRestore(); + }); +}); diff --git a/__tests__/lib/auth.test.ts b/__tests__/lib/auth.test.ts new file mode 100644 index 0000000..3fd3c47 --- /dev/null +++ b/__tests__/lib/auth.test.ts @@ -0,0 +1,49 @@ +import { + getSessionFromCookie, + sessionCookieHeader, + clearSessionCookieHeader, + type Session, +} from "@/lib/auth"; + +describe("lib/auth", () => { + describe("sessionCookieHeader e getSessionFromCookie", () => { + it("round-trip: header gerado é lido de volta", () => { + const session: Session = { email: "a@b.com" }; + const header = sessionCookieHeader(session); + const parsed = getSessionFromCookie(header); + expect(parsed?.email).toBe("a@b.com"); + }); + + it("getSessionFromCookie retorna null para header null", () => { + expect(getSessionFromCookie(null)).toBeNull(); + }); + + it("getSessionFromCookie retorna null quando cookie não existe", () => { + expect(getSessionFromCookie("other=val")).toBeNull(); + }); + + it("sessionCookieHeader contém Path e HttpOnly", () => { + const h = sessionCookieHeader({ email: "x@y.com" }); + expect(h).toContain("Path=/"); + expect(h).toContain("HttpOnly"); + }); + + it("getSessionFromCookie retorna null para sessão com email vazio", () => { + const header = sessionCookieHeader({ email: "" }); + expect(getSessionFromCookie(header)).toBeNull(); + }); + + it("getSessionFromCookie retorna null para valor malformado no cookie", () => { + expect(getSessionFromCookie("darshan_session=not-valid-base64!!!")).toBeNull(); + expect(getSessionFromCookie("darshan_session=")).toBeNull(); + }); + }); + + describe("clearSessionCookieHeader", () => { + it("retorna header que limpa a sessão", () => { + const h = clearSessionCookieHeader(); + expect(h).toContain("darshan_session=;"); + expect(h).toContain("Max-Age=0"); + }); + }); +}); diff --git a/__tests__/lib/credits.test.ts b/__tests__/lib/credits.test.ts new file mode 100644 index 0000000..95fb86e --- /dev/null +++ b/__tests__/lib/credits.test.ts @@ -0,0 +1,82 @@ +import { + CREDITS_PER_AI_REQUEST, + CREDIT_PACKAGES, + formatPriceBRL, + getCreditsFromCookie, + creditsCookieHeader, + clearCreditsCookieHeader, + getCreditsForRevelation, +} from "@/lib/credits"; + +describe("lib/credits", () => { + describe("CREDITS_PER_AI_REQUEST", () => { + it("deve ser 1 (modo ritual)", () => { + expect(CREDITS_PER_AI_REQUEST).toBe(1); + }); + }); + + describe("CREDIT_PACKAGES", () => { + it("deve ter 5 pacotes Fibonacci com id, amount, priceCents, label", () => { + expect(CREDIT_PACKAGES).toHaveLength(5); + expect(CREDIT_PACKAGES[0]).toEqual({ id: "13", amount: 13, priceCents: 890, label: "13 créditos" }); + expect(CREDIT_PACKAGES[1].id).toBe("21"); + expect(CREDIT_PACKAGES[2].amount).toBe(34); + expect(CREDIT_PACKAGES[4].priceCents).toBe(5590); + }); + }); + + describe("formatPriceBRL", () => { + it("formata centavos em BRL", () => { + expect(formatPriceBRL(1990)).toMatch(/19,90/); + expect(formatPriceBRL(0)).toMatch(/0,00/); + }); + }); + + describe("getCreditsFromCookie", () => { + it("retorna 0 quando header é null", () => { + expect(getCreditsFromCookie(null)).toBe(0); + }); + it("retorna 0 quando cookie não existe", () => { + expect(getCreditsFromCookie("other=1")).toBe(0); + }); + it("retorna o valor do cookie darshan_credits", () => { + expect(getCreditsFromCookie("darshan_credits=100")).toBe(100); + expect(getCreditsFromCookie("darshan_credits=0")).toBe(0); + expect(getCreditsFromCookie("foo=1; darshan_credits=50; bar=2")).toBe(50); + }); + it("retorna 0 para valor inválido ou negativo", () => { + expect(getCreditsFromCookie("darshan_credits=abc")).toBe(0); + expect(getCreditsFromCookie("darshan_credits=-1")).toBe(0); + }); + it("aceita valor com decodeURIComponent", () => { + expect(getCreditsFromCookie("darshan_credits=" + encodeURIComponent("42"))).toBe(42); + }); + }); + + describe("creditsCookieHeader", () => { + it("retorna header com valor não negativo e inteiro", () => { + const h = creditsCookieHeader(100); + expect(h).toContain("darshan_credits=100"); + expect(h).toContain("Path=/"); + expect(creditsCookieHeader(0)).toContain("darshan_credits=0"); + expect(creditsCookieHeader(99.7)).toContain("darshan_credits=99"); + expect(creditsCookieHeader(-5)).toContain("darshan_credits=0"); + }); + }); + + describe("clearCreditsCookieHeader", () => { + it("retorna header que limpa o cookie", () => { + const h = clearCreditsCookieHeader(); + expect(h).toContain("darshan_credits=;"); + expect(h).toContain("Max-Age=0"); + }); + }); + + describe("getCreditsForRevelation", () => { + it("ritual = 1, long = 2, long_image = 3", () => { + expect(getCreditsForRevelation("ritual")).toBe(1); + expect(getCreditsForRevelation("long")).toBe(2); + expect(getCreditsForRevelation("long_image")).toBe(3); + }); + }); +}); diff --git a/__tests__/lib/darshan.test.ts b/__tests__/lib/darshan.test.ts new file mode 100644 index 0000000..8d68f58 --- /dev/null +++ b/__tests__/lib/darshan.test.ts @@ -0,0 +1,11 @@ +import { PHASE_NAMES } from "@/lib/darshan"; + +describe("lib/darshan", () => { + describe("PHASE_NAMES", () => { + it("tem nomes para fases 1 a 7", () => { + expect(PHASE_NAMES[1]).toBe("Luz — frase-oráculo"); + expect(PHASE_NAMES[2]).toContain("Jyotish"); + expect(PHASE_NAMES[7]).toContain("presença"); + }); + }); +}); diff --git a/__tests__/lib/dateFormatBr.test.ts b/__tests__/lib/dateFormatBr.test.ts new file mode 100644 index 0000000..1237cef --- /dev/null +++ b/__tests__/lib/dateFormatBr.test.ts @@ -0,0 +1,77 @@ +import { + toBrDate, + fromBrDate, + maskBrDate, + maskBrTime, + fromBrTime, +} from "@/lib/dateFormatBr"; + +describe("lib/dateFormatBr", () => { + describe("toBrDate", () => { + it("converte YYYY-MM-DD para DD/MM/AAAA", () => { + expect(toBrDate("2025-01-15")).toBe("15/01/2025"); + }); + it("retorna vazio para undefined ou formato inválido", () => { + expect(toBrDate(undefined)).toBe(""); + expect(toBrDate("")).toBe(""); + expect(toBrDate("15/01/2025")).toBe(""); + expect(toBrDate("2025-1-5")).toBe(""); + }); + }); + + describe("fromBrDate", () => { + it("converte DD/MM/AAAA para YYYY-MM-DD", () => { + expect(fromBrDate("15/01/2025")).toBe("2025-01-15"); + expect(fromBrDate("15012025")).toBe("2025-01-15"); + }); + it("retorna vazio para string com menos de 8 dígitos", () => { + expect(fromBrDate("150125")).toBe(""); + }); + it("retorna vazio para datas inválidas", () => { + expect(fromBrDate("00/01/2025")).toBe(""); + expect(fromBrDate("32/01/2025")).toBe(""); + expect(fromBrDate("15/00/2025")).toBe(""); + expect(fromBrDate("15/13/2025")).toBe(""); + }); + it("retorna vazio para ano fora do intervalo 1900-2100", () => { + expect(fromBrDate("15/01/1899")).toBe(""); + expect(fromBrDate("15/01/2101")).toBe(""); + }); + }); + + describe("maskBrDate", () => { + it("aplica máscara DD/MM/AAAA", () => { + expect(maskBrDate("1")).toBe("1"); + expect(maskBrDate("15")).toBe("15"); + expect(maskBrDate("151")).toBe("15/1"); + expect(maskBrDate("1501")).toBe("15/01"); + expect(maskBrDate("15012025")).toBe("15/01/2025"); + expect(maskBrDate("15/01/2025")).toBe("15/01/2025"); + }); + }); + + describe("maskBrTime", () => { + it("aplica máscara HH:mm", () => { + expect(maskBrTime("1")).toBe("1"); + expect(maskBrTime("12")).toBe("12"); + expect(maskBrTime("123")).toBe("12:3"); + expect(maskBrTime("1234")).toBe("12:34"); + }); + }); + + describe("fromBrTime", () => { + it("normaliza HH:mm", () => { + expect(fromBrTime("12:34")).toBe("12:34"); + expect(fromBrTime("1234")).toBe("12:34"); + expect(fromBrTime("123")).toBe("12:3"); + }); + it("limita hora e minuto", () => { + expect(fromBrTime("25:00")).toBe("23:00"); + expect(fromBrTime("12:99")).toBe("12:59"); + }); + it("retorna br quando menos de 3 dígitos", () => { + expect(fromBrTime("1")).toBe("1"); + expect(fromBrTime("12")).toBe("12"); + }); + }); +}); diff --git a/__tests__/lib/finance/costEstimator.test.ts b/__tests__/lib/finance/costEstimator.test.ts new file mode 100644 index 0000000..0087232 --- /dev/null +++ b/__tests__/lib/finance/costEstimator.test.ts @@ -0,0 +1,101 @@ +import { + estimateCost, + setUsdToBrl, + getUsdToBrl, + getRates, + refreshUsdToBrlCache, + type CostProvider, +} from "@/lib/finance/costEstimator"; + +describe("lib/finance/costEstimator", () => { + beforeEach(() => { + setUsdToBrl(5); + }); + + describe("getUsdToBrl e setUsdToBrl", () => { + it("valor padrão e set", () => { + setUsdToBrl(5.5); + expect(getUsdToBrl()).toBe(5.5); + }); + it("não aceita valor menor que 0.01", () => { + setUsdToBrl(5); + setUsdToBrl(0); + expect(getUsdToBrl()).toBeGreaterThanOrEqual(0.01); + }); + }); + + describe("estimateCost", () => { + it("calcula custo USD e BRL para openai", () => { + setUsdToBrl(5); + const r = estimateCost("openai", 1000, 500); + expect(r.costUsd).toBeGreaterThan(0); + expect(r.costBrl).toBe(r.costUsd * 5); + }); + it("calcula custo para gemini", () => { + const r = estimateCost("gemini", 1_000_000, 500_000); + expect(r.costUsd).toBeGreaterThan(0); + expect(r.costBrl).toBeGreaterThan(0); + }); + it("provider desconhecido usa rates openai", () => { + const r = estimateCost("openai" as CostProvider, 0, 0); + expect(r.costUsd).toBe(0); + expect(r.costBrl).toBe(0); + }); + }); + + describe("getRates", () => { + it("retorna cópia das taxas para openai, anthropic, gemini", () => { + const rates = getRates(); + expect(rates.openai.inputPer1M).toBe(0.15); + expect(rates.openai.outputPer1M).toBe(0.6); + expect(rates.gemini.inputPer1M).toBe(0.35); + expect(rates.anthropic.outputPer1M).toBe(15); + }); + }); + + describe("refreshUsdToBrlCache", () => { + it("retorna valor atual quando fetch falha", async () => { + setUsdToBrl(5.2); + const globalFetch = global.fetch; + global.fetch = jest.fn().mockResolvedValue({ ok: false }); + const rate = await refreshUsdToBrlCache(); + expect(rate).toBe(5.2); + global.fetch = globalFetch; + }); + it("atualiza usdToBrl quando API retorna dados válidos", async () => { + setUsdToBrl(5); + const globalFetch = global.fetch; + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ compra: 5.1, venda: 5.2 }), + }); + const rate = await refreshUsdToBrlCache(); + expect(rate).toBeCloseTo(5.15); + global.fetch = globalFetch; + }); + it("mantém valor atual quando fetch lança erro", async () => { + setUsdToBrl(5.5); + const globalFetch = global.fetch; + global.fetch = jest.fn().mockRejectedValue(new Error("network")); + const rate = await refreshUsdToBrlCache(); + expect(rate).toBe(5.5); + global.fetch = globalFetch; + }); + it("mantém valor atual quando JSON não tem compra/venda numéricos", async () => { + setUsdToBrl(5); + const globalFetch = global.fetch; + global.fetch = jest.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({}), + }); + const rate = await refreshUsdToBrlCache(); + expect(rate).toBe(5); + global.fetch = globalFetch; + }); + it("anthropic usa taxas corretas", () => { + const r = estimateCost("anthropic", 1000, 500); + expect(r.costUsd).toBeGreaterThan(0); + expect(r.costBrl).toBeCloseTo(r.costUsd * 5); + }); + }); +}); diff --git a/__tests__/lib/finance/creditsManager.test.ts b/__tests__/lib/finance/creditsManager.test.ts new file mode 100644 index 0000000..97301ee --- /dev/null +++ b/__tests__/lib/finance/creditsManager.test.ts @@ -0,0 +1,326 @@ +import { + getCreditsBalance, + debitCredits, + addCredits, + addCreditsForPurchase, + recordPayment, +} from "@/lib/finance/creditsManager"; + +const mockGetSupabase = jest.fn(); +const mockIsSupabaseConfigured = jest.fn(); + +jest.mock("@/lib/supabase", () => ({ + getSupabase: (...args: unknown[]) => mockGetSupabase(...args), + isSupabaseConfigured: () => mockIsSupabaseConfigured(), +})); + +function createMockSupabaseClient(overrides: { + selectUser?: { data: { id: string; credits_balance: number } | null }; + insertUser?: { data: { id: string; credits_balance?: number } | null }; + updateUser?: { error: Error | null }; + insertLedger?: { data: { id: string } | null }; + insertPayment?: { data: { id: string } | null }; +} = {}) { + const { + selectUser = { data: { id: "u1", credits_balance: 50 } }, + insertUser = { data: { id: "u1", credits_balance: 50 } }, + updateUser = { error: null }, + insertLedger = { data: { id: "ledger1" } }, + insertPayment = { data: { id: "pay1" } }, + } = overrides; + + const from = jest.fn((table: string) => { + if (table === "users") { + return { + select: jest.fn(() => ({ + eq: jest.fn(() => ({ + single: jest.fn().mockResolvedValue(selectUser), + })), + })), + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue(insertUser), + })), + })), + update: jest.fn(() => ({ + eq: jest.fn().mockResolvedValue(updateUser), + })), + }; + } + if (table === "credit_ledger") { + return { + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue(insertLedger), + })), + })), + }; + } + if (table === "payments") { + return { + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue(insertPayment), + })), + })), + }; + } + return {}; + }); + return { from }; +} + +describe("lib/finance/creditsManager (sem Supabase)", () => { + beforeEach(() => { + mockGetSupabase.mockReturnValue(null); + mockIsSupabaseConfigured.mockReturnValue(false); + }); + + describe("getCreditsBalance", () => { + it("retorna balanceFromCookie quando Supabase não configurado", async () => { + const balance = await getCreditsBalance("a@b.com", 100); + expect(balance).toBe(100); + }); + }); + + describe("debitCredits", () => { + it("retorna newBalance = current - amount quando sem Supabase", async () => { + const r = await debitCredits("a@b.com", 13, "darshan_call", { + currentBalanceFromCookie: 50, + }); + expect(r.newBalance).toBe(37); + }); + it("não fica negativo", async () => { + const r = await debitCredits("a@b.com", 100, "darshan_call", { + currentBalanceFromCookie: 20, + }); + expect(r.newBalance).toBe(0); + }); + }); + + describe("addCredits", () => { + it("retorna newBalance = current + amount quando sem Supabase", async () => { + const r = await addCredits("a@b.com", 50, "purchase", { + currentBalanceFromCookie: 10, + }); + expect(r.newBalance).toBe(60); + }); + }); + + describe("recordPayment", () => { + it("retorna null quando Supabase não configurado", async () => { + const id = await recordPayment( + "a@b.com", + "stripe", + 39.9, + 100, + "completed", + "cs_abc" + ); + expect(id).toBeNull(); + }); + }); + + describe("addCreditsForPurchase", () => { + it("soma créditos e retorna newBalance quando sem Supabase", async () => { + const r = await addCreditsForPurchase( + "a@b.com", + 100, + 39.9, + "stripe", + "cs_xyz", + 0 + ); + expect(r.newBalance).toBe(100); + }); + }); +}); + +describe("lib/finance/creditsManager (com Supabase)", () => { + beforeEach(() => { + mockIsSupabaseConfigured.mockReturnValue(true); + }); + + describe("getCreditsBalance", () => { + it("retorna credits_balance do user quando existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 80 } }, + }) + ); + const balance = await getCreditsBalance("a@b.com", 0); + expect(balance).toBe(80); + }); + it("insere user e retorna balanceFromCookie quando user não existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: { id: "u2", credits_balance: 100 } }, + }) + ); + const balance = await getCreditsBalance("a@b.com", 100); + expect(balance).toBe(100); + }); + }); + + describe("debitCredits", () => { + it("debita e retorna newBalance quando user existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 50 } }, + }) + ); + const r = await debitCredits("a@b.com", 13, "darshan_call", { + relatedUsageId: "usage-1", + }); + expect(r.newBalance).toBe(37); + expect(r.ledgerId).toBe("ledger1"); + }); + it("cria user e debita quando user não existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: { id: "u-new", credits_balance: 0 } }, + }) + ); + const r = await debitCredits("a@b.com", 10, "darshan_call", { + currentBalanceFromCookie: 20, + }); + expect(r.newBalance).toBe(0); + }); + it("retorna fallback quando insert user falha (inserted null)", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: null }, + }) + ); + const r = await debitCredits("a@b.com", 13, "darshan_call", { + currentBalanceFromCookie: 50, + }); + expect(r.newBalance).toBe(37); + }); + it("retorna fallback quando update user retorna erro", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 50 } }, + updateUser: { error: new Error("db error") }, + }) + ); + const r = await debitCredits("a@b.com", 13, "darshan_call", { + currentBalanceFromCookie: 50, + }); + expect(r.newBalance).toBe(37); + }); + }); + + describe("addCredits", () => { + it("adiciona créditos quando user existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 10 } }, + }) + ); + const r = await addCredits("a@b.com", 50, "purchase", {}); + expect(r.newBalance).toBe(60); + }); + it("cria user e adiciona quando user não existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: { id: "u-new", credits_balance: 100 } }, + }) + ); + const r = await addCredits("a@b.com", 100, "purchase", { + currentBalanceFromCookie: 0, + }); + expect(r.newBalance).toBe(200); + }); + it("retorna fallback quando insert user falha", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: null }, + }) + ); + const r = await addCredits("a@b.com", 50, "purchase", { + currentBalanceFromCookie: 10, + }); + expect(r.newBalance).toBe(60); + }); + }); + + describe("recordPayment", () => { + it("retorna id do payment quando user existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 0 } }, + insertPayment: { data: { id: "pay-123" } }, + }) + ); + const id = await recordPayment( + "a@b.com", + "stripe", + 39.9, + 100, + "completed", + "cs_abc" + ); + expect(id).toBe("pay-123"); + }); + it("cria user e retorna id quando user não existe", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: null }, + insertUser: { data: { id: "u-new" } }, + insertPayment: { data: { id: "pay-456" } }, + }) + ); + const id = await recordPayment( + "a@b.com", + "stripe", + 19.9, + 50, + "completed" + ); + expect(id).toBe("pay-456"); + }); + it("retorna null quando insert payment falha", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1" } }, + insertPayment: { data: null }, + }) + ); + const id = await recordPayment( + "a@b.com", + "stripe", + 39.9, + 100, + "completed" + ); + expect(id).toBeNull(); + }); + }); + + describe("addCreditsForPurchase", () => { + it("registra payment e adiciona créditos", async () => { + mockGetSupabase.mockReturnValue( + createMockSupabaseClient({ + selectUser: { data: { id: "u1", credits_balance: 0 } }, + insertPayment: { data: { id: "pay-1" } }, + insertUser: { data: { id: "u1", credits_balance: 100 } }, + }) + ); + const r = await addCreditsForPurchase( + "a@b.com", + 100, + 39.9, + "stripe", + "cs_xyz", + 0 + ); + expect(r.newBalance).toBe(100); + }); + }); +}); diff --git a/__tests__/lib/finance/exportCsv.test.ts b/__tests__/lib/finance/exportCsv.test.ts new file mode 100644 index 0000000..b8a74a4 --- /dev/null +++ b/__tests__/lib/finance/exportCsv.test.ts @@ -0,0 +1,91 @@ +import { usageToCsv, paymentsToCsv } from "@/lib/finance/exportCsv"; + +describe("lib/finance/exportCsv", () => { + describe("usageToCsv", () => { + it("retorna header e linhas", () => { + const rows = [ + { + user_id: "a@b.com", + provider: "gemini", + total_calls: 2, + total_tokens: 1000, + total_cost_brl: 1.5, + credits_spent: 26, + revenue_estimate: 10.4, + }, + ]; + const csv = usageToCsv(rows); + expect(csv).toContain("user_id,provider,total_calls"); + expect(csv).toContain("a@b.com"); + expect(csv).toContain("gemini"); + expect(csv).toContain("1.50"); + }); + it("retorna só header para array vazio", () => { + const csv = usageToCsv([]); + expect(csv).toBe( + "user_id,provider,total_calls,total_tokens,total_cost_brl,credits_spent,revenue_estimate" + ); + }); + it("escapa vírgulas e aspas no user_id", () => { + const csv = usageToCsv([ + { + user_id: 'a"b,c@d.com', + provider: "openai", + total_calls: 1, + total_tokens: 100, + total_cost_brl: 0.5, + credits_spent: 13, + revenue_estimate: 5.2, + }, + ]); + expect(csv).toContain('"a""b,c@d.com"'); + }); + }); + + describe("paymentsToCsv", () => { + it("retorna header e linhas", () => { + const rows = [ + { + user_id: "u@x.com", + amount_brl: 39.9, + credits_added: 100, + status: "completed", + created_at: "2025-01-15T12:00:00.000Z", + }, + ]; + const csv = paymentsToCsv(rows); + expect(csv).toContain("user_id,amount_brl,credits_added,status,created_at"); + expect(csv).toContain("u@x.com"); + expect(csv).toContain("39.90"); + expect(csv).toContain("completed"); + }); + it("retorna só header para array vazio", () => { + const csv = paymentsToCsv([]); + expect(csv).toBe("user_id,amount_brl,credits_added,status,created_at"); + }); + it("escapa status com vírgula", () => { + const csv = paymentsToCsv([ + { + user_id: "u@x.com", + amount_brl: 0, + credits_added: 0, + status: "pending, review", + created_at: "2025-01-01T00:00:00.000Z", + }, + ]); + expect(csv).toContain('"pending, review"'); + }); + it("escapa created_at com newline", () => { + const csv = paymentsToCsv([ + { + user_id: "u@x.com", + amount_brl: 10, + credits_added: 50, + status: "completed", + created_at: "2025-01-01\nT00:00:00Z", + }, + ]); + expect(csv).toContain('"'); + }); + }); +}); diff --git a/__tests__/lib/finance/platformFee.test.ts b/__tests__/lib/finance/platformFee.test.ts new file mode 100644 index 0000000..393560a --- /dev/null +++ b/__tests__/lib/finance/platformFee.test.ts @@ -0,0 +1,67 @@ +import { getPlatformFeePercent, getPlatformFeeDecimal } from "@/lib/finance/platformFee"; + +const ENV_KEY = "PLATFORM_FEE_PERCENT"; + +describe("lib/finance/platformFee", () => { + const originalEnv = process.env[ENV_KEY]; + + afterEach(() => { + if (originalEnv !== undefined) { + process.env[ENV_KEY] = originalEnv; + } else { + delete process.env[ENV_KEY]; + } + }); + + describe("getPlatformFeePercent", () => { + it("retorna 30 quando PLATFORM_FEE_PERCENT não está definido", () => { + delete process.env[ENV_KEY]; + expect(getPlatformFeePercent()).toBe(30); + }); + + it("retorna 30 quando PLATFORM_FEE_PERCENT está vazio", () => { + process.env[ENV_KEY] = ""; + expect(getPlatformFeePercent()).toBe(30); + }); + + it("retorna o valor numérico válido da env", () => { + process.env[ENV_KEY] = "25"; + expect(getPlatformFeePercent()).toBe(25); + process.env[ENV_KEY] = "0"; + expect(getPlatformFeePercent()).toBe(0); + process.env[ENV_KEY] = "100"; + expect(getPlatformFeePercent()).toBe(100); + }); + + it("arredonda e limita entre 0 e 100", () => { + process.env[ENV_KEY] = "35.7"; + expect(getPlatformFeePercent()).toBe(36); + process.env[ENV_KEY] = "-10"; + expect(getPlatformFeePercent()).toBe(0); + process.env[ENV_KEY] = "150"; + expect(getPlatformFeePercent()).toBe(100); + }); + + it("retorna 30 quando valor não é numérico", () => { + process.env[ENV_KEY] = "abc"; + expect(getPlatformFeePercent()).toBe(30); + }); + }); + + describe("getPlatformFeeDecimal", () => { + it("retorna 0.30 quando percentual é 30", () => { + process.env[ENV_KEY] = "30"; + expect(getPlatformFeeDecimal()).toBe(0.3); + }); + + it("retorna 0 quando percentual é 0", () => { + process.env[ENV_KEY] = "0"; + expect(getPlatformFeeDecimal()).toBe(0); + }); + + it("retorna 1 quando percentual é 100", () => { + process.env[ENV_KEY] = "100"; + expect(getPlatformFeeDecimal()).toBe(1); + }); + }); +}); diff --git a/__tests__/lib/finance/usageLogger.test.ts b/__tests__/lib/finance/usageLogger.test.ts new file mode 100644 index 0000000..5866558 --- /dev/null +++ b/__tests__/lib/finance/usageLogger.test.ts @@ -0,0 +1,155 @@ +import { logAiUsage } from "@/lib/finance/usageLogger"; + +const mockGetSupabase = jest.fn(); +const mockIsSupabaseConfigured = jest.fn(); + +jest.mock("@/lib/supabase", () => ({ + getSupabase: (...args: unknown[]) => mockGetSupabase(...args), + isSupabaseConfigured: () => mockIsSupabaseConfigured(), +})); + +const baseEntry = { + provider: "gemini" as const, + model: "gemini-2.5-flash", + inputTokens: 100, + outputTokens: 50, + totalTokens: 150, + costUsd: 0.001, + costBrl: 0.005, + creditsSpent: 13, + mode: "now" as const, + questionLength: 10, + responseLength: 100, + success: true, +}; + +describe("lib/finance/usageLogger (sem Supabase)", () => { + beforeEach(() => { + mockGetSupabase.mockReturnValue(null); + mockIsSupabaseConfigured.mockReturnValue(false); + }); + + it("retorna null quando Supabase não configurado", async () => { + const id = await logAiUsage(baseEntry, { userEmail: "a@b.com" }); + expect(id).toBeNull(); + }); +}); + +describe("lib/finance/usageLogger (com Supabase)", () => { + beforeEach(() => { + mockIsSupabaseConfigured.mockReturnValue(true); + }); + + it("retorna id do log quando user existe e insert ok", async () => { + mockGetSupabase.mockReturnValue({ + from: jest.fn((table: string) => { + if (table === "users") { + return { + select: jest.fn(() => ({ + eq: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ data: { id: "u1" } }), + })), + })), + }; + } + if (table === "ai_usage_log") { + return { + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ + data: { id: "log-123" }, + error: null, + }), + })), + })), + }; + } + return {}; + }), + }); + const id = await logAiUsage(baseEntry, { userEmail: "a@b.com" }); + expect(id).toBe("log-123"); + }); + + it("cria user e insere log quando user não existe", async () => { + mockGetSupabase.mockReturnValue({ + from: jest.fn((table: string) => { + if (table === "users") { + return { + select: jest.fn(() => ({ + eq: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ data: null }), + })), + })), + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ + data: { id: "u-new" }, + }), + })), + })), + }; + } + if (table === "ai_usage_log") { + return { + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ + data: { id: "log-456" }, + error: null, + }), + })), + })), + }; + } + return {}; + }), + }); + const id = await logAiUsage(baseEntry, { userEmail: "new@b.com" }); + expect(id).toBe("log-456"); + }); + + it("retorna null quando userTableId passado mas insert ai_usage_log falha", async () => { + mockGetSupabase.mockReturnValue({ + from: jest.fn((table: string) => { + if (table === "ai_usage_log") { + return { + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ + data: null, + error: new Error("db error"), + }), + })), + })), + }; + } + return {}; + }), + }); + const id = await logAiUsage(baseEntry, { + userEmail: "a@b.com", + userTableId: "u1", + }); + expect(id).toBeNull(); + }); + + it("retorna null quando userEmail vazio e userTableId não passado", async () => { + mockGetSupabase.mockReturnValue({ + from: jest.fn(() => ({ + select: jest.fn(() => ({ + eq: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ data: null }), + })), + })), + insert: jest.fn(() => ({ + select: jest.fn(() => ({ + single: jest.fn().mockResolvedValue({ data: null }), + })), + })), + })), + }); + const id = await logAiUsage(baseEntry, { userEmail: "" }); + expect(id).toBeNull(); + }); +}); diff --git a/__tests__/lib/logger.test.ts b/__tests__/lib/logger.test.ts new file mode 100644 index 0000000..07dae0b --- /dev/null +++ b/__tests__/lib/logger.test.ts @@ -0,0 +1,67 @@ +import { logger } from "@/lib/logger"; + +const fs = require("fs"); +jest.mock("fs", () => ({ + existsSync: jest.fn(() => false), + mkdirSync: jest.fn(), + appendFileSync: jest.fn(), +})); + +describe("lib/logger", () => { + beforeEach(() => { + jest.clearAllMocks(); + (fs.existsSync as jest.Mock).mockReturnValue(false); + }); + + it("chama appendFileSync ao logar error", () => { + logger.error("test error"); + expect(fs.mkdirSync).toHaveBeenCalled(); + expect(fs.appendFileSync).toHaveBeenCalled(); + const call = (fs.appendFileSync as jest.Mock).mock.calls[0]; + expect(call[1]).toContain("test error"); + expect(call[1]).toContain("ERROR"); + }); + + it("chama appendFileSync ao logar info", () => { + logger.info("test info"); + expect(fs.appendFileSync).toHaveBeenCalled(); + const call = (fs.appendFileSync as jest.Mock).mock.calls[0]; + expect(call[1]).toContain("test info"); + }); + + it("aceita meta no segundo argumento", () => { + logger.warn("warn msg", { code: 429 }); + const call = (fs.appendFileSync as jest.Mock).mock.calls[0]; + expect(call[1]).toContain("429"); + }); + + it("debug chama write", () => { + const orig = process.env.LOG_LEVEL; + process.env.LOG_LEVEL = "debug"; + logger.debug("debug msg"); + expect(fs.appendFileSync).toHaveBeenCalled(); + process.env.LOG_LEVEL = orig; + }); + + it("não quebra quando appendFileSync lança", () => { + (fs.appendFileSync as jest.Mock).mockImplementationOnce(() => { + throw new Error("disk full"); + }); + const spy = jest.spyOn(console, "error").mockImplementation(); + expect(() => logger.error("fail")).not.toThrow(); + expect(spy).toHaveBeenCalledWith("[logger] write failed:", expect.any(Error)); + spy.mockRestore(); + }); + + it("chama mkdirSync quando diretório não existe", () => { + (fs.existsSync as jest.Mock).mockReturnValue(false); + logger.info("test"); + expect(fs.mkdirSync).toHaveBeenCalledWith(expect.any(String), { recursive: true }); + }); + + it("não chama mkdirSync quando diretório já existe", () => { + (fs.existsSync as jest.Mock).mockReturnValue(true); + logger.warn("test"); + expect(fs.mkdirSync).not.toHaveBeenCalled(); + }); +}); diff --git a/__tests__/lib/stripe.test.ts b/__tests__/lib/stripe.test.ts new file mode 100644 index 0000000..664a189 --- /dev/null +++ b/__tests__/lib/stripe.test.ts @@ -0,0 +1,45 @@ +const originalEnv = process.env; + +jest.mock("stripe", () => { + return jest.fn().mockImplementation(() => ({ _mock: "stripe" })); +}); + +describe("lib/stripe", () => { + afterEach(() => { + process.env = { ...originalEnv }; + jest.resetModules(); + }); + + describe("isStripeConfigured", () => { + it("retorna false quando STRIPE_SECRET_KEY não está definida", () => { + delete process.env.STRIPE_SECRET_KEY; + const { isStripeConfigured } = require("@/lib/stripe"); + expect(isStripeConfigured()).toBe(false); + }); + it("retorna false quando STRIPE_SECRET_KEY é string vazia", () => { + process.env.STRIPE_SECRET_KEY = " "; + const { isStripeConfigured } = require("@/lib/stripe"); + expect(isStripeConfigured()).toBe(false); + }); + it("retorna true quando STRIPE_SECRET_KEY tem valor", () => { + process.env.STRIPE_SECRET_KEY = "sk_test_abc"; + const { isStripeConfigured } = require("@/lib/stripe"); + expect(isStripeConfigured()).toBe(true); + }); + }); + + describe("getStripe", () => { + it("retorna null quando não configurado", () => { + delete process.env.STRIPE_SECRET_KEY; + jest.resetModules(); + const { getStripe } = require("@/lib/stripe"); + expect(getStripe()).toBeNull(); + }); + it("retorna instância quando configurado", () => { + process.env.STRIPE_SECRET_KEY = "sk_test_abc123"; + jest.resetModules(); + const { getStripe } = require("@/lib/stripe"); + expect(getStripe()).not.toBeNull(); + }); + }); +}); diff --git a/__tests__/lib/supabase.test.ts b/__tests__/lib/supabase.test.ts new file mode 100644 index 0000000..359ee74 --- /dev/null +++ b/__tests__/lib/supabase.test.ts @@ -0,0 +1,46 @@ +jest.mock("@supabase/supabase-js", () => ({ + createClient: jest.fn(() => ({ mock: true })), +})); + +const originalEnv = process.env; + +describe("lib/supabase", () => { + beforeEach(() => { + jest.resetModules(); + process.env = { ...originalEnv }; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + describe("isSupabaseConfigured", () => { + it("retorna false quando faltam variáveis", () => { + delete process.env.SUPABASE_URL; + delete process.env.SUPABASE_SERVICE_KEY; + const { isSupabaseConfigured } = require("@/lib/supabase"); + expect(isSupabaseConfigured()).toBe(false); + }); + it("retorna true quando ambas estão definidas", () => { + process.env.SUPABASE_URL = "https://x.supabase.co"; + process.env.SUPABASE_SERVICE_KEY = "key"; + const { isSupabaseConfigured } = require("@/lib/supabase"); + expect(isSupabaseConfigured()).toBe(true); + }); + }); + + describe("getSupabase", () => { + it("retorna null quando não configurado", () => { + delete process.env.SUPABASE_URL; + delete process.env.SUPABASE_SERVICE_KEY; + const { getSupabase } = require("@/lib/supabase"); + expect(getSupabase()).toBeNull(); + }); + it("retorna cliente quando configurado", () => { + process.env.SUPABASE_URL = "https://x.supabase.co"; + process.env.SUPABASE_SERVICE_KEY = "key"; + const { getSupabase } = require("@/lib/supabase"); + expect(getSupabase()).toEqual({ mock: true }); + }); + }); +}); diff --git a/__tests__/lib/usageLimits.test.ts b/__tests__/lib/usageLimits.test.ts new file mode 100644 index 0000000..862dece --- /dev/null +++ b/__tests__/lib/usageLimits.test.ts @@ -0,0 +1,84 @@ +import { + checkAndRecordRateLimit, + checkDailyLimit, + recordDailyRequest, + getRateLimitConfig, + getDailyLimitConfig, +} from "@/lib/usageLimits"; + +const RATE_KEY = "RATE_LIMIT_PER_MINUTE"; +const DAILY_KEY = "DAILY_AI_LIMIT"; + +describe("lib/usageLimits", () => { + const originalRate = process.env[RATE_KEY]; + const originalDaily = process.env[DAILY_KEY]; + + afterEach(() => { + if (originalRate !== undefined) process.env[RATE_KEY] = originalRate; + else delete process.env[RATE_KEY]; + if (originalDaily !== undefined) process.env[DAILY_KEY] = originalDaily; + else delete process.env[DAILY_KEY]; + }); + + describe("checkAndRecordRateLimit", () => { + it("retorna true quando RATE_LIMIT_PER_MINUTE=0 (desativado)", () => { + process.env[RATE_KEY] = "0"; + const key = "rate-test-off@test.com"; + expect(checkAndRecordRateLimit(key)).toBe(true); + expect(checkAndRecordRateLimit(key)).toBe(true); + }); + + it("permite até N requisições por minuto e bloqueia a N+1", () => { + process.env[RATE_KEY] = "2"; + const key = "rate-test-2@test.com"; + expect(checkAndRecordRateLimit(key)).toBe(true); + expect(checkAndRecordRateLimit(key)).toBe(true); + expect(checkAndRecordRateLimit(key)).toBe(false); + }); + + it("usa chaves diferentes por usuário", () => { + process.env[RATE_KEY] = "1"; + expect(checkAndRecordRateLimit("user-a@test.com")).toBe(true); + expect(checkAndRecordRateLimit("user-b@test.com")).toBe(true); + expect(checkAndRecordRateLimit("user-a@test.com")).toBe(false); + }); + }); + + describe("getRateLimitConfig / getDailyLimitConfig", () => { + it("retorna padrão quando env não definido", () => { + delete process.env[RATE_KEY]; + delete process.env[DAILY_KEY]; + expect(getRateLimitConfig().perMinute).toBe(5); + expect(getDailyLimitConfig()).toBe(30); + }); + it("retorna valor da env quando definido", () => { + process.env[RATE_KEY] = "10"; + process.env[DAILY_KEY] = "50"; + expect(getRateLimitConfig().perMinute).toBe(10); + expect(getDailyLimitConfig()).toBe(50); + }); + }); + + describe("checkDailyLimit", () => { + it("retorna allowed true e limit 0 quando DAILY_AI_LIMIT=0", async () => { + process.env[DAILY_KEY] = "0"; + const r = await checkDailyLimit("daily-off@test.com"); + expect(r.allowed).toBe(true); + expect(r.limit).toBe(0); + }); + it("retorna allowed true quando count < limit (sem Supabase usa in-memory 0)", async () => { + process.env[DAILY_KEY] = "30"; + const r = await checkDailyLimit("daily-new@test.com"); + expect(r.allowed).toBe(true); + expect(r.count).toBe(0); + expect(r.limit).toBe(30); + }); + }); + + describe("recordDailyRequest", () => { + it("não lança quando chamado (in-memory quando sem Supabase)", () => { + recordDailyRequest("record@test.com"); + recordDailyRequest("record@test.com"); + }); + }); +}); diff --git a/jest.config.mjs b/jest.config.mjs new file mode 100644 index 0000000..3719d42 --- /dev/null +++ b/jest.config.mjs @@ -0,0 +1,37 @@ +import nextJest from "next/jest.js"; + +const createJestConfig = nextJest({ dir: "./" }); + +/** @type {import('jest').Config} */ +const config = { + testEnvironment: "node", + collectCoverageFrom: [ + "lib/**/*.ts", + "!lib/**/*.d.ts", + "!lib/**/index.ts", + "!lib/**/types.ts", + "!lib/ai/**", + "!lib/knowledge/**", + "!lib/darshanPrompt.ts", + "!lib/configStore.ts", + "!lib/otpStore.ts", + "!lib/oracleOffline.ts", + "!lib/email.ts", + "!lib/userProfile.ts", + "!lib/timepulse.ts", + ], + coverageThreshold: { + global: { + lines: 90, + branches: 85, + functions: 90, + statements: 90, + }, + }, + testMatch: ["**/__tests__/**/*.test.ts", "**/__tests__/**/*.test.tsx"], + moduleNameMapper: { + "^@/(.*)$": "/$1", + }, +}; + +export default createJestConfig(config); From 1339a289c81e65877937a20ff2e12c01151bf0a9 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:11:43 -0300 Subject: [PATCH 6/9] ci: add lib/ and fix Jest rootDir so tests resolve @/ in CI --- jest.config.mjs | 2 + lib/adminAuth.ts | 30 ++ lib/ai/anthropic.ts | 35 ++ lib/ai/google.ts | 39 ++ lib/ai/index.ts | 41 ++ lib/ai/openai.ts | 37 ++ lib/ai/types.ts | 30 ++ lib/audit.ts | 51 +++ lib/auth.ts | 42 ++ lib/configStore.ts | 130 ++++++ lib/credits.ts | 55 +++ lib/darshan.ts | 21 +- lib/dateFormatBr.ts | 52 +++ lib/email.ts | 56 +++ lib/finance/costEstimator.ts | 77 ++++ lib/finance/creditsManager.ts | 264 +++++++++++ lib/finance/exportCsv.ts | 57 +++ lib/finance/index.ts | 41 ++ lib/finance/platformFee.ts | 29 ++ lib/finance/types.ts | 23 + lib/finance/usageLogger.ts | 104 +++++ lib/knowledge/archetypeTraits.ts | 242 ++++++++++ lib/knowledge/archetypes.ts | 158 +++++++ lib/knowledge/classicTexts.ts | 157 +++++++ lib/knowledge/formulations.ts | 77 ++++ lib/knowledge/index.ts | 16 + lib/knowledge/jyotishMeanings.ts | 760 +++++++++++++++++++++++++++++++ lib/knowledge/keywordMatch.ts | 98 ++++ lib/knowledge/moonPhases.ts | 81 ++++ lib/knowledge/numerology.ts | 233 ++++++++++ lib/knowledge/types.ts | 66 +++ lib/knowledge/upanishads.ts | 42 ++ lib/knowledge/vedic.ts | 179 ++++++++ lib/logPaths.ts | 19 + lib/logger.ts | 73 +++ lib/mercadopago.ts | 68 +++ lib/oracleOffline.ts | 181 ++++++++ lib/otpStore.ts | 38 ++ lib/readingOffline.ts | 113 +++++ lib/stripe.ts | 20 + lib/supabase.ts | 21 + lib/timepulse.ts | 43 +- lib/usageLimits.ts | 122 +++++ lib/userProfile.ts | 47 ++ 44 files changed, 4064 insertions(+), 6 deletions(-) create mode 100644 lib/adminAuth.ts create mode 100644 lib/ai/anthropic.ts create mode 100644 lib/ai/google.ts create mode 100644 lib/ai/index.ts create mode 100644 lib/ai/openai.ts create mode 100644 lib/ai/types.ts create mode 100644 lib/audit.ts create mode 100644 lib/auth.ts create mode 100644 lib/configStore.ts create mode 100644 lib/credits.ts create mode 100644 lib/dateFormatBr.ts create mode 100644 lib/email.ts create mode 100644 lib/finance/costEstimator.ts create mode 100644 lib/finance/creditsManager.ts create mode 100644 lib/finance/exportCsv.ts create mode 100644 lib/finance/index.ts create mode 100644 lib/finance/platformFee.ts create mode 100644 lib/finance/types.ts create mode 100644 lib/finance/usageLogger.ts create mode 100644 lib/knowledge/archetypeTraits.ts create mode 100644 lib/knowledge/archetypes.ts create mode 100644 lib/knowledge/classicTexts.ts create mode 100644 lib/knowledge/formulations.ts create mode 100644 lib/knowledge/index.ts create mode 100644 lib/knowledge/jyotishMeanings.ts create mode 100644 lib/knowledge/keywordMatch.ts create mode 100644 lib/knowledge/moonPhases.ts create mode 100644 lib/knowledge/numerology.ts create mode 100644 lib/knowledge/types.ts create mode 100644 lib/knowledge/upanishads.ts create mode 100644 lib/knowledge/vedic.ts create mode 100644 lib/logPaths.ts create mode 100644 lib/logger.ts create mode 100644 lib/mercadopago.ts create mode 100644 lib/oracleOffline.ts create mode 100644 lib/otpStore.ts create mode 100644 lib/readingOffline.ts create mode 100644 lib/stripe.ts create mode 100644 lib/supabase.ts create mode 100644 lib/usageLimits.ts create mode 100644 lib/userProfile.ts diff --git a/jest.config.mjs b/jest.config.mjs index 3719d42..42750b8 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -32,6 +32,8 @@ const config = { moduleNameMapper: { "^@/(.*)$": "/$1", }, + // Garante rootDir explícito para CI (evita $1 literal no path) + rootDir: ".", }; export default createJestConfig(config); diff --git a/lib/adminAuth.ts b/lib/adminAuth.ts new file mode 100644 index 0000000..f1c5cca --- /dev/null +++ b/lib/adminAuth.ts @@ -0,0 +1,30 @@ +/** + * Autenticação admin (CONFIG_SECRET) para rotas de administração. + */ + +export function getSecretFromRequest(req: Request): string | null { + const key = req.headers.get("x-config-key"); + if (key) return key; + const auth = req.headers.get("authorization"); + if (auth?.startsWith("Bearer ")) return auth.slice(7).trim(); + const url = new URL(req.url); + const q = url.searchParams.get("key"); + if (q) return q; + return null; +} + +export function checkAdminAuth(req: Request): { ok: true } | { ok: false; status: number; error: string } { + const secret = process.env.CONFIG_SECRET; + if (!secret) { + return { + ok: false, + status: 503, + error: "Admin desativado. Defina CONFIG_SECRET.", + }; + } + const provided = getSecretFromRequest(req); + if (provided !== secret) { + return { ok: false, status: 401, error: "Não autorizado." }; + } + return { ok: true }; +} diff --git a/lib/ai/anthropic.ts b/lib/ai/anthropic.ts new file mode 100644 index 0000000..179a11c --- /dev/null +++ b/lib/ai/anthropic.ts @@ -0,0 +1,35 @@ +import Anthropic from "@anthropic-ai/sdk"; +import type { AIConnector } from "./types"; + +function createClient(): Anthropic | null { + const key = process.env.ANTHROPIC_API_KEY; + if (!key) return null; + return new Anthropic({ apiKey: key }); +} + +export function createAnthropicConnector(): AIConnector | null { + const client = createClient(); + if (!client) return null; + + return { + id: "anthropic", + name: "Anthropic (Claude)", + async complete(systemPrompt: string, userContent: string) { + const message = await client.messages.create({ + model: process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514", + max_tokens: 1024, + system: systemPrompt, + messages: [{ role: "user", content: userContent }], + }); + const block = message.content.find((b) => b.type === "text"); + const raw = block && "text" in block ? block.text.trim() : ""; + const usage = message.usage + ? { + input_tokens: message.usage.input_tokens ?? 0, + output_tokens: message.usage.output_tokens ?? 0, + } + : undefined; + return { text: raw ?? "", usage }; + }, + }; +} diff --git a/lib/ai/google.ts b/lib/ai/google.ts new file mode 100644 index 0000000..1b16935 --- /dev/null +++ b/lib/ai/google.ts @@ -0,0 +1,39 @@ +import { GoogleGenerativeAI } from "@google/generative-ai"; +import type { AIConnector } from "./types"; + +function createClient(): GoogleGenerativeAI | null { + const key = process.env.GOOGLE_AI_API_KEY; + if (!key) return null; + return new GoogleGenerativeAI(key); +} + +export function createGoogleConnector(): AIConnector | null { + const client = createClient(); + if (!client) return null; + + return { + id: "google", + name: "Google (Gemini)", + async complete(systemPrompt: string, userContent: string) { + const model = client.getGenerativeModel({ + model: process.env.GOOGLE_MODEL ?? "gemini-2.5-flash", + generationConfig: { + temperature: 0.85, + }, + }); + const fullPrompt = `${systemPrompt}\n\n---\n\n${userContent}`; + const result = await model.generateContent(fullPrompt); + const response = result.response; + const raw = typeof response.text === "function" ? response.text() : response.text; + const text = (raw && typeof raw === "string" ? raw : "").trim(); + const meta = (response as { usageMetadata?: { promptTokenCount?: number; candidatesTokenCount?: number; totalTokenCount?: number } }).usageMetadata; + const usage = meta + ? { + input_tokens: meta.promptTokenCount ?? 0, + output_tokens: meta.candidatesTokenCount ?? (meta.totalTokenCount ?? 0) - (meta.promptTokenCount ?? 0), + } + : undefined; + return { text, usage }; + }, + }; +} diff --git a/lib/ai/index.ts b/lib/ai/index.ts new file mode 100644 index 0000000..b3e32e2 --- /dev/null +++ b/lib/ai/index.ts @@ -0,0 +1,41 @@ +import type { AIConnector, ConnectorId } from "./types"; +import { createOpenAIConnector } from "./openai"; +import { createAnthropicConnector } from "./anthropic"; +import { createGoogleConnector } from "./google"; + +const CONNECTORS: Record AIConnector | null> = { + openai: createOpenAIConnector, + anthropic: createAnthropicConnector, + google: createGoogleConnector, +}; + +/** + * Retorna o conector de IA configurado. + * DARSHAN_AI_PROVIDER = openai | anthropic | google (default: primeiro com chave definida). + */ +export function getConnector(): AIConnector | null { + const preferred = (process.env.DARSHAN_AI_PROVIDER ?? "").toLowerCase() as ConnectorId; + if (preferred && CONNECTORS[preferred]) { + const connector = CONNECTORS[preferred](); + if (connector) return connector; + } + for (const key of ["openai", "anthropic", "google"] as ConnectorId[]) { + const connector = CONNECTORS[key](); + if (connector) return connector; + } + return null; +} + +/** + * Lista os conectores disponíveis (com chave configurada). + */ +export function getAvailableConnectors(): AIConnector[] { + const list: AIConnector[] = []; + for (const key of ["openai", "anthropic", "google"] as ConnectorId[]) { + const connector = CONNECTORS[key](); + if (connector) list.push(connector); + } + return list; +} + +export type { AIConnector, ConnectorId } from "./types"; diff --git a/lib/ai/openai.ts b/lib/ai/openai.ts new file mode 100644 index 0000000..ccee574 --- /dev/null +++ b/lib/ai/openai.ts @@ -0,0 +1,37 @@ +import OpenAI from "openai"; +import type { AIConnector } from "./types"; + +function createClient(): OpenAI | null { + const key = process.env.OPENAI_API_KEY; + if (!key) return null; + return new OpenAI({ apiKey: key }); +} + +export function createOpenAIConnector(): AIConnector | null { + const client = createClient(); + if (!client) return null; + + return { + id: "openai", + name: "OpenAI (GPT)", + async complete(systemPrompt: string, userContent: string) { + const completion = await client.chat.completions.create({ + model: process.env.OPENAI_MODEL ?? "gpt-4o-mini", + messages: [ + { role: "system", content: systemPrompt }, + { role: "user", content: userContent }, + ], + response_format: { type: "json_object" }, + temperature: 0.85, + }); + const raw = completion.choices[0]?.message?.content?.trim() ?? ""; + const usage = completion.usage + ? { + input_tokens: completion.usage.prompt_tokens ?? 0, + output_tokens: completion.usage.completion_tokens ?? 0, + } + : undefined; + return { text: raw, usage }; + }, + }; +} diff --git a/lib/ai/types.ts b/lib/ai/types.ts new file mode 100644 index 0000000..b5dffeb --- /dev/null +++ b/lib/ai/types.ts @@ -0,0 +1,30 @@ +/** + * Uso de tokens retornado por alguns provedores (para custo e auditoria). + */ +export type AIUsage = { + input_tokens: number; + output_tokens: number; +}; + +/** + * Resultado de uma chamada complete: texto e opcionalmente uso de tokens. + */ +export type CompleteResult = { + text: string; + usage?: AIUsage; +}; + +/** + * Contrato de um conector de IA para o Darshan. + * Qualquer provedor (OpenAI, Anthropic, Google, etc.) implementa este contrato. + */ +export type AIConnector = { + readonly id: string; + readonly name: string; + /** + * Envia system + user e retorna o texto da resposta e, se disponível, uso de tokens. + */ + complete(systemPrompt: string, userContent: string): Promise; +}; + +export type ConnectorId = "openai" | "anthropic" | "google"; diff --git a/lib/audit.ts b/lib/audit.ts new file mode 100644 index 0000000..47d867c --- /dev/null +++ b/lib/audit.ts @@ -0,0 +1,51 @@ +/** + * Auditoria em arquivo — registro de ações relevantes (login, créditos, config). + * Grava em data/logs/audit.log (ou LOG_DIR quando definido). + */ + +import fs from "fs"; +import path from "path"; + +function getLogDir(): string { + const base = process.env.LOG_DIR || process.env.DATA_DIR || path.join(process.cwd(), "data"); + return path.join(base, "logs"); +} + +function getAuditLogPath(): string { + return path.join(getLogDir(), "audit.log"); +} + +function ensureLogDir(): void { + const dir = getLogDir(); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +export type AuditEvent = + | "login_email" + | "login_google" + | "logout" + | "credits_add" + | "credits_use" + | "config_read" + | "config_update" + | "config_unlock"; + +/** + * Registra um evento de auditoria. subject = identificador do ator (ex.: e-mail); details = dados opcionais. + */ +export function audit( + event: AuditEvent, + subject: string, + details?: Record +): void { + try { + ensureLogDir(); + const ts = new Date().toISOString(); + const line = `${ts} ${event} subject=${JSON.stringify(subject)}${details != null && Object.keys(details).length > 0 ? ` ${JSON.stringify(details)}` : ""}\n`; + fs.appendFileSync(getAuditLogPath(), line, "utf8"); + } catch (err) { + console.error("[audit] write failed:", err); + } +} diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 0000000..0de18fe --- /dev/null +++ b/lib/auth.ts @@ -0,0 +1,42 @@ +/** + * Autenticação sem senha — sessão por código enviado por email. + * Sessão armazenada em cookie; identificador único = email. + */ + +const SESSION_COOKIE = "darshan_session"; +const SESSION_MAX_AGE = 60 * 60 * 24 * 30; // 30 dias + +export type Session = { + email: string; +}; + +function encodeSession(s: Session): string { + return Buffer.from(JSON.stringify(s), "utf-8").toString("base64url"); +} + +function decodeSession(value: string): Session | null { + try { + const json = Buffer.from(value, "base64url").toString("utf-8"); + const s = JSON.parse(json) as Session; + return typeof s.email === "string" && s.email ? s : null; + } catch { + return null; + } +} + +export function getSessionFromCookie(cookieHeader: string | null): Session | null { + if (!cookieHeader) return null; + const match = cookieHeader.match(new RegExp(`${SESSION_COOKIE}=([^;]+)`)); + const value = match?.[1]; + if (!value) return null; + return decodeSession(decodeURIComponent(value)); +} + +export function sessionCookieHeader(session: Session): string { + const value = encodeURIComponent(encodeSession(session)); + return `${SESSION_COOKIE}=${value}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${SESSION_MAX_AGE}`; +} + +export function clearSessionCookieHeader(): string { + return `${SESSION_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`; +} diff --git a/lib/configStore.ts b/lib/configStore.ts new file mode 100644 index 0000000..6fd2ac3 --- /dev/null +++ b/lib/configStore.ts @@ -0,0 +1,130 @@ +/** + * Store de configuração editável — sementes da IA e textos do oráculo. + * Persistência em arquivo (data/config.json); em produção pode usar KV (variável de ambiente). + */ + +import fs from "fs"; +import path from "path"; + +export type ConfigFieldMode = "replace" | "complement"; + +export type AppConfig = { + masterPromptOverride?: string | null; + masterPromptMode?: ConfigFieldMode | null; + revelationInstructionOverride?: string | null; + revelationInstructionMode?: ConfigFieldMode | null; + mockMessagesOverride?: string[] | null; + mockMessagesMode?: ConfigFieldMode | null; + /** Instrução/prompt da Leitura (resumo completo Sol, Lua, planetas, Nakshatras, numerologia). */ + readingInstructionOverride?: string | null; + readingInstructionMode?: ConfigFieldMode | null; + /** Créditos por revelação (IA). Padrão 1. */ + creditsPerRevelation?: number | null; + /** Créditos por leitura (resumo completo). Padrão 9. */ + creditsPerReading?: number | null; + /** Valor por crédito em centavos (BRL). Ex.: 69 = R$ 0,69. Usado para exibição e pacotes. */ + pricePerCreditCents?: number | null; + updatedAt?: string | null; +}; + +const DEFAULT_CONFIG: AppConfig = {}; + +function getConfigPath(): string { + const dir = process.env.DATA_DIR || path.join(process.cwd(), "data"); + return path.join(dir, "config.json"); +} + +function ensureDataDir(): void { + const dir = path.dirname(getConfigPath()); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +/** + * Lê a configuração atual. Se o arquivo não existir ou KV não estiver configurado, retorna objeto vazio. + */ +export function getConfig(): AppConfig { + if (process.env.DARSHAN_CONFIG_KV === "1" && process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) { + return getConfigFromKV(); + } + const filePath = getConfigPath(); + try { + const raw = fs.readFileSync(filePath, "utf8"); + const data = JSON.parse(raw) as Record; + const mode = (v: unknown): ConfigFieldMode => + v === "replace" || v === "complement" ? v : "complement"; + return { + masterPromptOverride: typeof data.masterPromptOverride === "string" ? data.masterPromptOverride : null, + masterPromptMode: mode(data.masterPromptMode), + revelationInstructionOverride: + typeof data.revelationInstructionOverride === "string" ? data.revelationInstructionOverride : null, + revelationInstructionMode: mode(data.revelationInstructionMode), + mockMessagesOverride: Array.isArray(data.mockMessagesOverride) + ? data.mockMessagesOverride.filter((x): x is string => typeof x === "string") + : null, + mockMessagesMode: mode(data.mockMessagesMode), + readingInstructionOverride: + typeof data.readingInstructionOverride === "string" ? data.readingInstructionOverride : null, + readingInstructionMode: mode(data.readingInstructionMode), + creditsPerRevelation: typeof data.creditsPerRevelation === "number" && data.creditsPerRevelation >= 0 ? data.creditsPerRevelation : null, + creditsPerReading: typeof data.creditsPerReading === "number" && data.creditsPerReading >= 0 ? data.creditsPerReading : null, + pricePerCreditCents: typeof data.pricePerCreditCents === "number" && data.pricePerCreditCents >= 0 ? data.pricePerCreditCents : null, + updatedAt: typeof data.updatedAt === "string" ? data.updatedAt : null, + }; + } catch { + return { ...DEFAULT_CONFIG }; + } +} + +/** + * Grava configuração (merge com o que já existe). Em produção com KV, persiste no KV. + */ +export function setConfig(partial: Partial): AppConfig { + const current = getConfig(); + const updated: AppConfig = { + ...current, + ...partial, + updatedAt: new Date().toISOString(), + }; + if (process.env.DARSHAN_CONFIG_KV === "1" && process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) { + setConfigToKV(updated); + return updated; + } + ensureDataDir(); + const filePath = getConfigPath(); + fs.writeFileSync( + filePath, + JSON.stringify( + { + masterPromptOverride: updated.masterPromptOverride ?? null, + masterPromptMode: updated.masterPromptMode ?? "complement", + revelationInstructionOverride: updated.revelationInstructionOverride ?? null, + revelationInstructionMode: updated.revelationInstructionMode ?? "complement", + mockMessagesOverride: updated.mockMessagesOverride ?? null, + mockMessagesMode: updated.mockMessagesMode ?? "complement", + readingInstructionOverride: updated.readingInstructionOverride ?? null, + readingInstructionMode: updated.readingInstructionMode ?? "complement", + creditsPerRevelation: updated.creditsPerRevelation ?? null, + creditsPerReading: updated.creditsPerReading ?? null, + pricePerCreditCents: updated.pricePerCreditCents ?? null, + updatedAt: updated.updatedAt ?? null, + }, + null, + 2 + ), + "utf8" + ); + return updated; +} + +/** Placeholder: leitura de KV (Vercel KV / Upstash). Implementar quando DARSHAN_CONFIG_KV=1. */ +function getConfigFromKV(): AppConfig { + // TODO: fetch from KV_REST_API_URL with KV_REST_API_TOKEN, key "darshan:config" + return { ...DEFAULT_CONFIG }; +} + +/** Placeholder: escrita em KV. Implementar quando DARSHAN_CONFIG_KV=1. */ +function setConfigToKV(_config: AppConfig): void { + // TODO: PUT to KV_REST_API_URL with KV_REST_API_TOKEN, key "darshan:config" +} diff --git a/lib/credits.ts b/lib/credits.ts new file mode 100644 index 0000000..f718877 --- /dev/null +++ b/lib/credits.ts @@ -0,0 +1,55 @@ +/** + * Créditos de IA — 1 crédito = 1 resposta com IA (modo ritual). + * Planos Fibonacci: muito acessíveis, primeiro plano < R$10. + * Ver docs/PRECIFICACAO_CREDITOS.md. + */ + +const CREDITS_COOKIE = "darshan_credits"; + +/** 1 consulta (modo ritual, curto) = 1 crédito. Aprofundar = +1; imagem/sigil = +1. */ +export const CREDITS_PER_AI_REQUEST = 1; + +/** Mapa pessoal: resumo completo (Jyotish + numerologia + arquétipos) = 9 créditos. */ +export const CREDITS_PER_PERSONAL_MAP = 9; + +/** Modos de revelação para multiplicador de créditos (doc: ritual=1, longo=2, longo+imagem=3). */ +export type RevelationMode = "ritual" | "long" | "long_image"; + +/** Créditos por modo. ritual=1, longo (aprofundar)=2, longo+imagem/sigil=3. */ +export function getCreditsForRevelation(mode: RevelationMode): number { + return mode === "ritual" ? 1 : mode === "long" ? 2 : 3; +} + +/** Pacotes de créditos. Preço em centavos (BRL). */ +export const CREDIT_PACKAGES = [ + { id: "13", amount: 13, priceCents: 890, label: "13 créditos" }, + { id: "21", amount: 21, priceCents: 1390, label: "21 créditos" }, + { id: "34", amount: 34, priceCents: 2190, label: "34 créditos" }, + { id: "55", amount: 55, priceCents: 3490, label: "55 créditos" }, + { id: "89", amount: 89, priceCents: 5590, label: "89 créditos" }, +] as const; + +export function formatPriceBRL(cents: number): string { + return new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(cents / 100); +} + +export function getCreditsFromCookie(cookieHeader: string | null): number { + if (!cookieHeader) return 0; + const match = cookieHeader.match(new RegExp(`${CREDITS_COOKIE}=([^;]+)`)); + const value = match?.[1]; + if (!value) return 0; + const n = parseInt(decodeURIComponent(value), 10); + return Number.isFinite(n) && n >= 0 ? n : 0; +} + +export function creditsCookieHeader(balance: number): string { + const n = Math.max(0, Math.floor(balance)); + return `${CREDITS_COOKIE}=${n}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${60 * 60 * 24 * 365}`; +} + +export function clearCreditsCookieHeader(): string { + return `${CREDITS_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`; +} diff --git a/lib/darshan.ts b/lib/darshan.ts index b6dfefa..8187843 100644 --- a/lib/darshan.ts +++ b/lib/darshan.ts @@ -1,3 +1,20 @@ -export type DarshanResponse = { - steps: string[]; +export type DarshanTurn = { + phase: number; + userMessage?: string; + darshanMessage: string; +}; + +export type DarshanPhaseResponse = { + message: string; + phase: number; +}; + +export const PHASE_NAMES: Record = { + 1: "Luz — frase-oráculo", + 2: "Pulso Jyotish — qualidade do tempo agora", + 3: "Arquétipo Chinês — ciclo anual vigente", + 4: "Elemento — Ayurveda (Terra/Água/Fogo/Ar/Éter)", + 5: "Consciência — guna predominante (Sattva/Rajas/Tamas)", + 6: "Ação mínima — prática corporal segura (30–90s)", + 7: "Pergunta final — devolução à presença", }; diff --git a/lib/dateFormatBr.ts b/lib/dateFormatBr.ts new file mode 100644 index 0000000..5d36ae6 --- /dev/null +++ b/lib/dateFormatBr.ts @@ -0,0 +1,52 @@ +/** + * Formato Brasil: data DD/MM/AAAA e hora HH:mm. + * Armazenamento interno continua YYYY-MM-DD e HH:mm para compatibilidade. + */ + +/** Converte YYYY-MM-DD → DD/MM/AAAA */ +export function toBrDate(iso: string | undefined): string { + if (!iso || !/^\d{4}-\d{2}-\d{2}$/.test(iso)) return ""; + const [y, m, d] = iso.split("-"); + return `${d}/${m}/${y}`; +} + +/** Converte DD/MM/AAAA → YYYY-MM-DD (para salvar) */ +export function fromBrDate(br: string): string { + const cleaned = br.replace(/\D/g, ""); + if (cleaned.length !== 8) return ""; + const d = cleaned.slice(0, 2); + const m = cleaned.slice(2, 4); + const y = cleaned.slice(4, 8); + const day = parseInt(d, 10); + const month = parseInt(m, 10); + const year = parseInt(y, 10); + if (day < 1 || day > 31 || month < 1 || month > 12 || year < 1900 || year > 2100) return ""; + return `${y}-${m}-${d}`; +} + +/** Máscara de digitação: DD/MM/AAAA */ +export function maskBrDate(value: string): string { + const digits = value.replace(/\D/g, "").slice(0, 8); + if (digits.length <= 2) return digits; + if (digits.length <= 4) return `${digits.slice(0, 2)}/${digits.slice(2)}`; + return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`; +} + +/** Máscara de digitação: HH:mm */ +export function maskBrTime(value: string): string { + const digits = value.replace(/\D/g, "").slice(0, 4); + if (digits.length <= 2) return digits; + return `${digits.slice(0, 2)}:${digits.slice(2)}`; +} + +/** Converte HH:mm (ou HHmm) → HH:mm normalizado */ +export function fromBrTime(br: string): string { + const cleaned = br.replace(/\D/g, ""); + if (cleaned.length < 3) return br; + if (cleaned.length === 3) return `${cleaned.slice(0, 2)}:${cleaned.slice(2)}`; + const h = cleaned.slice(0, 2); + const m = cleaned.slice(2, 4); + const hh = Math.min(23, parseInt(h, 10) || 0); + const mm = Math.min(59, parseInt(m, 10) || 0); + return `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}`; +} diff --git a/lib/email.ts b/lib/email.ts new file mode 100644 index 0000000..b4f2ee7 --- /dev/null +++ b/lib/email.ts @@ -0,0 +1,56 @@ +/** + * Conector de e-mail — envio de código de verificação (Resend). + * Em produção defina RESEND_API_KEY e RESEND_FROM. + */ + +const RESEND_API_URL = "https://api.resend.com/emails"; + +export type SendResult = { ok: boolean; error?: string }; + +/** + * Envia o código de verificação por e-mail via Resend. + * Retorna { ok: true } se enviado; { ok: false, error } em caso de falha. + * Se RESEND_API_KEY não estiver definido, retorna { ok: false } e não envia. + */ +export async function sendVerificationCode(to: string, code: string): Promise { + const apiKey = process.env.RESEND_API_KEY; + const from = process.env.RESEND_FROM || "Darshan "; + + if (!apiKey?.trim()) { + return { ok: false, error: "RESEND_API_KEY não configurado." }; + } + + const subject = "Seu código Darshan"; + const html = ` +

Seu código de acesso é: ${code}

+

Use este código na tela de entrada do Darshan. O código expira em alguns minutos.

+

Se você não solicitou este código, ignore este e-mail.

+ `.trim(); + + try { + const res = await fetch(RESEND_API_URL, { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey.trim()}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + from: from.trim(), + to: [to.trim()], + subject, + html, + }), + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + const message = (data as { message?: string }).message || res.statusText; + return { ok: false, error: message }; + } + + return { ok: true }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { ok: false, error: message }; + } +} diff --git a/lib/finance/costEstimator.ts b/lib/finance/costEstimator.ts new file mode 100644 index 0000000..f889d88 --- /dev/null +++ b/lib/finance/costEstimator.ts @@ -0,0 +1,77 @@ +/** + * Estimativa de custo por provedor e tokens. + * Valores por 1M tokens (USD). USD→BRL configurável ou via API (com cache). + */ + +export type CostProvider = "openai" | "anthropic" | "gemini"; + +export interface CostRates { + inputPer1M: number; + outputPer1M: number; +} + +const DEFAULT_RATES: Record = { + openai: { inputPer1M: 0.15, outputPer1M: 0.6 }, + anthropic: { inputPer1M: 3, outputPer1M: 15 }, + gemini: { inputPer1M: 0.35, outputPer1M: 0.53 }, +}; + +const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24h +const DOLAR_API_URL = "https://br.dolarapi.com/v1/cotacoes/usd"; + +let usdToBrl = 5.0; +let cacheExpiresAt = 0; + +export function setUsdToBrl(rate: number): void { + usdToBrl = Math.max(0.01, rate); +} + +export function getUsdToBrl(): number { + return usdToBrl; +} + +/** + * Atualiza a cotação USD→BRL via API (DolarAPI) só se o cache expirou. + * Chame antes de estimateCost quando quiser cotação atualizada. + * Em erro ou API indisponível, mantém o valor atual (ou 5.0). + */ +export async function refreshUsdToBrlCache(): Promise { + if (Date.now() < cacheExpiresAt) return usdToBrl; + try { + const res = await fetch(DOLAR_API_URL, { cache: "no-store" }); + if (!res.ok) return usdToBrl; + const data = (await res.json()) as { compra?: number; venda?: number }; + const compra = Number(data.compra); + const venda = Number(data.venda); + if (Number.isFinite(compra) && Number.isFinite(venda)) { + usdToBrl = (compra + venda) / 2; + cacheExpiresAt = Date.now() + CACHE_TTL_MS; + } + } catch { + // mantém usdToBrl atual + } + return usdToBrl; +} + +/** + * Estima custo em USD e BRL para uma chamada. + */ +export function estimateCost( + provider: CostProvider, + inputTokens: number, + outputTokens: number +): { costUsd: number; costBrl: number } { + const rates = DEFAULT_RATES[provider] ?? DEFAULT_RATES.openai; + const inputUsd = (inputTokens / 1_000_000) * rates.inputPer1M; + const outputUsd = (outputTokens / 1_000_000) * rates.outputPer1M; + const costUsd = inputUsd + outputUsd; + const costBrl = costUsd * usdToBrl; + return { + costUsd: Math.round(costUsd * 1_000_000) / 1_000_000, + costBrl: Math.round(costBrl * 1_000_000) / 1_000_000, + }; +} + +export function getRates(): Record { + return { ...DEFAULT_RATES }; +} diff --git a/lib/finance/creditsManager.ts b/lib/finance/creditsManager.ts new file mode 100644 index 0000000..58864eb --- /dev/null +++ b/lib/finance/creditsManager.ts @@ -0,0 +1,264 @@ +/** + * Gerenciamento de créditos: saldo e ledger auditável. + * Se Supabase estiver configurado, usa tabelas users e credit_ledger. + * Caso contrário, retorna valores para o caller manter cookie. + */ + +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +export type LedgerReason = "purchase" | "darshan_call" | "personal_map" | "admin_adjust"; + +export interface DebitResult { + newBalance: number; + ledgerId?: string; +} + +/** + * Obtém ou cria user por email e retorna o saldo de créditos. + * Se não houver Supabase, retorna balanceFromCookie (passado pelo caller). + */ +export async function getCreditsBalance( + userEmail: string, + balanceFromCookie: number +): Promise { + const supabase = getSupabase(); + if (!supabase) return balanceFromCookie; + + const { data: user } = await supabase + .from("users") + .select("id, credits_balance") + .eq("email", userEmail) + .single(); + + if (user) return user.credits_balance ?? 0; + + const { data: inserted } = await supabase + .from("users") + .insert({ + email: userEmail, + credits_balance: balanceFromCookie, + plan: "free", + }) + .select("credits_balance") + .single(); + + return inserted?.credits_balance ?? balanceFromCookie; +} + +/** + * Debita créditos e registra no ledger. + * Se Supabase: atualiza users.credits_balance e insere credit_ledger. + * Retorna novo saldo; caller deve atualizar cookie se usar cookie. + */ +export async function debitCredits( + userEmail: string, + amount: number, + reason: LedgerReason, + options: { + relatedUsageId?: string; + relatedPaymentId?: string; + currentBalanceFromCookie?: number; + } = {} +): Promise { + const supabase = getSupabase(); + const { relatedUsageId, relatedPaymentId, currentBalanceFromCookie = 0 } = options; + + if (!supabase || !isSupabaseConfigured()) { + const newBalance = Math.max(0, currentBalanceFromCookie - amount); + return { newBalance }; + } + + const { data: user } = await supabase + .from("users") + .select("id, credits_balance") + .eq("email", userEmail) + .single(); + + let userId: string; + let currentBalance: number; + + if (user) { + userId = user.id; + currentBalance = user.credits_balance ?? 0; + } else { + const { data: inserted } = await supabase + .from("users") + .insert({ + email: userEmail, + credits_balance: 0, + plan: "free", + }) + .select("id, credits_balance") + .single(); + if (!inserted) { + return { newBalance: Math.max(0, currentBalanceFromCookie - amount) }; + } + userId = inserted.id; + currentBalance = inserted.credits_balance ?? 0; + } + + const newBalance = Math.max(0, currentBalance - amount); + + const { error: updateError } = await supabase + .from("users") + .update({ credits_balance: newBalance }) + .eq("id", userId); + + if (updateError) { + return { newBalance: Math.max(0, currentBalanceFromCookie - amount) }; + } + + const ledgerRow: Record = { + user_id: userId, + change_amount: -amount, + reason, + related_usage_id: relatedUsageId ?? null, + related_payment_id: relatedPaymentId ?? null, + }; + + const { data: ledger } = await supabase + .from("credit_ledger") + .insert(ledgerRow) + .select("id") + .single(); + + return { newBalance, ledgerId: ledger?.id }; +} + +/** + * Adiciona créditos (compra ou ajuste admin) e registra no ledger. + */ +export async function addCredits( + userEmail: string, + amount: number, + reason: LedgerReason, + options: { + relatedPaymentId?: string; + currentBalanceFromCookie?: number; + } = {} +): Promise { + const supabase = getSupabase(); + const { relatedPaymentId, currentBalanceFromCookie = 0 } = options; + + if (!supabase || !isSupabaseConfigured()) { + return { newBalance: currentBalanceFromCookie + amount }; + } + + const { data: user } = await supabase + .from("users") + .select("id, credits_balance") + .eq("email", userEmail) + .single(); + + let userId: string; + let currentBalance: number; + + if (user) { + userId = user.id; + currentBalance = user.credits_balance ?? 0; + } else { + const { data: inserted } = await supabase + .from("users") + .insert({ + email: userEmail, + credits_balance: amount, + plan: "free", + }) + .select("id, credits_balance") + .single(); + if (!inserted) return { newBalance: currentBalanceFromCookie + amount }; + userId = inserted.id; + currentBalance = inserted.credits_balance ?? 0; + } + + const newBalance = currentBalance + amount; + + await supabase.from("users").update({ credits_balance: newBalance }).eq("id", userId); + + await supabase.from("credit_ledger").insert({ + user_id: userId, + change_amount: amount, + reason, + related_payment_id: relatedPaymentId ?? null, + }); + + return { newBalance }; +} + +export type PaymentProvider = "stripe" | "mercadopago"; +export type PaymentStatus = "pending" | "completed" | "failed" | "refunded"; + +/** + * Registra um pagamento na tabela payments (Supabase). + * Retorna o id do registro ou null se Supabase não estiver configurado / erro. + */ +export async function recordPayment( + userEmail: string, + provider: PaymentProvider, + amountBrl: number, + creditsAdded: number, + status: PaymentStatus, + externalId?: string +): Promise { + const supabase = getSupabase(); + if (!supabase || !isSupabaseConfigured()) return null; + + const { data: user } = await supabase + .from("users") + .select("id") + .eq("email", userEmail) + .single(); + + let userId: string; + if (user) { + userId = user.id; + } else { + const { data: inserted } = await supabase + .from("users") + .insert({ email: userEmail, credits_balance: 0, plan: "free" }) + .select("id") + .single(); + if (!inserted) return null; + userId = inserted.id; + } + + const { data: payment } = await supabase + .from("payments") + .insert({ + user_id: userId, + provider, + amount_brl: amountBrl, + credits_added: creditsAdded, + status, + external_id: externalId ?? null, + }) + .select("id") + .single(); + + return payment?.id ?? null; +} + +/** + * Compra: registra pagamento (quando Supabase) e adiciona créditos + ledger. + * Usar no fulfill do checkout após confirmar pagamento. + */ +export async function addCreditsForPurchase( + userEmail: string, + creditsAdded: number, + amountBrl: number, + provider: PaymentProvider, + externalId: string | undefined, + currentBalanceFromCookie: number +): Promise { + const paymentId = await recordPayment( + userEmail, + provider, + amountBrl, + creditsAdded, + "completed", + externalId + ); + return addCredits(userEmail, creditsAdded, "purchase", { + relatedPaymentId: paymentId ?? undefined, + currentBalanceFromCookie, + }); +} diff --git a/lib/finance/exportCsv.ts b/lib/finance/exportCsv.ts new file mode 100644 index 0000000..c699189 --- /dev/null +++ b/lib/finance/exportCsv.ts @@ -0,0 +1,57 @@ +/** + * Exportação CSV para relatórios financeiros (uso de IA e pagamentos). + */ + +export interface UsageExportRow { + user_id: string; + provider: string; + total_calls: number; + total_tokens: number; + total_cost_brl: number; + credits_spent: number; + revenue_estimate: number; +} + +export interface PaymentExportRow { + user_id: string; + amount_brl: number; + credits_added: number; + status: string; + created_at: string; +} + +const CSV_ESCAPE = (cell: string): string => { + if (/[",\n\r]/.test(cell)) return `"${cell.replace(/"/g, '""')}"`; + return cell; +}; + +export function usageToCsv(rows: UsageExportRow[]): string { + const header = "user_id,provider,total_calls,total_tokens,total_cost_brl,credits_spent,revenue_estimate"; + const lines = rows.map( + (r) => + [ + CSV_ESCAPE(r.user_id), + CSV_ESCAPE(r.provider), + r.total_calls, + r.total_tokens, + r.total_cost_brl.toFixed(2), + r.credits_spent, + r.revenue_estimate.toFixed(2), + ].join(",") + ); + return [header, ...lines].join("\n"); +} + +export function paymentsToCsv(rows: PaymentExportRow[]): string { + const header = "user_id,amount_brl,credits_added,status,created_at"; + const lines = rows.map((r) => + [ + CSV_ESCAPE(r.user_id), + r.amount_brl.toFixed(2), + r.credits_added, + CSV_ESCAPE(r.status), + CSV_ESCAPE(r.created_at), + ].join(",") + ); + return [header, ...lines].join("\n"); +} diff --git a/lib/finance/index.ts b/lib/finance/index.ts new file mode 100644 index 0000000..05c879a --- /dev/null +++ b/lib/finance/index.ts @@ -0,0 +1,41 @@ +/** + * Módulo financeiro: custo, créditos, log de uso e exportação CSV. + */ + +export { + estimateCost, + setUsdToBrl, + getUsdToBrl, + refreshUsdToBrlCache, + getRates, + type CostProvider, + type CostRates, +} from "./costEstimator"; + +export { + getCreditsBalance, + debitCredits, + addCredits, + recordPayment, + addCreditsForPurchase, + type DebitResult, + type LedgerReason, + type PaymentProvider, + type PaymentStatus, +} from "./creditsManager"; + +export { logAiUsage, getTodayAiRequestCount, type LogUsageOptions } from "./usageLogger"; + +export { + usageToCsv, + paymentsToCsv, + type UsageExportRow, + type PaymentExportRow, +} from "./exportCsv"; + +export { + getPlatformFeePercent, + getPlatformFeeDecimal, +} from "./platformFee"; + +export type { AiUsageEntry, AiUsageProvider, UsageMode } from "./types"; diff --git a/lib/finance/platformFee.ts b/lib/finance/platformFee.ts new file mode 100644 index 0000000..412aee9 --- /dev/null +++ b/lib/finance/platformFee.ts @@ -0,0 +1,29 @@ +/** + * Taxa da plataforma (margem) — configurável por ambiente. + * Usada em descrições, relatórios e meta de custo IA vs faturamento. + * Ver docs/PRECIFICACAO_CREDITOS.md. + */ + +const ENV_KEY = "PLATFORM_FEE_PERCENT"; +const DEFAULT_PERCENT = 30; +const MIN = 0; +const MAX = 100; + +/** + * Retorna a taxa da plataforma em percentual (0–100). + * Lê PLATFORM_FEE_PERCENT; se inválido ou ausente, usa 30. + */ +export function getPlatformFeePercent(): number { + const raw = process.env[ENV_KEY]; + if (raw === undefined || raw === "") return DEFAULT_PERCENT; + const n = Number(raw); + if (!Number.isFinite(n)) return DEFAULT_PERCENT; + return Math.max(MIN, Math.min(MAX, Math.round(n))); +} + +/** + * Taxa como decimal (ex.: 0.30 para 30%). Útil para cálculos. + */ +export function getPlatformFeeDecimal(): number { + return getPlatformFeePercent() / 100; +} diff --git a/lib/finance/types.ts b/lib/finance/types.ts new file mode 100644 index 0000000..ddd762f --- /dev/null +++ b/lib/finance/types.ts @@ -0,0 +1,23 @@ +/** + * Tipos do módulo financeiro. + */ + +export type AiUsageProvider = "openai" | "anthropic" | "gemini"; +export type UsageMode = "now" | "question" | "personal_map"; + +export interface AiUsageEntry { + userId: string; + provider: AiUsageProvider; + model: string; + inputTokens: number; + outputTokens: number; + totalTokens: number; + costUsd: number; + costBrl: number; + creditsSpent: number; + mode: UsageMode; + questionLength: number; + responseLength: number; + success: boolean; + safetyFlags?: string | null; +} diff --git a/lib/finance/usageLogger.ts b/lib/finance/usageLogger.ts new file mode 100644 index 0000000..72e0f61 --- /dev/null +++ b/lib/finance/usageLogger.ts @@ -0,0 +1,104 @@ +/** + * Registro de uso de IA em ai_usage_log (Supabase). + * Se Supabase não estiver configurado, não grava (silencioso). + */ + +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +import type { AiUsageEntry } from "./types"; + +export interface LogUsageOptions { + /** user_id na tabela users (UUID). Se não tiver, insere por email e usa o id retornado. */ + userTableId?: string; + /** Email do usuário; usado para obter/criar user_id se userTableId não for passado. */ + userEmail: string; +} + +/** + * Grava um registro em ai_usage_log. + * user_id: se userTableId for passado, usa; senão busca/cria por userEmail e usa o id. + */ +export async function logAiUsage( + entry: Omit, + options: LogUsageOptions +): Promise { + const supabase = getSupabase(); + if (!supabase || !isSupabaseConfigured()) return null; + + let userId = options.userTableId; + if (!userId && options.userEmail) { + const { data: user } = await supabase + .from("users") + .select("id") + .eq("email", options.userEmail) + .single(); + if (user) userId = user.id; + else { + const { data: inserted } = await supabase + .from("users") + .insert({ + email: options.userEmail, + credits_balance: 0, + plan: "free", + }) + .select("id") + .single(); + userId = inserted?.id ?? undefined; + } + } + if (!userId) return null; + + const row = { + user_id: userId, + provider: entry.provider, + model: entry.model, + input_tokens: entry.inputTokens, + output_tokens: entry.outputTokens, + total_tokens: entry.totalTokens, + cost_usd: entry.costUsd, + cost_brl: entry.costBrl, + credits_spent: entry.creditsSpent, + mode: entry.mode, + question_length: entry.questionLength, + response_length: entry.responseLength, + success: entry.success, + safety_flags: entry.safetyFlags ?? null, + }; + + const { data, error } = await supabase + .from("ai_usage_log") + .insert(row) + .select("id") + .single(); + + if (error) return null; + return data?.id ?? null; +} + +/** + * Conta quantas requisições de IA o usuário fez hoje (UTC). + * Usado para limite diário. Sem Supabase retorna 0. + */ +export async function getTodayAiRequestCount(userEmail: string): Promise { + const supabase = getSupabase(); + if (!supabase || !isSupabaseConfigured()) return 0; + + const { data: user } = await supabase + .from("users") + .select("id") + .eq("email", userEmail) + .single(); + if (!user?.id) return 0; + + const todayStart = new Date(); + todayStart.setUTCHours(0, 0, 0, 0); + const iso = todayStart.toISOString(); + + const { count, error } = await supabase + .from("ai_usage_log") + .select("id", { count: "exact", head: true }) + .eq("user_id", user.id) + .gte("created_at", iso); + + if (error) return 0; + return typeof count === "number" ? count : 0; +} diff --git a/lib/knowledge/archetypeTraits.ts b/lib/knowledge/archetypeTraits.ts new file mode 100644 index 0000000..5f45ab1 --- /dev/null +++ b/lib/knowledge/archetypeTraits.ts @@ -0,0 +1,242 @@ +/** + * Dicionário de traits dos arquétipos — personalidade, tendências, desafios, frases. + * Populado para evitar replicação e permitir respostas orgânicas offline. + */ + +import type { ArchetypeKey } from "./types"; +import { pickIndexByKeywords } from "./keywordMatch"; + +export type ArchetypeTraitsEntry = { + key: ArchetypeKey; + name: string; + shortTrait: string; + personality: string[]; + tendencies: string[]; + challenges: string[]; + keywords: string[]; + phrases: string[]; +}; + +const ARCHETYPE_TRAITS: ArchetypeTraitsEntry[] = [ + { + key: "pioneiro", + name: "Pioneiro", + shortTrait: "Iniciativa, coragem, novo começo.", + personality: ["corajoso", "impulsivo", "líder natural", "direto", "energético"], + tendencies: ["iniciar projetos", "romper limites", "ação imediata", "independência", "competição saudável"], + challenges: ["impaciência", "agressividade", "esquecer o outro", "queimar etapas"], + keywords: ["fogo", "início", "coragem", "Aries", "Mesha"], + phrases: [ + "O pioneiro em você não precisa provar nada; já é o primeiro passo.", + "Iniciativa e coragem são dons; a pausa é o complemento.", + "O novo começo que você busca começa no agora.", + "O mapa sugere impulso; a sabedoria é saber quando frear.", + "Seu arquétipo traz fogo; a terra o sustenta.", + ], + }, + { + key: "raiz", + name: "Raiz", + shortTrait: "Estabilidade, sensorial, Terra.", + personality: ["estável", "sensual", "persistente", "prático", "paciente"], + tendencies: ["construir bases", "apreciar beleza", "acumular segurança", "ritual", "corpo"], + challenges: ["rigidez", "possessividade", "medo de mudança", "lentidão excessiva"], + keywords: ["terra", "estabilidade", "Touro", "Vrishabha", "sensorial"], + phrases: [ + "A raiz em você não precisa mais do que já tem; já está plantada.", + "Estabilidade e sensorial são dons; a leveza é o complemento.", + "O que você busca em segurança está na presença.", + "Seu arquétipo traz terra; o céu a completa.", + "Ritual e corpo são seus aliados; a rigidez é o desafio.", + ], + }, + { + key: "mensageiro", + name: "Mensageiro", + shortTrait: "Comunicação, curiosidade, dualidade.", + personality: ["curioso", "versátil", "comunicativo", "intelectual", "adaptável"], + tendencies: ["aprender", "conectar ideias", "variedade", "perguntas", "movimento"], + challenges: ["superficialidade", "dispersão", "nervosismo", "dificuldade em aprofundar"], + keywords: ["ar", "comunicação", "Gêmeos", "Mithuna", "dualidade"], + phrases: [ + "O mensageiro em você não precisa dizer tudo; o silêncio também comunica.", + "Comunicação e curiosidade são dons; a profundidade é o complemento.", + "A pergunta que você faz já contém a resposta.", + "Seu arquétipo traz ar; a terra o ancora.", + "Variedade e movimento são seus aliados; o centro é o desafio.", + ], + }, + { + key: "cuidador", + name: "Cuidador", + shortTrait: "Emoção, memória, nutrição.", + personality: ["sensível", "protetor", "intuitivo", "emocional", "acolhedor"], + tendencies: ["cuidar", "lembrar", "nutrir", "proteger", "casa e família"], + challenges: ["apego excessivo", "mágoa", "fuga em fantasia", "dependência emocional"], + keywords: ["água", "emoção", "Câncer", "Karka", "memória"], + phrases: [ + "O cuidador em você não precisa carregar todos; cuide de quem cuida.", + "Emoção e memória são dons; o desapego é o complemento.", + "A nutrição que você oferece começa em você mesmo.", + "Seu arquétipo traz água; o sol a aquece.", + "Proteção e acolhimento são seus aliados; o apego é o desafio.", + ], + }, + { + key: "soberano", + name: "Soberano", + shortTrait: "Centro, coração, reconhecimento.", + personality: ["generoso", "orgulhoso", "criativo", "leal", "dramático"], + tendencies: ["liderar com coração", "reconhecimento", "criatividade", "proteger os seus", "centro de atenção"], + challenges: ["orgulho excessivo", "vaidade", "raiva", "necessidade de aplausos"], + keywords: ["fogo", "coração", "Leão", "Simha", "centro"], + phrases: [ + "O soberano em você não precisa de coroa; já é o centro.", + "Coração e reconhecimento são dons; a humildade é o complemento.", + "O que você busca em brilho está na luz interior.", + "Seu arquétipo traz sol; a sombra o equilibra.", + "Criatividade e lealdade são seus aliados; o ego é o desafio.", + ], + }, + { + key: "servidor", + name: "Servidor", + shortTrait: "Precisão, discernimento, serviço.", + personality: ["analítico", "modesto", "eficiente", "crítico", "útil"], + tendencies: ["organizar", "purificar", "servir", "discernir", "perfeição"], + challenges: ["perfeccionismo", "crítica excessiva", "ansiedade", "negação do corpo"], + keywords: ["terra", "discernimento", "Virgem", "Kanya", "serviço"], + phrases: [ + "O servidor em você não precisa ser perfeito; já é completo.", + "Precisão e discernimento são dons; a aceitação é o complemento.", + "O serviço que você oferece começa no autocuidado.", + "Seu arquétipo traz pureza; a imperfeição é humana.", + "Organização e utilidade são seus aliados; a rigidez é o desafio.", + ], + }, + { + key: "harmonizador", + name: "Harmonizador", + shortTrait: "Equilíbrio, relação, justiça.", + personality: ["diplomático", "estético", "relacional", "justo", "indeciso"], + tendencies: ["equilibrar", "parceria", "beleza", "justiça", "evitar conflito"], + challenges: ["indecisão", "dependência do outro", "superficialidade", "evitar conflito a qualquer custo"], + keywords: ["ar", "equilíbrio", "Libra", "Tula", "relação"], + phrases: [ + "O harmonizador em você não precisa agradar a todos; já é equilíbrio.", + "Relação e justiça são dons; o centro próprio é o complemento.", + "O equilíbrio que você busca está na decisão interior.", + "Seu arquétipo traz balança; o peso de um lado é humano.", + "Parceria e beleza são seus aliados; a indecisão é o desafio.", + ], + }, + { + key: "transformador", + name: "Transformador", + shortTrait: "Profundidade, morte-renascimento, poder.", + personality: ["intenso", "investigativo", "poderoso", "secretivo", "regenerador"], + tendencies: ["transformar", "investigar", "poder", "intimidade", "crise como portal"], + challenges: ["ciúme", "controle", "raiva", "manipulação"], + keywords: ["água", "transformação", "Escorpião", "Vrischika", "poder"], + phrases: [ + "O transformador em você não precisa controlar; já é renascimento.", + "Profundidade e poder são dons; a entrega é o complemento.", + "A morte que você teme é a porta do novo.", + "Seu arquétipo traz fogo oculto; a confiança o libera.", + "Crise e regeneração são seus aliados; o controle é o desafio.", + ], + }, + { + key: "sabedor", + name: "Sabedor", + shortTrait: "Sentido, filosofia, expansão.", + personality: ["otimista", "filósofo", "aventureiro", "honesto", "expansionista"], + tendencies: ["buscar sentido", "ensinar", "viajar", "liberdade", "visão ampla"], + challenges: ["excesso de idealismo", "impaciência com limites", "dogmatismo", "fuga do cotidiano"], + keywords: ["fogo", "sabedoria", "Sagitário", "Dhanu", "expansão"], + phrases: [ + "O sabedor em você não precisa de mais conhecimento; já é a busca.", + "Sentido e expansão são dons; o aqui e agora é o complemento.", + "A filosofia que você busca está no corpo presente.", + "Seu arquétipo traz seta; o alvo é o instante.", + "Liberdade e verdade são seus aliados; o dogma é o desafio.", + ], + }, + { + key: "estruturador", + name: "Estruturador", + shortTrait: "Tempo, dever, realização.", + personality: ["disciplinado", "ambicioso", "paciente", "responsável", "reservado"], + tendencies: ["construir legado", "cumprir dever", "tempo longo", "autoridade", "realização"], + challenges: ["frieza emocional", "rigidez", "medo de falha", "workaholic"], + keywords: ["terra", "estrutura", "Capricórnio", "Makara", "tempo"], + phrases: [ + "O estruturador em você não precisa de mais conquistas; já é a montanha.", + "Dever e realização são dons; o afeto é o complemento.", + "O legado que você busca começa no presente vivido.", + "Seu arquétipo traz tempo; o instante é eterno.", + "Disciplina e paciência são seus aliados; a rigidez é o desafio.", + ], + }, + { + key: "visionario", + name: "Visionário", + shortTrait: "Liberdade, humanidade, futuro.", + personality: ["original", "humanitário", "rebelde", "intelectual", "desapegado"], + tendencies: ["inovar", "igualdade", "liberdade", "amizade", "futuro"], + challenges: ["frieza emocional", "teimosia", "utopia", "dificuldade em encarnar"], + keywords: ["ar", "liberdade", "Aquário", "Kumbha", "humanidade"], + phrases: [ + "O visionário em você não precisa mudar o mundo; já é a mudança.", + "Liberdade e humanidade são dons; o corpo é o complemento.", + "O futuro que você vislumbra começa no agora compartilhado.", + "Seu arquétipo traz ar; a terra o ancora.", + "Inovação e amizade são seus aliados; a desconexão é o desafio.", + ], + }, + { + key: "dissolvente", + name: "Dissolvente", + shortTrait: "Dissolução, devoção, entrega.", + personality: ["sensível", "devoto", "artístico", "escapista", "compassivo"], + tendencies: ["entrega", "devoção", "arte", "dissolver limites", "transcendência"], + challenges: ["fuga da realidade", "vitimização", "confusão", "dependência"], + keywords: ["água", "dissolução", "Peixes", "Mina", "entrega"], + phrases: [ + "O dissolvente em você não precisa escapar; já é o oceano.", + "Entrega e devoção são dons; os pés no chão são o complemento.", + "A transcendência que você busca está na presença.", + "Seu arquétipo traz água; as margens a contêm.", + "Compaixão e arte são seus aliados; a fuga é o desafio.", + ], + }, +]; + +export function getArchetypeTraits(key: ArchetypeKey): ArchetypeTraitsEntry | undefined { + return ARCHETYPE_TRAITS.find((t) => t.key === key); +} + +export function getRandomArchetypePhrase( + key: ArchetypeKey, + seed?: number, + preferKeywords?: string[] +): string { + const entry = getArchetypeTraits(key); + if (!entry) return "O arquétipo que você carrega é uma porta, não uma cela."; + const pool = entry.phrases; + const i = + preferKeywords?.length + ? pickIndexByKeywords(pool, (p) => p, preferKeywords, seed) + : seed !== undefined + ? Math.abs(Math.floor(seed)) % pool.length + : Math.floor(Math.random() * pool.length); + return pool[i] ?? pool[0]; +} + +export function getRandomArchetypeTendency(key: ArchetypeKey, seed?: number): string { + const entry = getArchetypeTraits(key); + if (!entry) return ""; + const pool = entry.tendencies; + const i = seed !== undefined ? Math.abs(Math.floor(seed)) % pool.length : Math.floor(Math.random() * pool.length); + return pool[i] ?? pool[0]; +} diff --git a/lib/knowledge/archetypes.ts b/lib/knowledge/archetypes.ts new file mode 100644 index 0000000..4d59598 --- /dev/null +++ b/lib/knowledge/archetypes.ts @@ -0,0 +1,158 @@ +/** + * Arquétipos e fórmulas para derivar arquétipo a partir do mapa (astrologia védica). + * O dicionário associa signos lunares (Rashi) e nakshatras a arquétipos interpretativos. + */ + +import type { ArchetypeEntry, RashiKey, NakshatraKey } from "./types"; + +/** Nomes em português dos 12 Rashis (signos sidereais) */ +export const RASHI_NAMES: Record = { + mesha: "Áries (Mesha)", + vrishabha: "Touro (Vrishabha)", + mithuna: "Gêmeos (Mithuna)", + karka: "Câncer (Karka)", + simha: "Leão (Simha)", + kanya: "Virgem (Kanya)", + tula: "Libra (Tula)", + vrischika: "Escorpião (Vrischika)", + dhanu: "Sagitário (Dhanu)", + makara: "Capricórnio (Makara)", + kumbha: "Aquário (Kumbha)", + mina: "Peixes (Mina)", +}; + +/** Os 27 Nakshatras (estações lunares) — lista de chaves para fórmulas */ +export const NAKSHATRA_KEYS: NakshatraKey[] = [ + "ashwini", "bharani", "krittika", "rohini", "mrigashira", "ardra", "punarvasu", + "pushya", "ashlesha", "magha", "purva-phalguni", "uttara-phalguni", "hasta", + "chitra", "swati", "vishakha", "anuradha", "jyestha", "mula", "purva-ashadha", + "uttara-ashadha", "shravana", "dhanishta", "shatabhisha", "purva-bhadra", + "uttara-bhadra", "revati", +]; + +export const ARCHETYPES: ArchetypeEntry[] = [ + { + key: "pioneiro", + name: "Pioneiro", + shortDescription: "Iniciativa, coragem, novo começo.", + moonRashi: ["mesha"], + nakshatraHints: ["ashwini", "krittika"], + formulationIds: ["f1", "f4", "f8"], + }, + { + key: "raiz", + name: "Raiz", + shortDescription: "Estabilidade, sensorial, Terra.", + moonRashi: ["vrishabha"], + nakshatraHints: ["rohini", "bharani"], + formulationIds: ["f2", "f9", "f11"], + }, + { + key: "mensageiro", + name: "Mensageiro", + shortDescription: "Comunicação, curiosidade, dualidade.", + moonRashi: ["mithuna"], + nakshatraHints: ["ardra", "punarvasu"], + formulationIds: ["f5", "f10", "f14"], + }, + { + key: "cuidador", + name: "Cuidador", + shortDescription: "Emoção, memória, nutrição.", + moonRashi: ["karka"], + nakshatraHints: ["pushya", "ashlesha"], + formulationIds: ["f2", "f7", "f11"], + }, + { + key: "soberano", + name: "Soberano", + shortDescription: "Centro, coração, reconhecimento.", + moonRashi: ["simha"], + nakshatraHints: ["magha", "purva-phalguni"], + formulationIds: ["f3", "f8", "f18"], + }, + { + key: "servidor", + name: "Servidor", + shortDescription: "Precisão, discernimento, serviço.", + moonRashi: ["kanya"], + nakshatraHints: ["hasta", "chitra"], + formulationIds: ["f6", "f14", "f16"], + }, + { + key: "harmonizador", + name: "Harmonizador", + shortDescription: "Equilíbrio, relação, justiça.", + moonRashi: ["tula"], + nakshatraHints: ["swati", "vishakha"], + formulationIds: ["f4", "f12", "f17"], + }, + { + key: "transformador", + name: "Transformador", + shortDescription: "Profundidade, morte-renascimento, poder.", + moonRashi: ["vrischika"], + nakshatraHints: ["anuradha", "jyestha"], + formulationIds: ["f4", "f15", "f18"], + }, + { + key: "sabedor", + name: "Sabedor", + shortDescription: "Sentido, filosofia, expansão.", + moonRashi: ["dhanu"], + nakshatraHints: ["mula", "purva-ashadha"], + formulationIds: ["f8", "f12", "f14"], + }, + { + key: "estruturador", + name: "Estruturador", + shortDescription: "Tempo, dever, realização.", + moonRashi: ["makara"], + nakshatraHints: ["uttara-ashadha", "shravana"], + formulationIds: ["f1", "f9", "f13"], + }, + { + key: "visionario", + name: "Visionário", + shortDescription: "Liberdade, humanidade, futuro.", + moonRashi: ["kumbha"], + nakshatraHints: ["dhanishta", "shatabhisha"], + formulationIds: ["f3", "f11", "f17"], + }, + { + key: "dissolvente", + name: "Dissolvente", + shortDescription: "Dissolução, devoção, entrega.", + moonRashi: ["mina"], + nakshatraHints: ["uttara-bhadra", "revati"], + formulationIds: ["f6", "f12", "f15"], + }, +]; + +/** Mapeia Rashi (signo lunar) → arquétipo(s) principal(is) */ +export function getArchetypeKeysByMoonRashi(moonRashi: RashiKey): string[] { + const entry = ARCHETYPES.find((a) => a.moonRashi?.includes(moonRashi)); + return entry ? [entry.key] : []; +} + +/** Mapeia Nakshatra → arquétipo(s) sugerido(s) */ +export function getArchetypeKeysByNakshatra(nakshatra: NakshatraKey): string[] { + return ARCHETYPES.filter((a) => + a.nakshatraHints?.some((n) => n === nakshatra) + ).map((a) => a.key); +} + +/** Dado um chart simplificado, retorna lista de arquétipos sugeridos */ +export function getArchetypeKeysFromChart(chart: { + moonRashi?: RashiKey; + moonNakshatra?: NakshatraKey; +}): string[] { + const keys = new Set(); + if (chart.moonRashi) { + getArchetypeKeysByMoonRashi(chart.moonRashi).forEach((k) => keys.add(k)); + } + if (chart.moonNakshatra) { + getArchetypeKeysByNakshatra(chart.moonNakshatra).forEach((k) => keys.add(k)); + } + return Array.from(keys); +} diff --git a/lib/knowledge/classicTexts.ts b/lib/knowledge/classicTexts.ts new file mode 100644 index 0000000..5bcc6e9 --- /dev/null +++ b/lib/knowledge/classicTexts.ts @@ -0,0 +1,157 @@ +/** + * Textos clássicos organizados por fonte — Upanishads, Bhagavad Gita, Yoga Sutras. + * Classificados por guna (sattva, rajas, tamas) e por arquétipo. + * Textos em português; termos originais substituídos por equivalentes abrangentes (sem sânscrito para o usuário). + */ + +import type { ArchetypeKey, Guna } from "./types"; +import { pickIndexByKeywords } from "./keywordMatch"; + +export type { Guna }; + +export type ClassicTextEntry = { + id: string; + source: "upanishad" | "bhagavad-gita" | "yoga-sutras" | "outro"; + work?: string; + text: string; + /** Qualidade predominante do trecho (clareza/equilíbrio, ação, repouso) */ + guna?: Guna; + /** Arquétipos com os quais o trecho combina (vazio = neutro) */ + archetypeHints?: ArchetypeKey[]; +}; + +const UPANISHADS: ClassicTextEntry[] = [ + { id: "up1", source: "upanishad", work: "Kena", guna: "sattva", text: "Aquilo que não pode ser pensado pela mente, mas pelo qual a mente pensa — conhece isso como o Absoluto." }, + { id: "up2", source: "upanishad", work: "Kena", guna: "sattva", text: "O que não pode ser visto pelo olho, mas pelo qual o olho vê — conhece isso como o Absoluto." }, + { id: "up3", source: "upanishad", work: "Isha", guna: "sattva", archetypeHints: ["dissolvente", "raiz"], text: "Isto é pleno; aquilo é pleno. Do pleno nasce o pleno. Tomando o pleno do pleno, o pleno permanece." }, + { id: "up4", source: "upanishad", work: "Isha", guna: "sattva", text: "No interior do ser reside o Ser; quem o vê alcança a paz." }, + { id: "up5", source: "upanishad", work: "Mundaka", guna: "rajas", archetypeHints: ["pioneiro", "sabedor"], text: "Como pássaro preso à corda, o homem preso ao corpo não vê a liberdade." }, + { id: "up6", source: "upanishad", work: "Katha", guna: "sattva", archetypeHints: ["servidor", "estruturador"], text: "Conhece o que em você observa como o condutor da carruagem; o corpo é a carruagem." }, + { id: "up7", source: "upanishad", work: "Katha", guna: "sattva", text: "O que está aqui está ali; o que está ali está aqui. Quem vê só a multiplicidade, da morte à morte vai." }, + { id: "up8", source: "upanishad", work: "Katha", guna: "rajas", archetypeHints: ["pioneiro", "transformador"], text: "Levanta-te, desperta, aproxima-te dos mestres e conhece. Afiada como o fio da navalha é a senda, diz o sábio." }, + { id: "up9", source: "upanishad", work: "Kena", guna: "sattva", text: "Não pelo discurso, não pela mente, não pelo olho se alcança o Ser. Só quem diz 'É Ele' o alcança." }, + { id: "up10", source: "upanishad", work: "Chandogya", guna: "sattva", archetypeHints: ["cuidador", "dissolvente"], text: "O Ser que reside no coração é menor que um grão de mostarda e maior que os céus." }, + { id: "up11", source: "upanishad", work: "Chandogya", guna: "sattva", text: "Isso és tu." }, + { id: "up12", source: "upanishad", work: "Mundaka", guna: "sattva", text: "Aquele que conhece o Absoluto torna-se o Absoluto." }, + { id: "up13", source: "upanishad", work: "Kena", guna: "sattva", text: "A paz que está além do entendimento — assim é quem conhece o Absoluto." }, + { id: "up14", source: "upanishad", work: "Taittiriya", guna: "sattva", text: "Conhecendo a bem-aventurança do Absoluto, o sábio não treme diante de nada." }, + { id: "up15", source: "upanishad", work: "Mandukya", guna: "tamas", archetypeHints: ["dissolvente", "raiz"], text: "O som que é o todo: o passado, o presente e o futuro." }, + { id: "up16", source: "upanishad", work: "Shvetashvatara", guna: "sattva", text: "Conhecendo o que habita no coração de todos os seres, o sábio entra na paz eterna." }, + { id: "up17", source: "upanishad", work: "Brihadaranyaka", guna: "rajas", archetypeHints: ["transformador", "visionario"], text: "Conduz-me do não-ser ao ser; conduz-me da escuridão à luz; conduz-me da morte à imortalidade." }, + { id: "up18", source: "upanishad", work: "Katha", guna: "sattva", text: "O Ser não nasce nem morre; não nasceu, não nascerá; eterno é." }, + { id: "up19", source: "upanishad", work: "Isha", guna: "sattva", archetypeHints: ["harmonizador", "dissolvente"], text: "Quem vê todos os seres no Ser e o Ser em todos os seres não mais se oculta." }, + { id: "up20", source: "upanishad", work: "Mundaka", guna: "sattva", text: "Duas aves, companheiras unidas, habitam a mesma árvore; uma come o fruto, a outra observa sem comer." }, +]; + +const BHAGAVAD_GITA: ClassicTextEntry[] = [ + { id: "bg1", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "O que é nascimento, o que é morte, o que é ser e não ser — conhece isso e age sem apego." }, + { id: "bg2", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", archetypeHints: ["harmonizador", "servidor"], text: "Aquele que vê inação na ação e ação na inação é sábio entre os homens." }, + { id: "bg3", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "rajas", archetypeHints: ["estruturador", "soberano"], text: "Faze o que deves fazer; melhor é o próprio dever, mesmo imperfeito, que o dever alheio bem feito." }, + { id: "bg4", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "O sábio não se perturba nem pela alegria nem pela dor; permanece igual em ambos." }, + { id: "bg5", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", archetypeHints: ["cuidador", "dissolvente"], text: "Quando a consciência se estabiliza, a tristeza dissolve-se." }, + { id: "bg6", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "Aquele que não odeia nem deseja, que age sem apego ao fruto, alcança a paz." }, + { id: "bg7", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "O corpo é efêmero; o que mora no corpo é eterno, inatingível." }, + { id: "bg8", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "rajas", archetypeHints: ["pioneiro", "sabedor"], text: "Oferece a mente e o coração; com a prática, chegarás." }, + { id: "bg9", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", archetypeHints: ["harmonizador", "dissolvente"], text: "Quem vê o Um em todos os seres e todos os seres no Um nunca se perde." }, + { id: "bg10", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "Entre os que dissipam as trevas, está a luz da consciência." }, + { id: "bg11", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "Quem age dedicando as ações ao Absoluto, abandonando o apego, não é manchado pela consequência dos atos." }, + { id: "bg12", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", text: "A paz vem para quem não odeia nenhum ser, quem é amigo e compassivo." }, + { id: "bg13", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "sattva", archetypeHints: ["sabedor", "servidor"], text: "O campo conhece o Campo; quem conhece isso conhece a libertação." }, + { id: "bg14", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "rajas", archetypeHints: ["pioneiro", "soberano"], text: "Quando a luz do conhecimento brilha em todos os sentidos, então se diz que o sol nasceu." }, + { id: "bg15", source: "bhagavad-gita", work: "Bhagavad Gita", guna: "rajas", text: "Corta a dúvida com a espada do conhecimento; refugia-te na prática; levanta-te." }, +]; + +const YOGA_SUTRAS: ClassicTextEntry[] = [ + { id: "ys1", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "Yoga é a cessação das flutuações da mente." }, + { id: "ys2", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "Quando a mente está em silêncio, o observador repousa em sua própria natureza." }, + { id: "ys3", source: "yoga-sutras", work: "Yoga Sutras", guna: "rajas", archetypeHints: ["estruturador", "servidor"], text: "A prática constante e o desapego são os meios." }, + { id: "ys4", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "A atitude em relação ao que pode ser evitado é: não é meu, não sou isso." }, + { id: "ys5", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "Quando a verdade é conhecida, a ação e seus frutos não mais prendem." }, + { id: "ys6", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", archetypeHints: ["raiz", "cuidador"], text: "O contentamento traz felicidade suprema." }, + { id: "ys7", source: "yoga-sutras", work: "Yoga Sutras", guna: "rajas", text: "A prática torna-se firme quando mantida por muito tempo, sem interrupção." }, + { id: "ys8", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "A respiração observada acalma a mente." }, + { id: "ys9", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "Quando a consciência se volta para dentro, surge o estado de fluir." }, + { id: "ys10", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "O sofrimento que ainda não veio pode ser evitado." }, + { id: "ys11", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "A causa do sofrimento é a identificação do observador com o observado." }, + { id: "ys12", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "O presente é o único tempo que existe; o passado e o futuro são projeções." }, + { id: "ys13", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "A clareza surge quando a mente reflete a luz do Ser." }, + { id: "ys14", source: "yoga-sutras", work: "Yoga Sutras", guna: "sattva", text: "O fruto da prática é a estabilidade e a leveza." }, + { id: "ys15", source: "yoga-sutras", work: "Yoga Sutras", guna: "rajas", archetypeHints: ["pioneiro", "visionario"], text: "Quando o esforço se torna alegria, a prática está enraizada." }, +]; + +const ALL_CLASSIC_TEXTS: ClassicTextEntry[] = [ + ...UPANISHADS, + ...BHAGAVAD_GITA, + ...YOGA_SUTRAS, +]; + +/** Mapeia arquétipo → guna predominante (para filtrar textos clássicos) */ +export const ARCHETYPE_TO_GUNA: Record = { + pioneiro: "rajas", + raiz: "tamas", + mensageiro: "rajas", + cuidador: "tamas", + soberano: "rajas", + servidor: "sattva", + harmonizador: "sattva", + transformador: "rajas", + sabedor: "sattva", + estruturador: "tamas", + visionario: "rajas", + dissolvente: "tamas", +}; + +export function getClassicTextById(id: string): ClassicTextEntry | undefined { + return ALL_CLASSIC_TEXTS.find((e) => e.id === id); +} + +export function getClassicTextsBySource(source: ClassicTextEntry["source"]): ClassicTextEntry[] { + return ALL_CLASSIC_TEXTS.filter((e) => e.source === source); +} + +/** Textos que combinam com o arquétipo (e opcionalmente com a guna do arquétipo) */ +export function getClassicTextsByArchetypeAndGuna( + archetypeKey: string, + preferGuna?: Guna +): ClassicTextEntry[] { + const guna = preferGuna ?? ARCHETYPE_TO_GUNA[archetypeKey]; + return ALL_CLASSIC_TEXTS.filter((e) => { + const matchGuna = !e.guna || e.guna === guna; + const matchArchetype = !e.archetypeHints?.length || e.archetypeHints.includes(archetypeKey); + return matchGuna && matchArchetype; + }); +} + +export function getRandomClassicText(seed?: number): ClassicTextEntry { + const i = seed !== undefined + ? Math.abs(Math.floor(seed)) % ALL_CLASSIC_TEXTS.length + : Math.floor(Math.random() * ALL_CLASSIC_TEXTS.length); + return ALL_CLASSIC_TEXTS[i] ?? ALL_CLASSIC_TEXTS[0]; +} + +/** Escolhe um texto clássico relacionado ao arquétipo (e à guna); fallback para lista total */ +export function getRandomClassicTextForArchetype( + archetypeKey: string, + seed?: number, + preferKeywords?: string[] +): ClassicTextEntry { + const pool = getClassicTextsByArchetypeAndGuna(archetypeKey); + const list = pool.length > 0 ? pool : ALL_CLASSIC_TEXTS; + const i = + preferKeywords?.length + ? pickIndexByKeywords(list, (e) => e.text, preferKeywords, seed) + : seed !== undefined + ? Math.abs(Math.floor(seed)) % list.length + : Math.floor(Math.random() * list.length); + return list[i] ?? ALL_CLASSIC_TEXTS[0]; +} + +export function getRandomClassicTextFromSource(source: ClassicTextEntry["source"], seed?: number): ClassicTextEntry { + const pool = getClassicTextsBySource(source); + const i = seed !== undefined + ? Math.abs(Math.floor(seed)) % pool.length + : Math.floor(Math.random() * pool.length); + return pool[i] ?? pool[0]; +} + +export { UPANISHADS, BHAGAVAD_GITA, YOGA_SUTRAS, ALL_CLASSIC_TEXTS }; diff --git a/lib/knowledge/formulations.ts b/lib/knowledge/formulations.ts new file mode 100644 index 0000000..9107cd9 --- /dev/null +++ b/lib/knowledge/formulations.ts @@ -0,0 +1,77 @@ +/** + * Formulações internas — frases curtas do oráculo (offline). + * Podem ser combinadas com arquétipos e afirmações das Upanishads. + */ + +import type { FormulationEntry } from "./types"; +import { pickIndexByKeywords } from "./keywordMatch"; + +export const FORMULATIONS: FormulationEntry[] = [ + { id: "f1", text: "O momento pede presença." }, + { id: "f2", text: "Respire e sinta o que já está aqui." }, + { id: "f3", text: "A luz do tempo não aponta para fora; ela revela o que pulsa em você." }, + { id: "f4", text: "O oráculo não adivinha — ele devolve." }, + { id: "f5", text: "Traga a pergunta ao corpo." }, + { id: "f6", text: "O que o silêncio responde?" }, + { id: "f7", text: "Cada fase lunar traz um tom; este instante pede escuta." }, + { id: "f8", text: "O mapa não define — sugere. Você é maior que qualquer aspecto." }, + { id: "f9", text: "Permita-se pausar." }, + { id: "f10", text: "O que em você quer ser ouvido?" }, + { id: "f11", text: "Respire e deixe o agora falar." }, + { id: "f12", text: "O que em você já sabe?" }, + { id: "f13", text: "O tempo pede pausa. Respire e tente novamente quando se sentir pronto." }, + { id: "f14", text: "Devolva a pergunta à consciência." }, + { id: "f15", text: "Samskaras e karmas são padrões; a presença os dissolve." }, + { id: "f16", text: "Sattva, rajas, tamas — o equilíbrio está no corpo que escuta." }, + { id: "f17", text: "O elemento que predomina neste instante pede reconhecimento, não luta." }, + { id: "f18", text: "O arquétipo que você carrega é uma porta, não uma cela." }, + { id: "f19", text: "O corpo não mente; ele guarda a verdade do instante." }, + { id: "f20", text: "A pergunta que você trouxe já é o primeiro passo da resposta." }, + { id: "f21", text: "Nenhum mapa diz quem você é; diz apenas o terreno que você atravessa." }, + { id: "f22", text: "O presente é o único tempo em que a mudança acontece." }, + { id: "f23", text: "O que você chama de problema pode ser o começo do caminho." }, + { id: "f24", text: "A consciência que observa já é maior que o que é observado." }, + { id: "f25", text: "Respirar com atenção é o primeiro ato de presença." }, + { id: "f26", text: "O número que te rege fala de tendência, não de destino." }, + { id: "f27", text: "Arquétipo e número são lentes; quem vê é você." }, + { id: "f28", text: "A tendência do seu número encontra equilíbrio na consciência." }, + { id: "f29", text: "Karma não é punição; é padrão que a atenção pode transformar." }, + { id: "f30", text: "O que em você já sabe não precisa de prova; precisa de escuta." }, + { id: "f31", text: "A lua no mapa fala de emoção; o sol, de direção; você é ambos e mais." }, + { id: "f32", text: "Nakshatra e Rashi são tons; a música é sua." }, + { id: "f33", text: "Entre o que o mapa sugere e o que você escolhe está a liberdade." }, + { id: "f34", text: "O silêncio não é vazio; é onde a resposta nasce." }, + { id: "f35", text: "Devolver a pergunta ao corpo é devolver a pergunta à vida." }, + { id: "f36", text: "O que você busca fora já está dentro, em outra forma." }, + { id: "f37", text: "Presença não é ausência de pensamento; é não se perder nele." }, + { id: "f38", text: "O instante não pede explicação; pede experiência." }, + { id: "f39", text: "Seu número regente revela tendências; a consciência revela escolha." }, + { id: "f40", text: "A sabedoria dos textos clássicos é a mesma que o corpo já conhece." }, +]; + +export function getFormulationById(id: string): FormulationEntry | undefined { + return FORMULATIONS.find((f) => f.id === id); +} + +export function getFormulationsByArchetype(archetypeKey: string): FormulationEntry[] { + return FORMULATIONS.filter( + (f) => !f.archetypeHints?.length || f.archetypeHints.includes(archetypeKey) + ); +} + +export function getRandomFormulation( + archetypeKey?: string, + seed?: number, + preferKeywords?: string[] +): FormulationEntry { + const pool = archetypeKey + ? getFormulationsByArchetype(archetypeKey) + : FORMULATIONS; + const i = + preferKeywords?.length + ? pickIndexByKeywords(pool, (e) => e.text, preferKeywords, seed) + : seed !== undefined + ? Math.abs(Math.floor(seed)) % pool.length + : Math.floor(Math.random() * pool.length); + return pool[i] ?? FORMULATIONS[0]; +} diff --git a/lib/knowledge/index.ts b/lib/knowledge/index.ts new file mode 100644 index 0000000..43a09fc --- /dev/null +++ b/lib/knowledge/index.ts @@ -0,0 +1,16 @@ +/** + * Base de conhecimento offline — dicionário interno do Darshan. + * Formulações, textos clássicos, arquétipos, traits, numerologia, astrologia védica. + */ + +export * from "./types"; +export * from "./formulations"; +export * from "./upanishads"; +export * from "./classicTexts"; +export * from "./archetypes"; +export * from "./archetypeTraits"; +export * from "./numerology"; +export * from "./jyotishMeanings"; +export * from "./moonPhases"; +export * from "./vedic"; +export * from "./keywordMatch"; diff --git a/lib/knowledge/jyotishMeanings.ts b/lib/knowledge/jyotishMeanings.ts new file mode 100644 index 0000000..d9bd18a --- /dev/null +++ b/lib/knowledge/jyotishMeanings.ts @@ -0,0 +1,760 @@ +/** + * Dicionário amplo de significados Jyotish — efeitos na consciência e na experiência humana. + * Por Rashi (signo lunar) e Nakshatra: temas de consciência, efeitos psicológicos, frases para o oráculo. + * Tudo em português; Rashis e Nakshatras populados. + */ + +import type { RashiKey, NakshatraKey } from "./types"; +import { pickIndexByKeywords } from "./keywordMatch"; + +export type RashiMeaningEntry = { + key: RashiKey; + namePt: string; + /** Temas que afetam a consciência (clareza, ação, repouso, etc.) */ + consciousnessThemes: string[]; + /** Efeitos na experiência psicológica e emocional */ + psychologicalEffects: string[]; + /** Frases curtas para usar na resposta do oráculo (sem sânscrito) */ + suggestedPhrases: string[]; +}; + +export type NakshatraMeaningEntry = { + key: NakshatraKey; + namePt: string; + consciousnessThemes: string[]; + psychologicalEffects: string[]; + suggestedPhrases: string[]; +}; + +/** Signos lunares (Rashi) — significados para consciência e resposta. Tudo em português. */ +export const RASHI_MEANINGS: RashiMeaningEntry[] = [ + { + key: "mesha", + namePt: "Áries", + consciousnessThemes: ["início", "coragem", "ação imediata", "novo começo", "impulso vital"], + psychologicalEffects: ["tendência a liderar", "impaciência com obstáculos", "necessidade de movimento", "identidade através da ação", "medo de ser ignorado"], + suggestedPhrases: [ + "O seu mapa sugere impulso e novo começo; a sabedoria está em saber quando pausar.", + "A energia do seu signo lunar fala de coragem; a pausa é o complemento.", + "Iniciativa e ação são forças; a escuta interior as equilibra.", + "O primeiro passo já está dado quando você respira; não precisa provar nada.", + "Coragem e movimento andam juntos; parar também é ato de coragem.", + "O novo começo que você busca habita neste instante.", + "Impulso vital pede direção; a direção nasce da quietude.", + "Liderar começa por escutar o que em você já sabe.", + "Ação sem pausa vira fuga; a pausa devolve o sentido.", + ], + }, + { + key: "vrishabha", + namePt: "Touro", + consciousnessThemes: ["estabilidade", "sensorial", "terra", "segurança", "beleza"], + psychologicalEffects: ["necessidade de segurança material e emocional", "apego ao que é estável", "prazer nos sentidos", "paciência e persistência", "medo de perda"], + suggestedPhrases: [ + "O seu mapa sugere raiz e estabilidade; a leveza é o complemento.", + "A terra no seu signo lunar pede paciência; a flexibilidade evita rigidez.", + "Estabilidade e beleza são dons; o desapego os libera.", + "Segurança que vem de dentro não depende do que você acumula.", + "O sensorial pede presença; o corpo é o primeiro templo.", + "Paciência e persistência constroem; a pressa desfaz.", + "A beleza que você busca já habita no que você é.", + "Raiz forte permite voar; sem raiz, o vento leva.", + "O prazer dos sentidos encontra paz na gratidão.", + ], + }, + { + key: "mithuna", + namePt: "Gêmeos", + consciousnessThemes: ["comunicação", "curiosidade", "dualidade", "movimento mental", "conexão"], + psychologicalEffects: ["mente ágil e versátil", "necessidade de variedade", "dificuldade em aprofundar", "comunicação como canal", "medo do tédio"], + suggestedPhrases: [ + "O seu mapa sugere comunicação e curiosidade; a profundidade é o complemento.", + "A dualidade do seu signo lunar pede centro; a pergunta e a resposta são uma.", + "Variedade e movimento são dons; o silêncio os completa.", + "A mente ágil encontra repouso quando o corpo é ouvido.", + "Conexão verdadeira nasce do que você não diz.", + "Curiosidade sem julgamento abre portas; a pressa fecha.", + "O que você comunica e o que você escuta são um só ato.", + "Movimento mental pede ancoragem; a respiração ancora.", + "Variedade é riqueza quando o centro não se perde.", + ], + }, + { + key: "karka", + namePt: "Câncer", + consciousnessThemes: ["emoção", "memória", "nutrição", "proteção", "casa"], + psychologicalEffects: ["sensibilidade emocional", "necessidade de cuidar e ser cuidado", "memória viva do passado", "proteção como expressão de amor", "medo de rejeição"], + suggestedPhrases: [ + "O seu mapa sugere emoção e cuidado; o autocuidado vem primeiro.", + "A lua no seu signo fala de memória; o presente é o lugar da cura.", + "Nutrição e proteção são dons; cuidar de si não é egoísmo.", + "A memória que dói pode ser visitada sem ser repetida.", + "Quem nutre merece ser nutrido; aceitar cuidado é dar cuidado.", + "Casa é onde o corpo se sente seguro; comece por aí.", + "Emoção não é obstáculo; é o rio que leva à clareza.", + "Proteger o que ama começa por proteger a própria paz.", + "O passado habita no corpo; o presente é o único lugar de mudança.", + ], + }, + { + key: "simha", + namePt: "Leão", + consciousnessThemes: ["centro", "coração", "reconhecimento", "criatividade", "luz"], + psychologicalEffects: ["necessidade de ser visto e reconhecido", "generosidade e orgulho", "criatividade como expressão", "lealdade", "medo da invisibilidade"], + suggestedPhrases: [ + "O seu mapa sugere coração e centro; a humildade é o complemento.", + "A luz do seu signo lunar pede reconhecimento; a luz interior não depende de aplausos.", + "Criatividade e lealdade são dons; o ego em excesso os ofusca.", + "Ser o centro não exige que todos olhem; basta você se ver.", + "Generosidade que vem do coração não cobra retorno.", + "O que você cria já tem valor antes de ser visto.", + "Reconhecimento externo é bônus; o interno é base.", + "Lealdade a si vem antes da lealdade ao papel.", + "Luz que brilha sem exigir aplausos ilumina mais longe.", + ], + }, + { + key: "kanya", + namePt: "Virgem", + consciousnessThemes: ["discernimento", "serviço", "pureza", "organização", "precisão"], + psychologicalEffects: ["necessidade de ordem e utilidade", "crítica interna forte", "serviço como sentido", "perfeccionismo", "medo do caos"], + suggestedPhrases: [ + "O seu mapa sugere discernimento e serviço; a aceitação é o complemento.", + "A precisão do seu signo lunar pede perfeição; o humano é imperfeito e válido.", + "Organização e utilidade são dons; a rigidez pode doer.", + "Servir com amor não exige ser perfeito; exige estar presente.", + "A ordem que acalma e a que aprisiona são diferentes; sinta a diferença.", + "Discernir é separar o essencial do acessório; você já sabe o essencial.", + "Utilidade não é só fazer; é também ser.", + "A crítica interna pode ser substituída por curiosidade.", + "Pureza não é ausência de erro; é intenção clara.", + ], + }, + { + key: "tula", + namePt: "Libra", + consciousnessThemes: ["equilíbrio", "relação", "justiça", "beleza", "parceria"], + psychologicalEffects: ["necessidade de harmonia e parceria", "indecisão entre opções", "estética e diplomacia", "evitar conflito", "medo da solidão"], + suggestedPhrases: [ + "O seu mapa sugere equilíbrio e relação; o centro próprio é o complemento.", + "A balança do seu signo lunar pede decisão; o equilíbrio começa em você.", + "Harmonia e justiça são dons; agradar a todos não é possível.", + "Decidir a partir de si não é egoísmo; é honestidade.", + "A parceria que sustenta nasce de dois centros, não de um que anula o outro.", + "Beleza e justiça pedem ação; a ação começa no corpo que escuta.", + "Indecisão pode ser sabedoria: esperar o momento certo.", + "Conflito evitado a qualquer custo vira conflito interno.", + "O equilíbrio que você busca já existe no instante em que você para.", + ], + }, + { + key: "vrischika", + namePt: "Escorpião", + consciousnessThemes: ["transformação", "profundidade", "poder", "morte e renascimento", "intimidade"], + psychologicalEffects: ["intensidade emocional", "necessidade de verdade oculta", "capacidade de regeneração", "ciúme e controle", "medo da traição"], + suggestedPhrases: [ + "O seu mapa sugere transformação e profundidade; a entrega é o complemento.", + "A crise no seu signo lunar pode ser portal; o controle excessivo trava.", + "Poder e intimidade são dons; a confiança os libera.", + "O que você esconde de si pode ser o que mais precisa de luz.", + "Regeneração não apaga o passado; integra-o.", + "Intensidade emocional é força quando não vira explosão.", + "A verdade oculta que você guarda pode ser dita primeiro a você mesmo.", + "Morte e renascimento são metáforas do corpo; permita-se mudar.", + "Ciúme e controle diminuem quando a confiança em si cresce.", + ], + }, + { + key: "dhanu", + namePt: "Sagitário", + consciousnessThemes: ["sentido", "expansão", "filosofia", "liberdade", "verdade"], + psychologicalEffects: ["necessidade de significado e aventura", "otimismo e honestidade", "expansão mental e física", "dogmatismo possível", "medo do aprisionamento"], + suggestedPhrases: [ + "O seu mapa sugere sentido e expansão; o aqui e agora é o complemento.", + "A seta do seu signo lunar aponta para o alto; o chão também é sagrado.", + "Liberdade e verdade são dons; a prática no corpo os encarna.", + "A filosofia que você busca está no gesto diário, não só no pensamento.", + "Aventura e significado andam juntos quando o corpo vai junto.", + "Otimismo sem raiz vira fuga; a raiz é o instante presente.", + "Expansão mental encontra limite no corpo; honre esse limite.", + "Verdade que liberta não precisa ser dogmática.", + "O sentido que você procura já habita na pergunta que você faz.", + ], + }, + { + key: "makara", + namePt: "Capricórnio", + consciousnessThemes: ["estrutura", "tempo", "dever", "realização", "autoridade"], + psychologicalEffects: ["necessidade de construir e realizar", "disciplina e paciência", "responsabilidade forte", "frieza emocional possível", "medo do fracasso"], + suggestedPhrases: [ + "O seu mapa sugere estrutura e dever; o afeto é o complemento.", + "A montanha do seu signo lunar pede paciência; o instante também conta.", + "Realização e disciplina são dons; o corpo e o coração merecem cuidado.", + "Construir legado começa por cuidar de quem constrói.", + "Tempo longo e tempo curto coexistem; o agora é o único que você tem.", + "Responsabilidade sem autocuidado vira peso; o cuidado libera.", + "Autoridade verdadeira não precisa provar; basta ser.", + "O dever que esgota não é destino; é padrão que pode mudar.", + "Fracasso e sucesso são passagens; o que fica é o que você aprende.", + ], + }, + { + key: "kumbha", + namePt: "Aquário", + consciousnessThemes: ["liberdade", "humanidade", "inovação", "futuro", "desapego"], + psychologicalEffects: ["necessidade de liberdade e igualdade", "visão de futuro", "originalidade e rebeldia", "frieza emocional possível", "medo da convenção"], + suggestedPhrases: [ + "O seu mapa sugere liberdade e humanidade; o corpo é o complemento.", + "O futuro no seu signo lunar pede encarnação; a mudança começa em você.", + "Inovação e amizade são dons; a conexão humana aquece.", + "Visão de futuro que ignora o corpo vira fuga.", + "Igualdade e liberdade começam em como você se trata.", + "Originalidade não exige romper com tudo; pode ser um gesto novo no cotidiano.", + "O que você idealiza para o mundo pode começar no seu entorno.", + "Desapego das convenções não precisa ser frieza; pode ser amor ampliado.", + "Humanidade que você defende inclui a sua; cuide de si.", + ], + }, + { + key: "mina", + namePt: "Peixes", + consciousnessThemes: ["dissolução", "devoção", "entrega", "transcendência", "compaixão"], + psychologicalEffects: ["sensibilidade e compaixão", "necessidade de transcendência", "confusão entre eu e outro", "arte e devoção", "medo da realidade dura"], + suggestedPhrases: [ + "O seu mapa sugere entrega e dissolução; os pés no chão são o complemento.", + "O oceano do seu signo lunar pede margens; a presença é a margem.", + "Compaixão e arte são dons; a fuga da realidade pode doer.", + "Transcendência que ignora o corpo adia a paz.", + "Confusão entre eu e outro se dissolve quando você se enxerga com clareza.", + "Devoção e compaixão crescem quando você se inclui no círculo.", + "A realidade dura também é passagem; a presença a atravessa.", + "Arte que nasce da entrega não precisa ser perfeita.", + "Dissolver limites não é perder-se; é encontrar-se no que é maior.", + ], + }, +]; + +/** Nakshatras (27 estações lunares) — significados para consciência e resposta. Tudo em português. */ +export const NAKSHATRA_MEANINGS: NakshatraMeaningEntry[] = [ + { + key: "ashwini", + namePt: "Estrela do Cavalo", + consciousnessThemes: ["início rápido", "cura", "movimento"], + psychologicalEffects: ["impulso de começar", "capacidade de curar", "inquietude"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de novo começo; a pausa também cura.", + "A energia do seu mapa pede movimento; a cura começa quando você para.", + "Início rápido e cura são dons; a paciência complementa.", + "Impulso de começar pede direção; a direção nasce da escuta.", + "Quem cura também precisa ser curado; permita-se receber.", + "Movimento sem pausa vira inquietude; a pausa devolve o foco.", + "O primeiro passo já está dado quando você respira.", + "Cura que vem de dentro não depende de remédio alheio.", + "Inquietude é sinal de vida; a quietude é o lugar de resposta.", + ], + }, + { + key: "bharani", + namePt: "A Portadora", + consciousnessThemes: ["carregar", "transformação", "renúncia"], + psychologicalEffects: ["capacidade de carregar peso", "transformação através da perda", "intensidade"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de carregar e soltar; a entrega libera.", + "A transformação no seu mapa passa pela renúncia; soltar é ganhar.", + "Carregar e transformar são dons; não carregue sozinho.", + "A portadora que não deposita o peso cansa; deposite em você mesmo às vezes.", + "Intensidade que transforma não precisa ser dramática.", + "Perda e ganho são dois lados do mesmo movimento.", + "Quem carrega peso merece descanso; o descanso não é fraqueza.", + "Renúncia que libera não é sacrifício vazio; é escolha clara.", + "Transformação através da perda é portal; a compaixão por si abre a porta.", + ], + }, + { + key: "krittika", + namePt: "Chama", + consciousnessThemes: ["purificação", "corte", "claridade", "início pelo fogo", "discernimento"], + psychologicalEffects: ["necessidade de limpar o que não serve", "impulsividade e honestidade cortante", "capacidade de iluminar", "intolerância ao que é obscuro", "medo da sujeira moral"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de chama e purificação; a compaixão suaviza o corte.", + "A claridade do seu mapa pede discernimento; o fogo que limpa não precisa queimar o outro.", + "Início pelo fogo é dom; a pausa evita o incêndio.", + "Purificação que queima sem amor deixa cicatriz; a compaixão acalma.", + "Corte que separa o essencial do supérfluo é dom; use com cuidado.", + "Honestidade cortante pode ser dita com suavidade.", + "O que você limpa em si reflete no que você vê no mundo.", + "Discernimento não é julgamento; é clareza que libera.", + "Fogo que ilumina também aquece; não precisa destruir.", + ], + }, + { + key: "rohini", + namePt: "Estrela Vermelha", + consciousnessThemes: ["fertilidade", "beleza", "crescimento", "atração", "abundância"], + psychologicalEffects: ["necessidade de beleza e harmonia", "capacidade de nutrir e atrair", "apego ao que floresce", "sensualidade e charme", "medo da esterilidade"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de fertilidade e beleza; a entrega ao ciclo completa.", + "A abundância do seu mapa pede gratidão; o que floresce também passa.", + "Crescimento e atração são dons; o desapego permite renascer.", + "Beleza que atrai sem apegar é liberdade.", + "Fertilidade não é só gerar; é nutrir o que já nasceu.", + "Sensualidade e charme são canais; a consciência os guia.", + "O que floresce em você merece ser visto por você primeiro.", + "Abundância interior não depende da externa; cultive-a.", + "Ciclo de florescimento e queda é natural; resistir à queda trava o próximo florescer.", + ], + }, + { + key: "mrigashira", + namePt: "Cabeça do Cervo", + consciousnessThemes: ["busca", "curiosidade", "suavidade", "exploração", "gentileza"], + psychologicalEffects: ["necessidade de buscar e descobrir", "mente inquieta e gentil", "dificuldade em fixar", "encanto pela jornada", "medo de ficar preso"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de busca e gentileza; o repouso também é caminho.", + "A curiosidade do seu mapa pede direção; nem toda busca precisa de destino.", + "Exploração e suavidade são dons; o centro evita a dispersão.", + "Mente inquieta encontra paz quando o corpo é o ancoradouro.", + "Gentileza na jornada não é lentidão; é respeito pelo processo.", + "Descobrir e buscar são prazeres; fixar é escolha, não obrigação.", + "Encanto pela jornada não precisa de meta final para ter sentido.", + "Suavidade e exploração andam juntos quando você não se julga.", + "O centro que evita dispersão não prende; sustenta.", + ], + }, + { + key: "ardra", + namePt: "Úmida", + consciousnessThemes: ["tempestade", "transformação pela dor", "clareza após a chuva", "ruptura", "renovação"], + psychologicalEffects: ["intensidade emocional e mental", "capacidade de atravessar crises", "tendência à tormenta interior", "honestidade que fere", "medo da fragilidade"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de tempestade e renovação; depois da chuva vem a clareza.", + "A transformação no seu mapa passa pela dor; a compaixão por si acalma.", + "Ruptura e renovação são dons; a calma não é fraqueza.", + "Intensidade emocional é força quando não vira autodestruição.", + "Atravessar crise é habilidade; pedir apoio não é falha.", + "Honestidade que fere pode ser dita com timing e amor.", + "Tormenta interior pede escuta; a escuta acalma.", + "Fragilidade reconhecida é o primeiro passo da resistência.", + "Renovação após a chuva não apaga o que houve; integra.", + ], + }, + { + key: "punarvasu", + namePt: "Boa Estrela", + consciousnessThemes: ["retorno", "renovação", "esperança", "segunda chance", "nutrição"], + psychologicalEffects: ["capacidade de recomeçar", "otimismo e fé no amanhã", "necessidade de lar e raiz", "adaptação após perda", "medo de não ser acolhido"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de retorno e boa estrela; cada recomeço é sagrado.", + "A renovação no seu mapa pede esperança; a segunda chance começa em você.", + "Retorno e nutrição são dons; permita-se ser nutrido.", + "Recomeçar não é apagar o passado; é escolher de novo a partir dele.", + "Boa estrela não é sorte cega; é abertura ao que vem.", + "Lar e raiz são necessidades; honre-as sem fechar-se.", + "Adaptação após perda é força; a dor não precisa ser escondida.", + "Segunda chance começa quando você se perdoa.", + "Esperança que nutre não ignora a dificuldade; a atravessa.", + ], + }, + { + key: "pushya", + namePt: "Nutritiva", + consciousnessThemes: ["nutrição", "cuidado", "proteção", "abundância interior", "sacralidade"], + psychologicalEffects: ["necessidade de nutrir e ser nutrido", "devoção e cuidado com o que ama", "generosidade e proteção", "apego ao papel de cuidador", "medo da escassez"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de nutrição e cuidado; quem nutre também precisa ser nutrido.", + "A abundância no seu mapa começa por dentro; o autocuidado não é egoísmo.", + "Cuidado e proteção são dons; a entrega equilibra.", + "Sacralidade do que você nutre não exige sacrifício de si.", + "Devoção e cuidado andam juntos quando você se inclui no círculo.", + "Generosidade que esgota não sustenta; recarregue-se.", + "Proteção que aprisiona vira peso; proteja com espaço.", + "Abundância interior multiplica quando compartilhada sem apego.", + "Medo da escassez diminui quando você confia no ciclo.", + ], + }, + { + key: "ashlesha", + namePt: "Abraço", + consciousnessThemes: ["abraço", "proteção que envolve", "mistério", "profundidade emocional", "transformação"], + psychologicalEffects: ["intensidade na relação", "necessidade de união profunda", "desconfiança e proteção", "capacidade de regeneração", "medo de ser traído"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de abraço e profundidade; a confiança libera o abraço.", + "A proteção no seu mapa pode virar prisão; soltar também é amar.", + "Profundidade e transformação são dons; a leveza complementa.", + "União profunda nasce de dois inteiros; não de um que engole o outro.", + "Desconfiança que protege pode ser suavizada com comunicação.", + "Regeneração após crise é dom; permita-se tempo.", + "Mistério na relação pode coexistir com transparência.", + "Abraço que sufoca não é amor; é medo.", + "Profundidade emocional pede expressão; guardar demais adoece.", + ], + }, + { + key: "magha", + namePt: "Grande", + consciousnessThemes: ["grandeza", "ancestralidade", "honra", "luz", "reconhecimento"], + psychologicalEffects: ["necessidade de honra e reconhecimento", "orgulho da linhagem e do próprio valor", "generosidade e autoridade", "medo da humilhação", "desejo de deixar legado"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de grandeza e honra; a humildade não diminui.", + "A luz no seu mapa pede reconhecimento; a luz interior não depende de aplausos.", + "Ancestralidade e legado são dons; o presente também é sagrado.", + "Orgulho da linhagem pode conviver com gratidão pelo que você constrói hoje.", + "Autoridade que serve não oprime; inspire.", + "Medo da humilhação diminui quando você não depende do julgamento alheio.", + "Deixar legado começa por viver bem o instante.", + "Grandeza que exige aplauso é frágil; a que vem de dentro sustenta.", + "Honra e reconhecimento são desejos legítimos; não precisam definir seu valor.", + ], + }, + { + key: "purva-phalguni", + namePt: "Primeira Vermelha", + consciousnessThemes: ["prazer", "criatividade", "união", "beleza", "celebração"], + psychologicalEffects: ["necessidade de prazer e beleza", "criatividade e charme", "desejo de parceria e reconhecimento", "tendência ao excesso", "medo da rejeição"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de prazer e criatividade; o equilíbrio evita o excesso.", + "A beleza no seu mapa pede celebração; celebrar a si é válido.", + "União e criatividade são dons; a solidão também nutre.", + "Prazer que não fere a si nem ao outro é liberdade.", + "Criatividade que nasce do corpo não precisa ser obra-prima.", + "Desejo de parceria pode começar por parceria consigo.", + "Tendência ao excesso pede consciência; não culpa.", + "Rejeição que dói não define seu valor; define apenas um momento.", + "Celebração do que você é não depende de plateia.", + ], + }, + { + key: "uttara-phalguni", + namePt: "Segunda Vermelha", + consciousnessThemes: ["parceria", "equilíbrio", "reciprocidade", "amizade", "aliança"], + psychologicalEffects: ["necessidade de parceria equilibrada", "capacidade de compartilhar poder", "lealdade e diplomacia", "medo de ficar só", "desejo de reconhecimento mútuo"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de parceria e reciprocidade; o centro próprio sustenta a aliança.", + "A aliança no seu mapa pede equilíbrio; dar e receber são um só.", + "Parceria e amizade são dons; a independência interior fortalece.", + "Compartilhar poder não é perder poder; é multiplicar.", + "Lealdade e diplomacia andam juntos quando a verdade é dita com cuidado.", + "Medo de ficar só pode ser suavizado pela relação consigo.", + "Reconhecimento mútuo nasce de dois que se veem.", + "Parceria equilibrada não exige que um anule o outro.", + "Amizade que sustenta não cobra; oferece.", + ], + }, + { + key: "hasta", + namePt: "Mão", + consciousnessThemes: ["habilidade", "fazer", "destreza", "cura pelas mãos", "concretização"], + psychologicalEffects: ["necessidade de criar com as mãos", "capacidade de concretizar ideias", "criticidade e perfeccionismo", "serviço através do fazer", "medo de não ser útil"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de mão e habilidade; o fazer a partir do ser.", + "A destreza no seu mapa pede concretização; a pausa também é produtiva.", + "Fazer e curar são dons; permita-se receber.", + "Concretizar ideias pede tempo; não exija de si o instantâneo.", + "Serviço através do fazer ganha sentido quando o ser está presente.", + "Medo de não ser útil pode ser questionado; você já é.", + "Mão que cria também merece repouso.", + "Curar pelas mãos é dom; receber cuidado também.", + "Habilidade que nasce da prática não precisa ser perfeita no primeiro gesto.", + ], + }, + { + key: "chitra", + namePt: "Brilhante", + consciousnessThemes: ["beleza", "ordem", "construção", "brilho", "forma"], + psychologicalEffects: ["necessidade de ordem e beleza", "capacidade de construir e embelezar", "crítica ao que é feio ou caótico", "perfeccionismo estético", "medo do caos"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de brilho e forma; a imperfeição também é humana.", + "A beleza no seu mapa pede construção; a aceitação do que é evita sofrimento.", + "Ordem e brilho são dons; a flexibilidade evita rigidez.", + "Construir e embelezar são atos de amor; não de controle.", + "Crítica ao caos pode ser suavizada pela compaixão ao que está em desordem.", + "Perfeccionismo estético pode conviver com a aceitação do imperfeito.", + "Medo do caos diminui quando você confia no processo.", + "Forma que libera é diferente da forma que aprisiona.", + "Brilho que vem de dentro não depende de cenário perfeito.", + ], + }, + { + key: "swati", + namePt: "Independente", + consciousnessThemes: ["liberdade", "movimento", "independência", "vento", "mudança"], + psychologicalEffects: ["necessidade de liberdade e espaço", "adaptação e movimento", "dificuldade em comprometer-se", "diplomacia e leveza", "medo de ser preso"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de liberdade e vento; a raiz permite voar.", + "A independência no seu mapa pede movimento; o compromisso pode ser livre.", + "Liberdade e mudança são dons; o centro evita a dispersão.", + "Espaço e liberdade são necessidades; honre-as sem ferir o outro.", + "Adaptação não é perda de identidade; é flexibilidade.", + "Comprometer-se a partir de escolha é liberdade; por obrigação, é prisão.", + "Leveza e diplomacia andam juntos quando você não carrega o que não é seu.", + "Medo de ser preso pode ser aliviado pela confiança em si.", + "Vento que leva também traz; permita-se ser trazido.", + ], + }, + { + key: "vishakha", + namePt: "Ramo", + consciousnessThemes: ["determinação", "objetivo", "vitória", "persistência", "fruto"], + psychologicalEffects: ["necessidade de vencer obstáculos", "determinação e competitividade", "capacidade de esperar o fruto", "tendência à impaciência", "medo de falhar"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de ramo e objetivo; o caminho também é o fruto.", + "A determinação no seu mapa pede persistência; a paciência complementa.", + "Vitória e fruto são dons; a entrega ao processo libera.", + "Vencer obstáculos não exige vencer a si mesmo.", + "Competitividade saudável não precisa anular o outro.", + "Esperar o fruto com paciência não é passividade; é sabedoria.", + "Medo de falhar diminui quando o processo vale mais que o resultado.", + "Determinação que esgota pede repouso; repousar não é desistir.", + "O fruto que você busca pode estar no gesto que você faz hoje.", + ], + }, + { + key: "anuradha", + namePt: "Sucesso", + consciousnessThemes: ["sucesso", "amizade", "devoção", "aliança", "fidelidade"], + psychologicalEffects: ["necessidade de sucesso através da relação", "devoção e lealdade", "capacidade de cultivar amizades profundas", "medo da traição", "desejo de reconhecimento"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de sucesso e amizade; a fidelidade a si vem primeiro.", + "A devoção no seu mapa pede aliança; não só aos outros, também a você.", + "Sucesso e fidelidade são dons; o autocuidado sustenta.", + "Sucesso através da relação não exige anulação de si.", + "Devoção e lealdade crescem quando você se inclui no pacto.", + "Amizades profundas nascem de presença; não de performance.", + "Medo da traição pode ser suavizado pela confiança em si.", + "Reconhecimento que vem dos outros é bônus; o seu próprio sustenta.", + "Cultivar amizade consigo é a primeira aliança.", + ], + }, + { + key: "jyestha", + namePt: "A Maior", + consciousnessThemes: ["poder", "proteção", "mistério", "autoridade", "transformação"], + psychologicalEffects: ["necessidade de poder e proteção", "intensidade e ciúme possíveis", "capacidade de liderar em crise", "medo de ser superado", "desejo de ser o primeiro"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de poder e proteção; a humildade não diminui.", + "A autoridade no seu mapa pede serviço; o poder verdadeiro não oprime.", + "Proteção e transformação são dons; a confiança libera.", + "Poder que protege não precisa dominar.", + "Intensidade e ciúme podem ser transmutados em presença e cuidado.", + "Liderar em crise é dom; pedir apoio não o anula.", + "Medo de ser superado diminui quando você não compete consigo.", + "Ser o primeiro não é obrigação; é escolha quando faz sentido.", + "Mistério que protege não precisa ser muralha.", + ], + }, + { + key: "mula", + namePt: "Raiz", + consciousnessThemes: ["raiz", "origem", "investigação", "ruptura para renascer", "verdade oculta"], + psychologicalEffects: ["necessidade de ir à raiz", "capacidade de questionar tudo", "crise como portal", "honestidade radical", "medo do vazio"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de raiz e origem; a investigação leva à verdade.", + "A ruptura no seu mapa pode ser renascimento; a raiz que corta também sustenta.", + "Raiz e verdade são dons; a compaixão por si acalma.", + "Ir à raiz não exige destruir; pode ser escavar com cuidado.", + "Questionar tudo pode conviver com confiar em algo.", + "Crise como portal pede coragem; não heroísmo solitário.", + "Honestidade radical não precisa ser brutal.", + "Medo do vazio diminui quando você habita o instante.", + "Verdade oculta que emerge traz alívio; permita-se revelar.", + ], + }, + { + key: "purva-ashadha", + namePt: "Primeira Invencível", + consciousnessThemes: ["invencibilidade", "vitória", "vontade", "não desistir", "fogo"], + psychologicalEffects: ["necessidade de vencer e não desistir", "vontade forte e competitividade", "capacidade de inspirar", "impaciência com derrota", "medo de parecer fraco"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de invencibilidade; a entrega também é força.", + "A vitória no seu mapa pode ser interior; o que não desiste de si.", + "Vontade e fogo são dons; a pausa recarrega.", + "Vencer e não desistir podem incluir desistir do que não serve.", + "Competitividade que inspira não precisa esmagar.", + "Impaciência com derrota pode ser transmutada em aprendizado.", + "Medo de parecer fraco diminui quando a vulnerabilidade é aceita.", + "Fogo que impulsiona não precisa queimar; pode aquecer.", + "Invencibilidade verdadeira às vezes é saber quando recuar.", + ], + }, + { + key: "uttara-ashadha", + namePt: "Segunda Invencível", + consciousnessThemes: ["vitória duradoura", "estabilidade no triunfo", "paciência", "legado", "firmeza"], + psychologicalEffects: ["necessidade de vitória que permaneça", "paciência e persistência", "capacidade de construir legado", "medo de perder o que conquistou", "desejo de reconhecimento eterno"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de vitória duradoura; o instante também é eterno.", + "O legado no seu mapa começa no agora; cada passo conta.", + "Firmeza e paciência são dons; a flexibilidade evita quebra.", + "Vitória que permanece não depende só de você; aceite o que não controla.", + "Persistência e paciência andam juntos quando o objetivo vale a pena.", + "Medo de perder o conquistado pode ser suavizado pela gratidão.", + "Reconhecimento eterno é ilusão; o que fica é o que você viveu.", + "Estabilidade no triunfo pede humildade; a montanha também erode.", + "Legado que sustenta não é monumento; é gesto repetido com amor.", + ], + }, + { + key: "shravana", + namePt: "Escuta", + consciousnessThemes: ["escuta", "aprendizado", "recepção", "sabedoria que ouve", "comunicação"], + psychologicalEffects: ["capacidade de ouvir e aprender", "necessidade de ser ouvido", "sabedoria através da escuta", "medo do silêncio ou da surdez", "desejo de transmitir"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de escuta; a sabedoria que ouve também se ouve.", + "A escuta no seu mapa pede receptividade; o silêncio fala.", + "Escuta e aprendizado são dons; a voz interior merece atenção.", + "Ouvir e aprender são atos de humildade; não de inferioridade.", + "Necessidade de ser ouvido é legítima; comunique-a com gentileza.", + "Sabedoria através da escuta não exige silêncio absoluto; exige presença.", + "Medo do silêncio pode ser explorado; o que ele esconde?", + "Transmitir o que você aprendeu é dom; receber também.", + "A voz interior que merece atenção às vezes sussurra; aproxime-se.", + ], + }, + { + key: "dhanishta", + namePt: "Riqueza", + consciousnessThemes: ["riqueza", "abundância", "ritmo", "música", "prosperidade"], + psychologicalEffects: ["necessidade de prosperidade material e espiritual", "ritmo e capacidade de sincronizar", "generosidade quando pleno", "medo da pobreza", "desejo de compartilhar riqueza"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de riqueza e ritmo; a abundância começa por dentro.", + "A prosperidade no seu mapa pede compartilhar; dar e receber são um.", + "Ritmo e abundância são dons; a gratidão multiplica.", + "Prosperidade material e espiritual podem coexistir; uma não anula a outra.", + "Ritmo e sincronia nascem da escuta; do corpo e do outro.", + "Generosidade quando pleno não exige esvaziar-se.", + "Medo da pobreza diminui quando você confia no ciclo de dar e receber.", + "Compartilhar riqueza é ato de liberdade; não de obrigação.", + "Música e ritmo são metáforas do fluxo; permita-se fluir.", + ], + }, + { + key: "shatabhisha", + namePt: "Cem Curas", + consciousnessThemes: ["cura", "mistério", "solidão", "renovação", "dissolução"], + psychologicalEffects: ["capacidade de curar e ser curado", "necessidade de solidão criativa", "mistério e introspecção", "medo da exposição", "desejo de transformar"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de cura e mistério; a solidão também cura.", + "As cem curas no seu mapa começam em você; permita-se ser curado.", + "Renovação e dissolução são dons; a presença acalma.", + "Curar e ser curado são dois lados do mesmo movimento.", + "Solidão criativa não é isolamento; é encontro consigo.", + "Mistério e introspecção podem conviver com abertura ao outro.", + "Medo da exposição pode ser respeitado; não precisa ser vencido à força.", + "Transformar é dom; ser transformado também.", + "Dissolução que renova não apaga; integra.", + ], + }, + { + key: "purva-bhadra", + namePt: "Primeira Auspiciosa", + consciousnessThemes: ["auspício", "início sagrado", "fogo transformador", "purificação", "novo ciclo"], + psychologicalEffects: ["necessidade de começos auspiciosos", "intensidade e purificação", "capacidade de quebrar padrões", "impulsividade possível", "medo do que é impuro"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de auspício e novo ciclo; cada instante pode ser sagrado.", + "O início no seu mapa pede purificação; a compaixão por si é o primeiro passo.", + "Fogo transformador e novo ciclo são dons; a pausa evita queima.", + "Começos auspiciosos não exigem condições perfeitas; exigem intenção clara.", + "Intensidade e purificação andam juntos quando o amor está presente.", + "Quebrar padrões pode ser gradual; não só explosivo.", + "Impulsividade possível pode ser canalizada; não reprimida.", + "Medo do impuro pode ser suavizado pela aceitação do humano.", + "Novo ciclo que renasce não nega o que passou; honra-o e segue.", + ], + }, + { + key: "uttara-bhadra", + namePt: "Segunda Auspiciosa", + consciousnessThemes: ["estabilidade auspiciosa", "fundação", "serpente que sustenta", "profundidade", "transformação lenta"], + psychologicalEffects: ["necessidade de base estável", "capacidade de sustentar transformações", "profundidade emocional", "medo do colapso", "desejo de legado estável"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de fundação auspiciosa; a paciência constrói.", + "A profundidade no seu mapa pede raiz; a transformação lenta também transforma.", + "Estabilidade e profundidade são dons; a flexibilidade evita rigidez.", + "Fundação que sustenta não precisa ser rígida; pode ser viva.", + "Sustentar transformações é habilidade; não carregue sozinho.", + "Profundidade emocional pede expressão; guardar demais adoece.", + "Medo do colapso diminui quando você confia no que construiu.", + "Legado estável não é imutável; é resiliente.", + "Transformação lenta também transforma; não despreze o tempo.", + ], + }, + { + key: "revati", + namePt: "Próspera", + consciousnessThemes: ["prosperidade", "nutrição final", "entrega", "completude", "transição"], + psychologicalEffects: ["necessidade de completar ciclos", "capacidade de nutrir até o fim", "entrega e generosidade", "medo de perder o que foi construído", "desejo de deixar bênção"], + suggestedPhrases: [ + "O tom da sua estação lunar fala de prosperidade e completude; a entrega é a última nutrição.", + "A transição no seu mapa pede entrega; o que termina também começa.", + "Prosperidade e completude são dons; permita-se receber o ciclo.", + "Completar ciclos é ato de amor; não de obrigação.", + "Nutrir até o fim não exige esgotar-se; nutra-se também.", + "Entrega e generosidade andam juntos quando você se inclui no dar.", + "Medo de perder o construído pode ser suavizado pela gratidão pelo que foi.", + "Deixar bênção não exige grandeza; exige presença.", + "Transição que honra o que passou abre espaço para o que vem.", + ], + }, +]; + +function getRashiMeaning(rashi: RashiKey): RashiMeaningEntry | undefined { + return RASHI_MEANINGS.find((e) => e.key === rashi); +} + +function getNakshatraMeaning(nakshatra: NakshatraKey): NakshatraMeaningEntry | undefined { + return NAKSHATRA_MEANINGS.find((e) => e.key === nakshatra); +} + +/** Retorna uma frase do dicionário de significados Jyotish para o chart (Rashi ou Nakshatra); em português. */ +export function getJyotishPhraseForChart( + chart: { moonRashi?: RashiKey; moonNakshatra?: NakshatraKey }, + seed?: number, + preferKeywords?: string[] +): string | null { + const rashiEntry = chart.moonRashi ? getRashiMeaning(chart.moonRashi) : undefined; + const nakshatraEntry = chart.moonNakshatra ? getNakshatraMeaning(chart.moonNakshatra) : undefined; + + const rashiPhrases = rashiEntry?.suggestedPhrases ?? []; + const nakshatraPhrases = nakshatraEntry?.suggestedPhrases ?? []; + const combined = [...rashiPhrases, ...nakshatraPhrases].filter(Boolean); + if (combined.length === 0) return null; + + const i = + preferKeywords?.length + ? pickIndexByKeywords(combined, (p) => p, preferKeywords, seed) + : seed !== undefined + ? Math.abs(Math.floor(seed)) % combined.length + : Math.floor(Math.random() * combined.length); + return combined[i] ?? null; +} + +/** + * Interpretação Jyotish para leitura (mapa pessoal) — usa temas de consciência e + * efeitos psicológicos em forma narrativa, sem as frases prontas do oráculo. + */ +export function getJyotishReadingInterpretation( + chart: { moonRashi?: RashiKey; moonNakshatra?: NakshatraKey }, + _seed?: number +): string | null { + const rashiEntry = chart.moonRashi ? getRashiMeaning(chart.moonRashi) : undefined; + const nakshatraEntry = chart.moonNakshatra ? getNakshatraMeaning(chart.moonNakshatra) : undefined; + if (!rashiEntry && !nakshatraEntry) return null; + + const parts: string[] = []; + + if (rashiEntry) { + const themes = rashiEntry.consciousnessThemes?.slice(0, 4).join(", ") ?? ""; + const effects = rashiEntry.psychologicalEffects?.slice(0, 3).join("; ") ?? ""; + if (themes) parts.push(`O signo lunar ${rashiEntry.namePt} traz à tona temas como ${themes}.`); + if (effects) parts.push(`Na experiência psicológica isso pode se manifestar como ${effects}.`); + } + + if (nakshatraEntry) { + const nThemes = nakshatraEntry.consciousnessThemes?.slice(0, 3).join(", ") ?? ""; + const nEffects = nakshatraEntry.psychologicalEffects?.slice(0, 2).join("; ") ?? ""; + if (nThemes) parts.push(`A estação lunar ${nakshatraEntry.namePt} acrescenta ${nThemes}.`); + if (nEffects) parts.push(`Na dinâmica interna pode aparecer como ${nEffects}.`); + } + + return parts.length > 0 ? parts.join(" ") : null; +} diff --git a/lib/knowledge/keywordMatch.ts b/lib/knowledge/keywordMatch.ts new file mode 100644 index 0000000..090f934 --- /dev/null +++ b/lib/knowledge/keywordMatch.ts @@ -0,0 +1,98 @@ +/** + * Extração de palavras da mensagem do usuário e pontuação de frases por relevância. + * Usado pelo oráculo offline para preferir frases que relacionam com o que o usuário digitou. + */ + +/** Palavras comuns em português que não ajudam a relacionar com frases do oráculo */ +const STOPWORDS = new Set([ + "de", "da", "do", "das", "dos", "e", "o", "a", "os", "as", "um", "uma", "uns", "umas", + "que", "em", "no", "na", "nos", "nas", "por", "para", "com", "sem", "ao", "aos", "às", + "pelo", "pela", "pelos", "pelas", "seu", "sua", "seus", "suas", "me", "meu", "minha", + "te", "tu", "ele", "ela", "isso", "este", "esta", "esse", "essa", "nós", "vós", "eles", "elas", + "ou", "mas", "se", "porque", "como", "quando", "onde", "qual", "quais", "quem", "quanto", + "ser", "estar", "ter", "fazer", "há", "foi", "são", "era", "sou", "somos", "é", "será", + "pode", "posso", "podem", "já", "mais", "menos", "muito", "bem", "mal", "só", "apenas", + "também", "ainda", "assim", "então", "aqui", "ali", "agora", "sempre", "nunca", "nada", "tudo", +]); + +const MIN_WORD_LENGTH = 2; + +/** + * Normaliza uma palavra para comparação (minúscula, sem acentos opcional). + */ +function normalizeWord(w: string): string { + return w + .toLowerCase() + .normalize("NFD") + .replace(/\p{Diacritic}/gu, "") + .trim(); +} + +/** + * Extrai palavras significativas da mensagem do usuário para preferência na seleção de frases. + * Remove stopwords e palavras muito curtas. + */ +export function extractKeywords(message: string | undefined | null): string[] { + if (!message || typeof message !== "string") return []; + const normalized = message + .toLowerCase() + .normalize("NFD") + .replace(/\p{Diacritic}/gu, ""); + const words = normalized.split(/\s+/).map((w) => w.replace(/[^\p{L}\p{N}]/gu, "")); + const seen = new Set(); + const out: string[] = []; + for (const w of words) { + if (w.length < MIN_WORD_LENGTH) continue; + if (STOPWORDS.has(w)) continue; + if (seen.has(w)) continue; + seen.add(w); + out.push(w); + } + return out; +} + +/** + * Pontua um texto pela quantidade de palavras-chave que aparecem nele. + * Retorna o número de keywords que aparecem no texto (normalizado, sem acentos). + */ +export function scoreByKeywords(text: string, keywords: string[]): number { + if (!text || keywords.length === 0) return 0; + const lower = text + .toLowerCase() + .normalize("NFD") + .replace(/\p{Diacritic}/gu, ""); + let score = 0; + for (const k of keywords) { + if (k.length < MIN_WORD_LENGTH) continue; + if (lower.includes(k)) score += 1; + } + return score; +} + +/** + * Dado um array de itens com texto, retorna o índice do item preferido: + * prefere os que têm maior score por keywords; em empate ou sem keywords, usa seed para escolher. + */ +export function pickIndexByKeywords( + items: T[], + getText: (item: T) => string, + keywords: string[], + seed?: number +): number { + if (items.length === 0) return 0; + if (keywords.length === 0) { + const i = seed !== undefined ? Math.abs(Math.floor(seed)) % items.length : Math.floor(Math.random() * items.length); + return i; + } + const scores = items.map((item) => scoreByKeywords(getText(item), keywords)); + const maxScore = Math.max(...scores); + const bestIndices = scores + .map((s, i) => (s === maxScore ? i : -1)) + .filter((i) => i >= 0); + if (bestIndices.length === 0) return 0; + const i = + seed !== undefined + ? bestIndices[Math.abs(Math.floor(seed)) % bestIndices.length] + : bestIndices[Math.floor(Math.random() * bestIndices.length)]; + return i; +} diff --git a/lib/knowledge/moonPhases.ts b/lib/knowledge/moonPhases.ts new file mode 100644 index 0000000..a45d6c2 --- /dev/null +++ b/lib/knowledge/moonPhases.ts @@ -0,0 +1,81 @@ +/** + * Fases da lua — simbologia profunda para consciência e oráculo. + * Usado em hovers e contexto do TimeHeader (Luz do Tempo). + */ + +export type MoonPhaseKey = + | "Lua nova" + | "Lua crescente" + | "Lua quarto crescente" + | "Lua cheia" + | "Lua minguante" + | "Lua quarto minguante"; + +export interface MoonPhaseEntry { + /** Rótulo exibido (igual à chave). */ + label: string; + /** Resumo profundo da simbologia — uma linha para hover. */ + hover: string; + /** Texto expandido para contexto/IA (opcional). */ + symbolism?: string; +} + +const MOON_PHASES: Record = { + "Lua nova": { + label: "Lua nova", + hover: "Semente no escuro. O que ainda não nasceu pede silêncio e intenção.", + symbolism: + "Invisível no céu, a lua nova simboliza o princípio absoluto, o vazio fértil, o momento antes do germe. Em muitas tradições é tempo de definir intenções, limpar o passado e confiar no que virá sem forçar. O inconsciente e o sonho ganham força; a ação visível pode esperar.", + }, + "Lua crescente": { + label: "Lua crescente", + hover: "O que foi lançado na nova agora brota. Crescimento suave, confiança no ritmo.", + symbolism: + "A lua crescente (foice no céu) marca o despertar da intenção em gesto. Energia de expansão, mas ainda delicada: plantar, iniciar, abrir-se ao novo sem exigir resultado imediato. Simboliza esperança ativa, o corpo que se abre ao ciclo e a consciência que aceita o tempo.", + }, + "Lua quarto crescente": { + label: "Lua quarto crescente", + hover: "Meio caminho entre intenção e realização. Decisão e esforço consciente.", + symbolism: + "Quarto crescente é o equilíbrio dinâmico entre o invisível (nova) e o pleno (cheia). Momento de consolidar escolhas, enfrentar obstáculos com clareza e comprometer-se com o que se iniciou. Em muitas culturas é tempo de trabalho ritual, disciplina e alinhamento entre desejo e ação.", + }, + "Lua cheia": { + label: "Lua cheia", + hover: "Plenitude visível. Revelação, colheita e entrega ao que está completo.", + symbolism: + "A lua cheia é o ápice do ciclo lunar: tudo o que foi semeado e nutrido se revela. Simboliza iluminação, clareza emocional, celebração e gratidão. Também é tempo de deixar ir o que já cumpriu sua função — colher e liberar. O consciente e o inconsciente se encontram; a sombra e a luz são visíveis.", + }, + "Lua minguante": { + label: "Lua minguante", + hover: "O ciclo se recolhe. Soltar, perdoar e preparar o solo para o novo.", + symbolism: + "A lua minguante representa o declínio consciente da luz visível e o crescimento do espaço interior. Tempo de desapego, limpeza, perdão e digestão do que foi vivido. Simboliza sabedoria que vem do deixar ir, da humildade diante do fim do ciclo e da confiança no repouso antes da nova semente.", + }, + "Lua quarto minguante": { + label: "Lua quarto minguante", + hover: "Último quarto: integração. O que foi vivido vira memória e ensinamento.", + symbolism: + "Quarto minguante é o espelho do quarto crescente: em vez de construir para fora, integra-se para dentro. Momento de revisar, aprender com o ciclo e preparar conscientemente o terreno para a lua nova. Simboliza maturidade do ciclo, discernimento e a passagem suave da ação para o repouso.", + }, +}; + +/** + * Retorna a entrada da fase lunar pelo rótulo (ex.: "Lua nova", "Lua cheia"). + * Se não encontrar, retorna entrada genérica para "Lua". + */ +export function getMoonPhaseEntry(phaseLabel: string): MoonPhaseEntry { + const key = phaseLabel as MoonPhaseKey; + if (key in MOON_PHASES) return MOON_PHASES[key]; + return { + label: phaseLabel, + hover: "Cada fase lunar traz um tom; este instante pede escuta.", + symbolism: "A lua marca o ritmo entre visível e invisível, ação e repouso.", + }; +} + +/** + * Texto curto para hover da lua (simbologia em uma linha). + */ +export function getMoonPhaseHover(phaseLabel: string): string { + return getMoonPhaseEntry(phaseLabel).hover; +} diff --git a/lib/knowledge/numerology.ts b/lib/knowledge/numerology.ts new file mode 100644 index 0000000..d1cca6d --- /dev/null +++ b/lib/knowledge/numerology.ts @@ -0,0 +1,233 @@ +/** + * Numerologia — número regente a partir do nome (Pitágoras). + * Dicionário de tendências e frases por número (1–9, 11, 22) para respostas coerentes. + */ + +import { pickIndexByKeywords } from "./keywordMatch"; + +export type RulingNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 22; + +/** Mapa letra → valor (Pitágoras) */ +const LETTER_VALUES: Record = { + A: 1, J: 1, S: 1, + B: 2, K: 2, T: 2, + C: 3, L: 3, U: 3, + D: 4, M: 4, V: 4, + E: 5, N: 5, W: 5, + F: 6, O: 6, X: 6, + G: 7, P: 7, Y: 7, + H: 8, Q: 8, Z: 8, + I: 9, R: 9, +}; + +function reduceToDigit(n: number): number { + if (n === 11 || n === 22) return n; + while (n > 9) { + n = String(n).split("").reduce((s, d) => s + parseInt(d, 10), 0); + } + return n; +} + +/** + * Calcula o número regente (Pitágoras) a partir do nome completo. + * Soma os valores das letras, reduz a um dígito (ou mantém 11/22). + */ +export function getRulingNumberFromName(fullName: string): RulingNumber { + const name = (fullName || "").toUpperCase().replace(/\s+/g, "").replace(/[^A-ZÀ-Ú]/g, ""); + if (!name.length) return 7; + let sum = 0; + for (const char of name) { + const v = LETTER_VALUES[char] ?? LETTER_VALUES[char.normalize("NFD").replace(/\p{Diacritic}/gu, "").toUpperCase()]; + if (v) sum += v; + } + if (sum === 0) return 7; + while (sum > 99) { + sum = String(sum).split("").reduce((s, d) => s + parseInt(d, 10), 0); + } + if (sum === 11 || sum === 22) return sum as 11 | 22; + return reduceToDigit(sum) as RulingNumber; +} + +/** Entrada do dicionário por número: tendências e frases para o oráculo */ +export type NumberTraitsEntry = { + number: RulingNumber; + name: string; + shortTrait: string; + tendencies: string[]; + challenges: string[]; + phrases: string[]; +}; + +const NUMBERS_DICTIONARY: NumberTraitsEntry[] = [ + { + number: 1, + name: "Líder", + shortTrait: "Iniciativa, independência, unidade.", + tendencies: ["liderança natural", "vontade forte", "criatividade pioneira", "autoconfiança", "individualidade"], + challenges: ["rigidez", "solidão", "impatência", "ego inflado"], + phrases: [ + "Seu número fala de unidade: o um que contém o todo.", + "A tendência do um é liderar; a sabedoria é saber quando seguir.", + "Independência e iniciativa são seus dons; a entrega é o desafio.", + "O um não precisa provar nada; já é completo.", + ], + }, + { + number: 2, + name: "Diplomata", + shortTrait: "Cooperação, sensibilidade, parceria.", + tendencies: ["diplomacia", "intuição", "parceria", "harmonia", "paciência"], + challenges: ["dependência", "indecisão", "evitar conflito a qualquer custo"], + phrases: [ + "Seu número traz o dom da escuta e do equilíbrio.", + "Dois é a dança entre eu e o outro; nenhum existe sem o outro.", + "Sensibilidade é força quando não vira fuga.", + "A cooperação que você busca começa em você mesmo.", + ], + }, + { + number: 3, + name: "Comunicador", + shortTrait: "Expressão, criatividade, alegria.", + tendencies: ["comunicação", "criatividade", "otimismo", "sociabilidade", "expressão"], + challenges: ["superficialidade", "dispersão", "crítica excessiva"], + phrases: [ + "Seu número é a trindade: corpo, mente e espírito em expressão.", + "Criatividade e alegria são seus canais; a profundidade os completa.", + "O três não precisa falar por falar; o silêncio também expressa.", + "Comunicação verdadeira nasce do que você é, não do que você mostra.", + ], + }, + { + number: 4, + name: "Construtor", + shortTrait: "Estabilidade, trabalho, raiz.", + tendencies: ["disciplina", "organização", "confiabilidade", "persistência", "estrutura"], + challenges: ["rigidez", "medo de mudança", "workaholic"], + phrases: [ + "Seu número constrói; a base que você busca está em você.", + "Quatro é a terra: firmeza e paciência. A flexibilidade é o complemento.", + "O trabalho que importa é o trabalho interior.", + "Estabilidade não é imobilidade; é centro em movimento.", + ], + }, + { + number: 5, + name: "Livre", + shortTrait: "Liberdade, mudança, experiência.", + tendencies: ["versatilidade", "curiosidade", "liberdade", "adaptação", "sentidos"], + challenges: ["inquietude", "compromisso difícil", "excesso de estímulo"], + phrases: [ + "Seu número anseia liberdade; a verdadeira liberdade é interior.", + "Cinco sentidos, cinco direções — o centro é o sexto.", + "Mudança constante pode esconder o que não quer ser visto.", + "A liberdade que você busca já habita o instante.", + ], + }, + { + number: 6, + name: "Cuidador", + shortTrait: "Responsabilidade, amor, serviço.", + tendencies: ["cuidado", "responsabilidade", "harmonia doméstica", "justiça", "beleza"], + challenges: ["sacrifício excessivo", "controle", "culpa"], + phrases: [ + "Seu número cuida; lembre-se de cuidar de quem cuida.", + "Seis é o equilíbrio entre dar e receber; ambos são um.", + "O amor que você oferece começa no amor por si.", + "Responsabilidade sem autocuidado vira peso.", + ], + }, + { + number: 7, + name: "Buscador", + shortTrait: "Reflexão, mistério, sabedoria.", + tendencies: ["introspecção", "espiritualidade", "análise", "intuição", "solidão criativa"], + challenges: ["isolamento", "cinismo", "fuga do mundo"], + phrases: [ + "Seu número busca o invisível; ele já está em você.", + "Sete é o sábio que sabe que não sabe.", + "A busca exterior reflete a busca interior.", + "Mistério e silêncio são seus aliados; use-os com compaixão.", + ], + }, + { + number: 8, + name: "Realizador", + shortTrait: "Poder, abundância, manifestação.", + tendencies: ["ambição", "organização", "autoridade", "material e espiritual", "justiça"], + challenges: ["materialismo", "controle", "workaholic"], + phrases: [ + "Seu número manifesta; o poder verdadeiro é o que não precisa provar.", + "Oito é o infinito deitado: abundância sem apego.", + "Realização externa reflete realização interna.", + "Poder e humildade podem coexistir quando o ego se acalma.", + ], + }, + { + number: 9, + name: "Humanitário", + shortTrait: "Completude, entrega, sabedoria.", + tendencies: ["generosidade", "visão ampla", "desapego", "compaixão", "ciclos"], + challenges: ["martírio", "idealismo rígido", "dificuldade em receber"], + phrases: [ + "Seu número completa o ciclo; o nove devolve tudo ao um.", + "Humanitarismo começa no humano que você é.", + "Entrega e desapego são dons quando não viram fuga.", + "A sabedoria do nove é saber que já está completo.", + ], + }, + { + number: 11, + name: "Visionário", + shortTrait: "Inspiração, intuição elevada, canal.", + tendencies: ["intuição forte", "idealismo", "inspiração", "sensibilidade", "visão"], + challenges: ["nervosismo", "expectativas altas", "fuga da materialidade"], + phrases: [ + "Onze é o número mestre: ponte entre mundos.", + "Sua intuição é canal; aterrar evita dispersão.", + "Visionários precisam de pés no chão para realizar a visão.", + "A inspiração que você sente pode servir ao mundo sem você se perder nela.", + ], + }, + { + number: 22, + name: "Mestre Construtor", + shortTrait: "Visão prática, realização em larga escala.", + tendencies: ["capacidade de realizar grandes projetos", "equilíbrio ideal e prático", "liderança", "organização"], + challenges: ["pressão", "perfeccionismo", "sobrecarga"], + phrases: [ + "Vinte e dois constrói no mundo o que o onze vislumbra.", + "Seu número pode manifestar o que serve a muitos; o cuidado consigo vem primeiro.", + "Mestre construtor não é quem faz tudo, é quem faz o essencial.", + "A realização em larga escala começa na paz interior.", + ], + }, +]; + +export function getNumberTraits(num: RulingNumber): NumberTraitsEntry { + const entry = NUMBERS_DICTIONARY.find((e) => e.number === num); + return entry ?? NUMBERS_DICTIONARY.find((e) => e.number === 7)!; +} + +export function getRandomPhraseForNumber( + num: RulingNumber, + seed?: number, + preferKeywords?: string[] +): string { + const entry = getNumberTraits(num); + const pool = entry.phrases; + const i = + preferKeywords?.length + ? pickIndexByKeywords(pool, (p) => p, preferKeywords, seed) + : seed !== undefined + ? Math.abs(Math.floor(seed)) % pool.length + : Math.floor(Math.random() * pool.length); + return pool[i] ?? entry.phrases[0]; +} + +export function getRandomTendencyForNumber(num: RulingNumber, seed?: number): string { + const entry = getNumberTraits(num); + const pool = entry.tendencies; + const i = seed !== undefined ? Math.abs(Math.floor(seed)) % pool.length : Math.floor(Math.random() * pool.length); + return pool[i] ?? entry.tendencies[0]; +} diff --git a/lib/knowledge/types.ts b/lib/knowledge/types.ts new file mode 100644 index 0000000..c4d0054 --- /dev/null +++ b/lib/knowledge/types.ts @@ -0,0 +1,66 @@ +/** + * Tipos do dicionário offline — base de conhecimento para o oráculo sem IA. + * Permite interpretar perfil, mapa e pergunta usando apenas dados locais. + */ + +export type UserProfileForOracle = { + fullName?: string; + birthDate?: string; // YYYY-MM-DD + birthPlace?: string; + birthTime?: string; // HH:mm +}; + +/** Chave de arquétipo derivada do mapa (signo lunar, nakshatra, etc.) */ +export type ArchetypeKey = string; + +/** Gunas — qualidades que afetam a consciência (sattva = clareza/equilíbrio, rajas = ação/movimento, tamas = inércia/repouso) */ +export type Guna = "sattva" | "rajas" | "tamas"; + +/** Signo sidereal (Védico) — 12 Rashis */ +export type RashiKey = + | "mesha" | "vrishabha" | "mithuna" | "karka" | "simha" | "kanya" + | "tula" | "vrischika" | "dhanu" | "makara" | "kumbha" | "mina"; + +/** Nakshatra (27 estações lunares) — chave abreviada */ +export type NakshatraKey = string; + +/** Resultado de cálculo védico simplificado (offline) */ +export type VedicChartSimplified = { + /** Signo solar sidereal aproximado */ + sunRashi?: RashiKey; + /** Signo lunar sidereal aproximado (prioridade para interpretação) */ + moonRashi?: RashiKey; + /** Nakshatra lunar aproximada */ + moonNakshatra?: NakshatraKey; + /** Arquétipo(s) sugerido(s) pelas fórmulas internas */ + archetypeKeys: ArchetypeKey[]; +}; + +/** Uma entrada do dicionário de formulações (frases internas) */ +export type FormulationEntry = { + id: string; + text: string; + /** Arquétipos com os quais combina (vazio = neutro) */ + archetypeHints?: ArchetypeKey[]; +}; + +/** Afirmação contínua das Upanishads */ +export type UpanishadEntry = { + id: string; + text: string; + source?: string; // ex: "Isha", "Kena", "Mundaka" + archetypeHints?: ArchetypeKey[]; +}; + +/** Definição de arquétipo + regras de associação ao mapa */ +export type ArchetypeEntry = { + key: ArchetypeKey; + name: string; + shortDescription: string; + /** Signos lunares associados (Rashi) */ + moonRashi?: RashiKey[]; + /** Nakshatras associadas */ + nakshatraHints?: NakshatraKey[]; + /** Frases típicas deste arquétipo (ids ou texto) */ + formulationIds?: string[]; +}; diff --git a/lib/knowledge/upanishads.ts b/lib/knowledge/upanishads.ts new file mode 100644 index 0000000..c699868 --- /dev/null +++ b/lib/knowledge/upanishads.ts @@ -0,0 +1,42 @@ +/** + * Afirmações contínuas das Upanishads — trechos para o oráculo offline. + * Fonte: textos clássicos (traduções/adaptações em português). + */ + +import type { UpanishadEntry } from "./types"; + +export const UPANISHAD_AFFIRMATIONS: UpanishadEntry[] = [ + { id: "u1", text: "Aquilo que não pode ser pensado pela mente, mas pelo qual a mente pensa — conhece isso como Brahman.", source: "Kena" }, + { id: "u2", text: "O que não pode ser visto pelo olho, mas pelo qual o olho vê — conhece isso como Brahman.", source: "Kena" }, + { id: "u3", text: "Isto é pleno; aquilo é pleno. Do pleno nasce o pleno. Tomando o pleno do pleno, o pleno permanece.", source: "Isha" }, + { id: "u4", text: "No interior do ser reside o Ser; quem o vê alcança a paz.", source: "Isha" }, + { id: "u5", text: "Como pássaro preso à corda, o homem preso ao corpo não vê a liberdade.", source: "Mundaka" }, + { id: "u6", text: "Conhece o Atman como o senhor da carruagem; o corpo é a carruagem.", source: "Katha" }, + { id: "u7", text: "O que está aqui está ali; o que está ali está aqui. Quem vê multiplicidade aqui, da morte à morte vai.", source: "Katha" }, + { id: "u8", text: "Levanta-te, desperta, aproxima-te dos mestres e conhece. Afiada como o fio da navalha é a senda, diz o sábio.", source: "Katha" }, + { id: "u9", text: "Não pelo discurso, não pela mente, não pelo olho se alcança o Atman. Só quem diz 'É Ele' o alcança.", source: "Kena" }, + { id: "u10", text: "O Ser que reside no coração é menor que um grão de mostarda e maior que os céus.", source: "Chandogya" }, + { id: "u11", text: "Tat tvam asi — Isso és tu.", source: "Chandogya" }, + { id: "u12", text: "Aquele que conhece Brahman torna-se Brahman.", source: "Mundaka" }, + { id: "u13", text: "A paz que está além do entendimento — assim é o conhecedor de Brahman.", source: "Kena" }, + { id: "u14", text: "O silêncio do sábio é a sua resposta.", source: "adaptado" }, + { id: "u15", text: "O que você busca já está em você. A pergunta e a resposta são o mesmo movimento.", source: "adaptado" }, +]; + +export function getUpanishadById(id: string): UpanishadEntry | undefined { + return UPANISHAD_AFFIRMATIONS.find((u) => u.id === id); +} + +export function getUpanishadsByArchetype(archetypeKey: string): UpanishadEntry[] { + return UPANISHAD_AFFIRMATIONS.filter( + (u) => !u.archetypeHints?.length || u.archetypeHints.includes(archetypeKey) + ); +} + +export function getRandomUpanishad(archetypeKey?: string): UpanishadEntry { + const pool = archetypeKey + ? getUpanishadsByArchetype(archetypeKey) + : UPANISHAD_AFFIRMATIONS; + const i = Math.floor(Math.random() * pool.length); + return pool[i] ?? UPANISHAD_AFFIRMATIONS[0]; +} diff --git a/lib/knowledge/vedic.ts b/lib/knowledge/vedic.ts new file mode 100644 index 0000000..c28d7fb --- /dev/null +++ b/lib/knowledge/vedic.ts @@ -0,0 +1,179 @@ +/** + * Astrologia védica (Jyotish) — cálculos offline. + * Usa mhah-panchang quando instalado (npm install mhah-panchang); senão, aproximações locais. + */ + +import type { RashiKey, NakshatraKey, VedicChartSimplified } from "./types"; +import { getArchetypeKeysFromChart } from "./archetypes"; + +/** Mapeia ino (1–12) do mhah-panchang Raasi para RashiKey */ +const INO_TO_RASHI: RashiKey[] = [ + "mesha", "vrishabha", "mithuna", "karka", "simha", "kanya", + "tula", "vrischika", "dhanu", "makara", "kumbha", "mina", +]; + +/** Mapeia ino (1–27) do mhah-panchang Nakshatra para NakshatraKey */ +const INO_TO_NAKSHATRA: NakshatraKey[] = [ + "ashwini", "bharani", "krittika", "rohini", "mrigashira", "ardra", "punarvasu", + "pushya", "ashlesha", "magha", "purva-phalguni", "uttara-phalguni", "hasta", + "chitra", "swati", "vishakha", "anuradha", "jyestha", "mula", "purva-ashadha", + "uttara-ashadha", "shravana", "dhanishta", "shatabhisha", "purva-bhadra", + "uttara-bhadra", "revati", +]; + +function tryMhahPanchang(birthDate: string, birthTime?: string): VedicChartSimplified | null { + try { + const { MhahPanchang } = require("mhah-panchang"); + const obj = new MhahPanchang(); + const dateStr = birthTime ? `${birthDate}T${birthTime}:00` : `${birthDate}T12:00:00`; + const d = new Date(dateStr); + if (Number.isNaN(d.getTime())) return null; + const mhahObj = obj.calculate(d); + const raasi = mhahObj?.Raasi; + const nakshatra = mhahObj?.Nakshatra; + const moonRashi: RashiKey | undefined = raasi?.ino != null + ? INO_TO_RASHI[(raasi.ino - 1) % 12] + : undefined; + const moonNakshatra: NakshatraKey | undefined = nakshatra?.ino != null + ? INO_TO_NAKSHATRA[(nakshatra.ino - 1) % 27] + : undefined; + const archetypeKeys = getArchetypeKeysFromChart({ moonRashi, moonNakshatra }); + return { + sunRashi: moonRashi, + moonRashi, + moonNakshatra, + archetypeKeys: archetypeKeys.length ? archetypeKeys : ["dissolvente"], + }; + } catch { + return null; + } +} + +/** Ayanamsa aproximado (Lahiri) em graus — diferença tropical/sidereal (~24° em 2000) */ +const AYANAMSA_DEG = 24; + +/** Graus por signo */ +const DEG_PER_SIGN = 30; + +/** Ordem dos Rashis (signos sidereais) */ +const RASHI_ORDER: RashiKey[] = [ + "mesha", "vrishabha", "mithuna", "karka", "simha", "kanya", + "tula", "vrischika", "dhanu", "makara", "kumbha", "mina", +]; + +/** + * Converte data (YYYY-MM-DD) em dia do ano (0–365). + */ +function dayOfYear(dateStr: string): number | null { + const d = new Date(dateStr + "T12:00:00Z"); + if (Number.isNaN(d.getTime())) return null; + const start = new Date(d.getFullYear(), 0, 1); + return Math.floor((d.getTime() - start.getTime()) / (24 * 60 * 60 * 1000)); +} + +/** + * Posição tropical aproximada do Sol em graus (0–360) a partir do dia do ano. + * Fórmula simplificada: Sol avança ~1° por dia. + */ +function tropicalSunLongitude(dayOfYearVal: number): number { + const deg = (dayOfYearVal * 360) / 365.25; + return deg % 360; +} + +/** + * Converte longitude em graus (0–360) em signo sidereal (Rashi). + * Aplica ayanamsa: longitude_sidereal ≈ longitude_tropical - ayanamsa. + */ +function longitudeToRashi(longitudeDeg: number, subtractAyanamsa: boolean): RashiKey { + let L = longitudeDeg; + if (subtractAyanamsa) { + L = (L - AYANAMSA_DEG + 360) % 360; + } + const index = Math.floor(L / DEG_PER_SIGN) % 12; + return RASHI_ORDER[index] ?? "mesha"; +} + +/** + * Nakshatra aproximada a partir da longitude lunar (0–360). + * 27 nakshatras = 360/27 ≈ 13,33° cada. + */ +function longitudeToNakshatra(longitudeDeg: number): NakshatraKey { + const L = longitudeDeg % 360; + const index = Math.floor(L / (360 / 27)) % 27; + return INO_TO_NAKSHATRA[index] ?? "ashwini"; +} + +/** + * Longitude lunar aproximada a partir de data e hora. + * Fórmula muito simplificada: Lua avança ~13° por dia (ciclo ~27,3 dias). + * Não substitui efeméride real; serve só para fallback/estimação. + */ +function approximateMoonLongitude(dateStr: string, timeStr?: string): number | null { + const day = dayOfYear(dateStr); + if (day === null) return null; + let dayFraction = day; + if (timeStr) { + const [h, m] = timeStr.split(":").map(Number); + if (!Number.isNaN(h)) dayFraction += (h + (Number.isNaN(m) ? 0 : m / 60)) / 24; + } + const lunarCycle = 27.321661; + const moonDeg = (dayFraction / lunarCycle) * 360; + return moonDeg % 360; +} + +/** + * Calcula um chart védico simplificado a partir do perfil. + * - Signo solar sidereal: aproximado por data de nascimento. + * - Signo lunar e nakshatra: aproximados se houver data e hora (senão indefinidos). + * - Arquétipos: derivados do signo lunar (prioridade) e do solar. + */ +export function computeVedicChartSimplified(profile: { + birthDate?: string; + birthTime?: string; +}): VedicChartSimplified { + if (profile.birthDate && profile.birthTime) { + const fromPanchang = tryMhahPanchang(profile.birthDate, profile.birthTime); + if (fromPanchang) return fromPanchang; + } + if (profile.birthDate) { + const fromPanchang = tryMhahPanchang(profile.birthDate); + if (fromPanchang) return fromPanchang; + } + + const archetypeKeys: string[] = []; + let sunRashi: RashiKey | undefined; + let moonRashi: RashiKey | undefined; + let moonNakshatra: NakshatraKey | undefined; + + if (profile.birthDate) { + const day = dayOfYear(profile.birthDate); + if (day !== null) { + const tropSun = tropicalSunLongitude(day); + sunRashi = longitudeToRashi(tropSun, true); + archetypeKeys.push(...getArchetypeKeysFromChart({ moonRashi: sunRashi })); + } + } + + if (profile.birthDate && profile.birthTime) { + const moonLong = approximateMoonLongitude(profile.birthDate, profile.birthTime); + if (moonLong !== null) { + moonNakshatra = longitudeToNakshatra(moonLong); + moonRashi = longitudeToRashi(moonLong, true); + const fromMoon = getArchetypeKeysFromChart({ moonRashi, moonNakshatra }); + fromMoon.forEach((k) => { + if (!archetypeKeys.includes(k)) archetypeKeys.push(k); + }); + } + } + + if (archetypeKeys.length === 0) { + archetypeKeys.push("dissolvente"); + } + + return { + sunRashi, + moonRashi, + moonNakshatra, + archetypeKeys, + }; +} diff --git a/lib/logPaths.ts b/lib/logPaths.ts new file mode 100644 index 0000000..daa09c8 --- /dev/null +++ b/lib/logPaths.ts @@ -0,0 +1,19 @@ +/** + * Caminhos dos arquivos de log (app.log, audit.log). + * Usado pela API admin de monitoramento para leitura. + */ + +import path from "path"; + +export function getLogDir(): string { + const base = process.env.LOG_DIR || process.env.DATA_DIR || path.join(process.cwd(), "data"); + return path.join(base, "logs"); +} + +export function getAppLogPath(): string { + return path.join(getLogDir(), "app.log"); +} + +export function getAuditLogPath(): string { + return path.join(getLogDir(), "audit.log"); +} diff --git a/lib/logger.ts b/lib/logger.ts new file mode 100644 index 0000000..c22db5f --- /dev/null +++ b/lib/logger.ts @@ -0,0 +1,73 @@ +/** + * Log em arquivo — níveis info, warn, error, debug. + * Grava em data/logs/app.log (ou LOG_DIR quando definido). + */ + +import fs from "fs"; +import path from "path"; + +export type LogLevel = "debug" | "info" | "warn" | "error"; + +const LOG_LEVEL_ORDER: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3, +}; + +function getLogDir(): string { + const base = process.env.LOG_DIR || process.env.DATA_DIR || path.join(process.cwd(), "data"); + return path.join(base, "logs"); +} + +function getAppLogPath(): string { + return path.join(getLogDir(), "app.log"); +} + +function ensureLogDir(): void { + const dir = getLogDir(); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } +} + +function minLevel(): LogLevel { + const env = process.env.LOG_LEVEL?.toLowerCase(); + if (env === "debug" || env === "info" || env === "warn" || env === "error") return env; + return process.env.NODE_ENV === "development" ? "debug" : "info"; +} + +function shouldLog(level: LogLevel): boolean { + return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[minLevel()]; +} + +function formatLine(level: LogLevel, message: string, meta?: Record): string { + const ts = new Date().toISOString(); + const metaStr = meta != null && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ""; + return `${ts} [${level.toUpperCase()}] ${message}${metaStr}\n`; +} + +function write(level: LogLevel, message: string, meta?: Record): void { + if (!shouldLog(level)) return; + try { + ensureLogDir(); + fs.appendFileSync(getAppLogPath(), formatLine(level, message, meta), "utf8"); + } catch (err) { + console.error("[logger] write failed:", err); + } +} + +export const logger = { + debug(message: string, meta?: Record) { + write("debug", message, meta); + }, + info(message: string, meta?: Record) { + write("info", message, meta); + }, + warn(message: string, meta?: Record) { + write("warn", message, meta); + }, + error(message: string, meta?: Record) { + write("error", message, meta); + }, +}; diff --git a/lib/mercadopago.ts b/lib/mercadopago.ts new file mode 100644 index 0000000..31f090c --- /dev/null +++ b/lib/mercadopago.ts @@ -0,0 +1,68 @@ +/** + * Mercado Pago — Checkout Pro via API REST. + * Configure MERCADOPAGO_ACCESS_TOKEN em .env.local. + */ + +const MP_API = "https://api.mercadopago.com"; + +export function isMercadoPagoConfigured(): boolean { + const token = process.env.MERCADOPAGO_ACCESS_TOKEN; + return typeof token === "string" && token.trim().length > 0; +} + +function getAccessToken(): string | null { + const token = process.env.MERCADOPAGO_ACCESS_TOKEN; + return typeof token === "string" && token.trim().length > 0 ? token.trim() : null; +} + +export type CreatePreferenceBody = { + items: Array<{ title: string; quantity: number; unit_price: number; currency_id?: string }>; + payer?: { email?: string }; + back_urls?: { success: string; failure: string; pending: string }; + auto_return?: "approved" | "all"; + notification_url?: string; + external_reference?: string; + metadata?: Record; +}; + +export type PreferenceResponse = { + id: string; + init_point?: string; + sandbox_init_point?: string; +}; + +/** Cria preferência de pagamento (Checkout Pro). Retorna init_point (URL de redirecionamento). */ +export async function createPreference(body: CreatePreferenceBody): Promise { + const token = getAccessToken(); + if (!token) return null; + const res = await fetch(`${MP_API}/checkout/preferences`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + if (!res.ok) return null; + return res.json() as Promise; +} + +export type PaymentResponse = { + id: number; + status: string; + status_detail?: string; + transaction_amount?: number; + metadata?: { credits?: string; email?: string }; + external_reference?: string; +}; + +/** Busca pagamento por ID. */ +export async function getPayment(paymentId: string): Promise { + const token = getAccessToken(); + if (!token) return null; + const res = await fetch(`${MP_API}/v1/payments/${paymentId}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) return null; + return res.json() as Promise; +} diff --git a/lib/oracleOffline.ts b/lib/oracleOffline.ts new file mode 100644 index 0000000..94a7b81 --- /dev/null +++ b/lib/oracleOffline.ts @@ -0,0 +1,181 @@ +/** + * Oráculo offline — interpreta perfil e pergunta usando apenas o dicionário local. + * Integra: arquétipo (Jyotish), número regente (numerologia), traits, textos clássicos e formulações. + * Usa seed derivado do perfil para variedade e menor replicação. + */ + +import type { UserProfileForOracle } from "./knowledge/types"; +import { computeVedicChartSimplified } from "./knowledge/vedic"; +import { extractKeywords } from "./knowledge/keywordMatch"; +import { getRandomFormulation } from "./knowledge/formulations"; +import { getRandomClassicTextForArchetype } from "./knowledge/classicTexts"; +import { getRandomArchetypePhrase } from "./knowledge/archetypeTraits"; +import { getJyotishPhraseForChart } from "./knowledge/jyotishMeanings"; +import { getRulingNumberFromName, getRandomPhraseForNumber } from "./knowledge/numerology"; + +const MAX_SEED_TRIES = 30; +/** Número de blocos: na maioria 1, às vezes 2, raramente 3. */ +function getTargetBlockCount(seed4: number): number { + const r = seed4 % 10; + if (r <= 6) return 1; // ~70% — mais 1 + if (r <= 8) return 2; // ~20% — poucas vezes 2 + return 3; // ~10% — raramente 3 +} + +/** Normaliza texto para comparação (trim + lowercase). */ +function normalizeForCompare(text: string): string { + return text.trim().toLowerCase(); +} + +/** Verifica se o texto já existe nos blocos (normalizado) para evitar repetição. */ +function isDuplicate(text: string, blocks: string[]): boolean { + const n = normalizeForCompare(text); + if (!n) return true; + return blocks.some((b) => normalizeForCompare(b) === n); +} + +/** Verifica se o texto está no conjunto de frases usadas nas últimas rodadas. */ +function isRecentlyUsed(text: string, recentSet: Set): boolean { + const n = normalizeForCompare(text); + return n.length > 0 && recentSet.has(n); +} + +/** Retorna true se o texto pode ser usado (não está em blocks nem em recentSet). */ +function canUsePhrase(text: string, blocks: string[], recentSet: Set): boolean { + return !isDuplicate(text, blocks) && !isRecentlyUsed(text, recentSet); +} + +/** Gera um seed numérico a partir do perfil para variedade com menor replicação */ +function getSeedFromProfile(profile: UserProfileForOracle, offset: number = 0): number { + const str = [ + profile.fullName ?? "", + profile.birthDate ?? "", + profile.birthTime ?? "", + String(offset), + ].join("|"); + let h = 0; + for (let i = 0; i < str.length; i++) { + h = ((h << 5) - h) + str.charCodeAt(i); + h |= 0; + } + return Math.abs(h); +} + +/** Embaralha um array de forma determinística (seed) para ordem fluida, não fixa por tema. */ +function shuffleBySeed(items: T[], seed: number): T[] { + const out = [...items]; + let s = seed; + for (let i = out.length - 1; i > 0; i--) { + s = (s * 1103515245 + 12345) >>> 0; + const j = s % (i + 1); + [out[i], out[j]] = [out[j], out[i]]; + } + return out; +} + +/** Sal de aleatoriedade por requisição (tempo + aleatório) para variar as frases a cada chamada. */ +function getRequestSalt(): number { + return (Date.now() * 1000 + Math.floor(Math.random() * 1000)) >>> 0; +} + +/** Gera uma mensagem de revelação a partir do perfil e do dicionário offline. */ +export function getOfflineRevelation( + profile: UserProfileForOracle, + userMessage?: string, + recentlyUsedPhrases?: string[] +): string { + const salt = getRequestSalt(); + const seedBase = (getSeedFromProfile(profile) + salt) >>> 0; + const seed1 = (getSeedFromProfile(profile, 1) + salt * 31) >>> 0; + const seed2 = (getSeedFromProfile(profile, 2) + salt * 37) >>> 0; + const seed3 = (getSeedFromProfile(profile, 3) + salt * 41) >>> 0; + const seed4 = (getSeedFromProfile(profile, 4) + salt * 43) >>> 0; + + const recentSet = new Set( + (recentlyUsedPhrases ?? []) + .map((p) => normalizeForCompare(p)) + .filter((n) => n.length > 0) + ); + + const keywords = extractKeywords(userMessage); + const blocks: string[] = []; + + const chart = computeVedicChartSimplified({ + birthDate: profile.birthDate, + birthTime: profile.birthTime, + }); + const archetypeKey = chart.archetypeKeys[0]; + const rulingNumber = getRulingNumberFromName(profile.fullName ?? ""); + const firstName = profile.fullName?.trim().split(/\s+/)[0]; + const closing = firstName && (seed4 % 3 === 0) + ? `${firstName}, o que em você já sabe?` + : "O que em você já sabe?"; + + function pickPhrase( + getPhrase: (seed: number) => string | null, + baseSeed: number + ): string | null { + let phrase = getPhrase(baseSeed); + for (let k = 0; k < MAX_SEED_TRIES && phrase && !canUsePhrase(phrase, blocks, recentSet); k++) { + phrase = getPhrase(baseSeed + k + 1); + } + return phrase && !isDuplicate(phrase, blocks) ? phrase : null; + } + + const formulationText = pickPhrase( + (s) => getRandomFormulation(archetypeKey, s, keywords)?.text ?? null, + seedBase + ); + + const archetypePhrase = pickPhrase( + (s) => getRandomArchetypePhrase(archetypeKey, s, keywords), + seed1 + ); + const classicText = pickPhrase( + (s) => getRandomClassicTextForArchetype(archetypeKey, s, keywords)?.text ?? null, + seed2 + ); + const jyotishPhrase = pickPhrase( + (s) => getJyotishPhraseForChart(chart, s, keywords), + seed2 + 1 + ); + const numberPhrase = pickPhrase( + (s) => getRandomPhraseForNumber(rulingNumber, s, keywords), + seed3 + ); + + const typedPhrases: { phrase: string }[] = []; + const seenNormalized = new Set(); + + function addIfNew(phrase: string | null): void { + if (!phrase) return; + const n = normalizeForCompare(phrase); + if (!n || seenNormalized.has(n)) return; + seenNormalized.add(n); + typedPhrases.push({ phrase }); + } + + addIfNew(formulationText); + addIfNew(archetypePhrase); + addIfNew(classicText); + addIfNew(jyotishPhrase); + addIfNew(numberPhrase); + if (closing && !isRecentlyUsed(closing, recentSet)) addIfNew(closing); + + const shuffled = shuffleBySeed(typedPhrases, seed4); + const targetTotal = getTargetBlockCount(seed4); + + for (const { phrase } of shuffled) { + if (phrase && !isDuplicate(phrase, blocks) && !isRecentlyUsed(phrase, recentSet)) { + blocks.push(phrase); + } + if (blocks.length >= targetTotal) break; + } + + if (blocks.length === 0 && shuffled.length > 0) { + const fallback = shuffled.find(({ phrase }) => phrase && !isDuplicate(phrase, blocks)); + if (fallback?.phrase) blocks.push(fallback.phrase); + } + + return blocks.join("\n\n"); +} diff --git a/lib/otpStore.ts b/lib/otpStore.ts new file mode 100644 index 0000000..c7bc261 --- /dev/null +++ b/lib/otpStore.ts @@ -0,0 +1,38 @@ +/** + * Armazenamento temporário de códigos OTP por email (em memória). + * Em produção: enviar código por email (Resend, SendGrid, etc.) e opcionalmente persistir em Redis/DB. + */ + +const store = new Map(); +const TTL_MS = 10 * 60 * 1000; // 10 minutos + +function generateCode(): string { + return String(Math.floor(100000 + Math.random() * 900000)); +} + +export function setOtp(email: string): string { + const code = generateCode(); + store.set(email.toLowerCase().trim(), { + code, + expiry: Date.now() + TTL_MS, + }); + return code; +} + +export function verifyOtp(email: string, code: string): boolean { + const key = email.toLowerCase().trim(); + const entry = store.get(key); + if (!entry || entry.expiry < Date.now()) { + store.delete(key); + return false; + } + const ok = entry.code === String(code).trim(); + if (ok) store.delete(key); + return ok; +} + +/** Para desenvolvimento: código fixo 123456 sempre válido quando NODE_ENV !== production */ +export function isDevCode(email: string, code: string): boolean { + if (process.env.NODE_ENV === "production") return false; + return String(code).trim() === "123456"; +} diff --git a/lib/readingOffline.ts b/lib/readingOffline.ts new file mode 100644 index 0000000..f56719f --- /dev/null +++ b/lib/readingOffline.ts @@ -0,0 +1,113 @@ +/** + * Leitura (mapa pessoal) offline — monta um texto a partir do conhecimento local + * (Jyotish, numerologia, arquétipos), sem chamar a IA. + * Usa interpretação Jyotish para leitura (temas e efeitos narrativos), não as frases do oráculo. + */ + +import { computeVedicChartSimplified } from "@/lib/knowledge/vedic"; +import { getRulingNumberFromName, getNumberTraits } from "@/lib/knowledge/numerology"; +import { getArchetypeTraits } from "@/lib/knowledge/archetypeTraits"; +import { getRandomClassicTextForArchetype } from "@/lib/knowledge/classicTexts"; +import { + getJyotishReadingInterpretation, + RASHI_MEANINGS, + NAKSHATRA_MEANINGS, +} from "@/lib/knowledge/jyotishMeanings"; +import type { RashiKey, NakshatraKey } from "@/lib/knowledge/types"; + +function getSeedFromProfile(profile: { fullName?: string; birthDate?: string; birthTime?: string }, offset: number): number { + const str = [ + profile.fullName ?? "", + profile.birthDate ?? "", + profile.birthTime ?? "", + String(offset), + ].join("|"); + let h = 0; + for (let i = 0; i < str.length; i++) { + h = ((h << 5) - h) + str.charCodeAt(i); + h |= 0; + } + return Math.abs(h); +} + +function rashiName(key: RashiKey): string { + const e = RASHI_MEANINGS.find((x) => x.key === key); + return e?.namePt ?? key; +} + +function nakshatraName(key: NakshatraKey): string { + const e = NAKSHATRA_MEANINGS.find((x) => x.key === key); + return e?.namePt ?? key; +} + +/** + * Gera uma leitura pessoal (mapa) totalmente offline, sem IA. + * Usa interpretação Jyotish (temas + efeitos), descrição de arquétipo e numerologia, + * e um texto clássico; evita as frases prontas do oráculo Darshan. + */ +export function getOfflineReading(profile: { + fullName?: string; + birthDate?: string; + birthPlace?: string; + birthTime?: string; +}): string { + const seed3 = (getSeedFromProfile(profile, 3) + Date.now()) >>> 0; + + const chart = computeVedicChartSimplified({ + birthDate: profile.birthDate, + birthTime: profile.birthTime, + }); + const rulingNumber = getRulingNumberFromName(profile.fullName ?? ""); + const numberTraits = getNumberTraits(rulingNumber); + const archetypeKey = chart.archetypeKeys?.[0]; + const archetypeEntry = archetypeKey ? getArchetypeTraits(archetypeKey) : undefined; + + const sections: string[] = []; + + // Introdução + sections.push( + "Esta leitura integra o mapa védico (Lua, signo e estação lunar), numerologia e arquétipos em uma síntese interpretativa." + ); + + // Lua e Jyotish (interpretação narrativa, não frases do oráculo) + if (chart.moonRashi || chart.moonNakshatra) { + const rashi = chart.moonRashi ? rashiName(chart.moonRashi as RashiKey) : ""; + const naks = chart.moonNakshatra ? nakshatraName(chart.moonNakshatra as NakshatraKey) : ""; + const line: string[] = []; + if (rashi) line.push(`Sua Lua está no signo de ${rashi}`); + if (naks) line.push(`na estação lunar ${naks}`); + if (line.length) { + sections.push(line.join(" e ") + "."); + const interpretation = getJyotishReadingInterpretation(chart); + if (interpretation) sections.push(interpretation); + } + } + + // Arquétipo (descrição a partir de traits, sem frase pronta) + if (archetypeKey && archetypeEntry) { + sections.push(""); + sections.push(`Arquétipo: ${archetypeEntry.name} — ${archetypeEntry.shortTrait}`); + const personality = archetypeEntry.personality?.slice(0, 3).join(", ") ?? ""; + const tendencies = archetypeEntry.tendencies?.slice(0, 3).join("; ") ?? ""; + const challenges = archetypeEntry.challenges?.slice(0, 2).join("; ") ?? ""; + if (personality) sections.push(`Traços: ${personality}.`); + if (tendencies) sections.push(`Tendências: ${tendencies}.`); + if (challenges) sections.push(`Desafios possíveis: ${challenges}.`); + } + + // Numerologia (descrição a partir de traits, sem frase pronta) + sections.push(""); + sections.push(`Numerologia (Pitágoras) — Número regente ${rulingNumber}: ${numberTraits.name}.`); + sections.push(numberTraits.shortTrait + "."); + sections.push(`Tendências: ${numberTraits.tendencies.join("; ")}.`); + sections.push(`Desafios: ${numberTraits.challenges.join("; ")}.`); + + // Um texto clássico (reflexão, não oráculo) + const classic = getRandomClassicTextForArchetype(archetypeKey ?? "sábio", seed3); + if (classic?.text) { + sections.push(""); + sections.push(classic.text); + } + + return sections.join("\n\n").trim(); +} diff --git a/lib/stripe.ts b/lib/stripe.ts new file mode 100644 index 0000000..68fc999 --- /dev/null +++ b/lib/stripe.ts @@ -0,0 +1,20 @@ +/** + * Cliente Stripe para Checkout (pagamento com cartão e Google Pay). + */ + +import Stripe from "stripe"; + +let stripe: Stripe | null = null; + +export function getStripe(): Stripe | null { + const key = process.env.STRIPE_SECRET_KEY; + if (!key?.trim()) return null; + if (!stripe) { + stripe = new Stripe(key.trim()); + } + return stripe; +} + +export function isStripeConfigured(): boolean { + return Boolean(process.env.STRIPE_SECRET_KEY?.trim()); +} diff --git a/lib/supabase.ts b/lib/supabase.ts new file mode 100644 index 0000000..b1be270 --- /dev/null +++ b/lib/supabase.ts @@ -0,0 +1,21 @@ +/** + * Cliente Supabase para o módulo financeiro (server-side). + * Requer SUPABASE_URL e SUPABASE_SERVICE_KEY no .env. + */ + +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; + +let _client: SupabaseClient | null = null; + +export function getSupabase(): SupabaseClient | null { + if (_client) return _client; + const url = process.env.SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_KEY; + if (!url || !key) return null; + _client = createClient(url, key, { auth: { persistSession: false } }); + return _client; +} + +export function isSupabaseConfigured(): boolean { + return !!(process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_KEY); +} diff --git a/lib/timepulse.ts b/lib/timepulse.ts index a867f7a..2c0726a 100644 --- a/lib/timepulse.ts +++ b/lib/timepulse.ts @@ -1,13 +1,48 @@ +import * as SunCalc from "suncalc"; + export type TimePulse = { sunrise: string; sunset: string; moonPhase: string; }; -export function getTimePulse(): TimePulse { +/** Formata Date para HH:mm em pt-BR (locale do usuário ou fixo). */ +function formatTime(d: Date): string { + return d.toLocaleTimeString("pt-BR", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); +} + +/** Mapeia fase lunar (0–1) para rótulo em português. */ +function moonPhaseLabel(phase: number): string { + if (phase <= 0.03) return "Lua nova"; + if (phase <= 0.22) return "Lua crescente"; + if (phase <= 0.28) return "Lua quarto crescente"; + if (phase <= 0.47) return "Lua crescente"; + if (phase <= 0.53) return "Lua cheia"; + if (phase <= 0.72) return "Lua minguante"; + if (phase <= 0.78) return "Lua quarto minguante"; + if (phase <= 0.97) return "Lua minguante"; + return "Lua nova"; +} + +/** + * Calcula o TimePulse do momento: nascer/pôr do sol e fase lunar. + * Usa coordenadas padrão (São Paulo) quando não fornecidas. + */ +export function getTimePulse( + date: Date = new Date(), + lat: number = -23.55, + lng: number = -46.63 +): TimePulse { + const times = SunCalc.getTimes(date, lat, lng); + const moon = SunCalc.getMoonIllumination(date); + return { - sunrise: "06:10", - sunset: "18:42", - moonPhase: "Lua crescente", + sunrise: formatTime(times.sunrise), + sunset: formatTime(times.sunset), + moonPhase: moonPhaseLabel(moon.phase), }; } diff --git a/lib/usageLimits.ts b/lib/usageLimits.ts new file mode 100644 index 0000000..b8b769b --- /dev/null +++ b/lib/usageLimits.ts @@ -0,0 +1,122 @@ +/** + * Limites de consumo de IA: rate limit (req/min) e limite diário (respostas/dia). + * Ver docs/PRECIFICACAO_CREDITOS.md. + */ + +import { getTodayAiRequestCount } from "@/lib/finance/usageLogger"; +import { isSupabaseConfigured } from "@/lib/supabase"; + +const ENV_RATE = "RATE_LIMIT_PER_MINUTE"; +const ENV_DAILY = "DAILY_AI_LIMIT"; +const DEFAULT_RATE = 5; +const DEFAULT_DAILY = 30; +const WINDOW_MS = 60 * 1000; // 1 minuto + +/** Timestamps das últimas requisições por chave (email). */ +const rateWindow = new Map(); + +/** Contagem in-memory por dia (quando Supabase não está configurado). key = email, value = { date: YYYY-MM-DD, count } */ +const dailyCountByKey = new Map(); + +function getRateLimit(): number { + const raw = process.env[ENV_RATE]; + if (raw === undefined || raw === "") return DEFAULT_RATE; + const n = parseInt(raw, 10); + return Number.isFinite(n) && n >= 0 ? n : DEFAULT_RATE; +} + +function getDailyLimit(): number { + const raw = process.env[ENV_DAILY]; + if (raw === undefined || raw === "") return DEFAULT_DAILY; + const n = parseInt(raw, 10); + return Number.isFinite(n) && n >= 0 ? n : DEFAULT_DAILY; +} + +function todayUtcKey(): string { + const d = new Date(); + return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`; +} + +function pruneOldTimestamps(now: number, list: number[]): number[] { + const cutoff = now - WINDOW_MS; + return list.filter((t) => t > cutoff); +} + +/** + * Verifica e registra rate limit (requisições por minuto). + * Retorna true se permitido; false se excedeu. 0 = desativado (sempre true). + */ +export function checkAndRecordRateLimit(key: string): boolean { + const limit = getRateLimit(); + if (limit === 0) return true; + + const now = Date.now(); + let list = rateWindow.get(key) ?? []; + list = pruneOldTimestamps(now, list); + if (list.length >= limit) { + return false; + } + list.push(now); + rateWindow.set(key, list); + return true; +} + +export function getRateLimitConfig(): { perMinute: number } { + return { perMinute: getRateLimit() }; +} + +export function getDailyLimitConfig(): number { + return getDailyLimit(); +} + +/** + * Retorna a contagem in-memory de requisições hoje (usado quando Supabase não está configurado). + */ +function getTodayCountInMemory(key: string): number { + const today = todayUtcKey(); + const entry = dailyCountByKey.get(key); + if (!entry || entry.date !== today) return 0; + return entry.count; +} + +/** + * Incrementa a contagem diária in-memory. Chamar após uma requisição de IA bem-sucedida + * apenas quando Supabase não está configurado (senão o count vem de ai_usage_log). + */ +export function recordDailyRequest(key: string): void { + if (isSupabaseConfigured()) return; + const today = todayUtcKey(); + const entry = dailyCountByKey.get(key); + if (!entry || entry.date !== today) { + dailyCountByKey.set(key, { date: today, count: 1 }); + return; + } + entry.count += 1; +} + +export interface DailyLimitResult { + allowed: boolean; + count: number; + limit: number; +} + +/** + * Verifica o limite diário (respostas/dia). Com Supabase usa ai_usage_log; sem Supabase usa contagem in-memory. + * limit 0 = desativado (sempre allowed). + */ +export async function checkDailyLimit(key: string): Promise { + const limit = getDailyLimit(); + if (limit === 0) { + return { allowed: true, count: 0, limit: 0 }; + } + + const count = isSupabaseConfigured() + ? await getTodayAiRequestCount(key) + : getTodayCountInMemory(key); + + return { + allowed: count < limit, + count, + limit, + }; +} diff --git a/lib/userProfile.ts b/lib/userProfile.ts new file mode 100644 index 0000000..042f1ae --- /dev/null +++ b/lib/userProfile.ts @@ -0,0 +1,47 @@ +const STORAGE_KEY = "darshan_user_profile"; + +export type UserProfile = { + fullName?: string; + birthDate?: string; // YYYY-MM-DD + birthPlace?: string; + birthTime?: string; // HH:mm +}; + +export function loadUserProfile(): UserProfile { + if (typeof window === "undefined") return {}; + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw) as UserProfile; + return { + fullName: typeof parsed.fullName === "string" ? parsed.fullName : undefined, + birthDate: typeof parsed.birthDate === "string" ? parsed.birthDate : undefined, + birthPlace: typeof parsed.birthPlace === "string" ? parsed.birthPlace : undefined, + birthTime: typeof parsed.birthTime === "string" ? parsed.birthTime : undefined, + }; + } catch { + return {}; + } +} + +export function saveUserProfile(profile: UserProfile): void { + if (typeof window === "undefined") return; + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(profile)); + } catch { + // ignore + } +} + +export function hasProfileData(profile: UserProfile): boolean { + return !!( + (profile.fullName?.trim()) || + (profile.birthDate?.trim()) || + (profile.birthPlace?.trim()) || + (profile.birthTime?.trim()) + ); +} + +export function clearUserProfile(): void { + saveUserProfile({}); +} From 2f6f81a2ea58836ad8201632946f63342ba36c63 Mon Sep 17 00:00:00 2001 From: Raphael Date: Fri, 30 Jan 2026 02:13:17 -0300 Subject: [PATCH 7/9] ci: add app layout, pages, api routes and Next config for build --- app/api/admin/export-payments/route.ts | 57 ++ app/api/admin/export-usage/route.ts | 108 +++ app/api/admin/logs/route.ts | 58 ++ app/api/admin/status/route.ts | 34 + app/api/auth/callback/google/route.ts | 99 +++ app/api/auth/google/route.ts | 36 + app/api/auth/logout/route.ts | 20 + app/api/auth/send-code/route.ts | 32 + app/api/auth/session/route.ts | 15 + app/api/auth/verify/route.ts | 29 + app/api/checkout/create/route.ts | 106 +++ app/api/checkout/fulfill-mercadopago/route.ts | 107 +++ app/api/checkout/fulfill/route.ts | 117 +++ app/api/checkout/mercadopago/route.ts | 95 +++ app/api/config/route.ts | 96 +++ app/api/config/unlock/route.ts | 97 +++ app/api/credits/cost/route.ts | 21 + app/api/credits/dev-decrease/route.ts | 34 + app/api/credits/route.ts | 43 ++ app/api/darshan/check/route.ts | 25 + app/api/darshan/route.ts | 353 +++++++-- app/api/map/personal/route.ts | 299 ++++++++ app/api/webhooks/mercadopago/route.ts | 94 +++ app/api/webhooks/stripe/route.ts | 89 +++ app/components/CreditsModal.tsx | 139 ++++ app/components/CrystalOrb.tsx | 32 +- app/components/DarshanMessage.tsx | 80 +++ app/components/DarshanReveal.tsx | 35 +- app/components/IconMetatronCube.tsx | 36 + app/components/IconPlus.tsx | 18 + app/components/LoginModal.tsx | 200 ++++++ app/components/PersonalMapIcon.tsx | 42 ++ app/components/PersonalMapModal.tsx | 331 +++++++++ app/components/ProfileIcon.tsx | 34 + app/components/ProfilePanel.tsx | 171 +++++ app/components/SupportModal.tsx | 80 +++ app/components/TimeHeader.tsx | 91 ++- app/components/Tooltip.tsx | 45 ++ app/config/page.tsx | 477 ++++++++++++ app/globals.css | 44 ++ app/layout.tsx | 28 + app/monitor/page.tsx | 283 ++++++++ app/page.tsx | 677 ++++++++++++++++-- next-env.d.ts | 6 + next.config.ts | 12 + postcss.config.mjs | 9 + tailwind.config.ts | 25 + tsconfig.json | 41 ++ 48 files changed, 4886 insertions(+), 114 deletions(-) create mode 100644 app/api/admin/export-payments/route.ts create mode 100644 app/api/admin/export-usage/route.ts create mode 100644 app/api/admin/logs/route.ts create mode 100644 app/api/admin/status/route.ts create mode 100644 app/api/auth/callback/google/route.ts create mode 100644 app/api/auth/google/route.ts create mode 100644 app/api/auth/logout/route.ts create mode 100644 app/api/auth/send-code/route.ts create mode 100644 app/api/auth/session/route.ts create mode 100644 app/api/auth/verify/route.ts create mode 100644 app/api/checkout/create/route.ts create mode 100644 app/api/checkout/fulfill-mercadopago/route.ts create mode 100644 app/api/checkout/fulfill/route.ts create mode 100644 app/api/checkout/mercadopago/route.ts create mode 100644 app/api/config/route.ts create mode 100644 app/api/config/unlock/route.ts create mode 100644 app/api/credits/cost/route.ts create mode 100644 app/api/credits/dev-decrease/route.ts create mode 100644 app/api/credits/route.ts create mode 100644 app/api/darshan/check/route.ts create mode 100644 app/api/map/personal/route.ts create mode 100644 app/api/webhooks/mercadopago/route.ts create mode 100644 app/api/webhooks/stripe/route.ts create mode 100644 app/components/CreditsModal.tsx create mode 100644 app/components/DarshanMessage.tsx create mode 100644 app/components/IconMetatronCube.tsx create mode 100644 app/components/IconPlus.tsx create mode 100644 app/components/LoginModal.tsx create mode 100644 app/components/PersonalMapIcon.tsx create mode 100644 app/components/PersonalMapModal.tsx create mode 100644 app/components/ProfileIcon.tsx create mode 100644 app/components/ProfilePanel.tsx create mode 100644 app/components/SupportModal.tsx create mode 100644 app/components/Tooltip.tsx create mode 100644 app/config/page.tsx create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/monitor/page.tsx create mode 100644 next-env.d.ts create mode 100644 next.config.ts create mode 100644 postcss.config.mjs create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json diff --git a/app/api/admin/export-payments/route.ts b/app/api/admin/export-payments/route.ts new file mode 100644 index 0000000..6887afd --- /dev/null +++ b/app/api/admin/export-payments/route.ts @@ -0,0 +1,57 @@ +import { NextResponse } from "next/server"; +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +import { checkAdminAuth } from "@/lib/adminAuth"; +import { paymentsToCsv, type PaymentExportRow } from "@/lib/finance"; + +export const dynamic = "force-dynamic"; + +/** + * GET /api/admin/export-payments?key=CONFIG_SECRET + * Retorna CSV: user_id, amount_brl, credits_added, status, created_at + */ +export async function GET(req: Request) { + const auth = checkAdminAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + if (!isSupabaseConfigured()) { + return NextResponse.json( + { error: "Exportação requer SUPABASE_URL e SUPABASE_SERVICE_KEY." }, + { status: 503 } + ); + } + + const supabase = getSupabase(); + if (!supabase) { + return NextResponse.json({ error: "Supabase não disponível." }, { status: 503 }); + } + + const { data: payments, error } = await supabase + .from("payments") + .select("user_id, amount_brl, credits_added, status, created_at") + .order("created_at", { ascending: false }); + + if (error) { + return NextResponse.json({ error: "Falha ao buscar pagamentos." }, { status: 500 }); + } + + const { data: users } = await supabase.from("users").select("id, email"); + const idToEmail = new Map((users ?? []).map((u) => [u.id, u.email])); + + const rows: PaymentExportRow[] = (payments ?? []).map((p) => ({ + user_id: idToEmail.get(p.user_id) ?? p.user_id, + amount_brl: Number(p.amount_brl ?? 0), + credits_added: Number(p.credits_added ?? 0), + status: String(p.status ?? "pending"), + created_at: new Date(p.created_at).toISOString(), + })); + + const csv = paymentsToCsv(rows); + return new NextResponse(csv, { + status: 200, + headers: { + "Content-Type": "text/csv; charset=utf-8", + "Content-Disposition": `attachment; filename="darshan-payments-${Date.now()}.csv"`, + }, + }); +} diff --git a/app/api/admin/export-usage/route.ts b/app/api/admin/export-usage/route.ts new file mode 100644 index 0000000..c47d7c2 --- /dev/null +++ b/app/api/admin/export-usage/route.ts @@ -0,0 +1,108 @@ +import { NextResponse } from "next/server"; +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +import { checkAdminAuth } from "@/lib/adminAuth"; +import { usageToCsv, type UsageExportRow } from "@/lib/finance"; + +export const dynamic = "force-dynamic"; + +/** Preço médio por crédito em BRL (para revenue_estimate). */ +const AVG_REVENUE_PER_CREDIT_BRL = 0.40; + +/** + * GET /api/admin/export-usage?range=month&key=CONFIG_SECRET + * Query: range = day | week | month (default month) + * Retorna CSV: user_id, provider, total_calls, total_tokens, total_cost_brl, credits_spent, revenue_estimate + */ +export async function GET(req: Request) { + const auth = checkAdminAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + if (!isSupabaseConfigured()) { + return NextResponse.json( + { error: "Exportação requer SUPABASE_URL e SUPABASE_SERVICE_KEY." }, + { status: 503 } + ); + } + + const url = new URL(req.url); + const range = url.searchParams.get("range") ?? "month"; + const since = getSinceDate(range); + + const supabase = getSupabase(); + if (!supabase) { + return NextResponse.json({ error: "Supabase não disponível." }, { status: 503 }); + } + + const { data: logs, error: logError } = await supabase + .from("ai_usage_log") + .select("user_id, provider, input_tokens, output_tokens, total_tokens, cost_brl, credits_spent") + .gte("created_at", since.toISOString()); + + if (logError) { + return NextResponse.json({ error: "Falha ao buscar uso." }, { status: 500 }); + } + + const rows = (logs ?? []).reduce((acc, row) => { + const key = `${row.user_id}:${row.provider}`; + const existing = acc.get(key); + const costBrl = Number(row.cost_brl ?? 0); + const creditsSpent = Number(row.credits_spent ?? 0); + const totalTokens = Number(row.total_tokens ?? 0) || Number(row.input_tokens ?? 0) + Number(row.output_tokens ?? 0); + if (existing) { + existing.total_calls += 1; + existing.total_tokens += totalTokens; + existing.total_cost_brl += costBrl; + existing.credits_spent += creditsSpent; + } else { + acc.set(key, { + user_id: row.user_id, + provider: row.provider, + total_calls: 1, + total_tokens: totalTokens, + total_cost_brl: costBrl, + credits_spent: creditsSpent, + revenue_estimate: 0, + }); + } + return acc; + }, new Map()); + + const { data: users } = await supabase.from("users").select("id, email"); + const idToEmail = new Map((users ?? []).map((u) => [u.id, u.email])); + + const exportRows: UsageExportRow[] = Array.from(rows.values()).map((r) => ({ + user_id: idToEmail.get(r.user_id) ?? r.user_id, + provider: r.provider, + total_calls: r.total_calls, + total_tokens: r.total_tokens, + total_cost_brl: Math.round(r.total_cost_brl * 100) / 100, + credits_spent: r.credits_spent, + revenue_estimate: Math.round(r.credits_spent * AVG_REVENUE_PER_CREDIT_BRL * 100) / 100, + })); + + const csv = usageToCsv(exportRows); + return new NextResponse(csv, { + status: 200, + headers: { + "Content-Type": "text/csv; charset=utf-8", + "Content-Disposition": `attachment; filename="darshan-usage-${range}-${Date.now()}.csv"`, + }, + }); +} + +function getSinceDate(range: string): Date { + const now = new Date(); + switch (range) { + case "day": + now.setDate(now.getDate() - 1); + break; + case "week": + now.setDate(now.getDate() - 7); + break; + default: + now.setMonth(now.getMonth() - 1); + break; + } + return now; +} diff --git a/app/api/admin/logs/route.ts b/app/api/admin/logs/route.ts new file mode 100644 index 0000000..e4ba87b --- /dev/null +++ b/app/api/admin/logs/route.ts @@ -0,0 +1,58 @@ +import { NextResponse } from "next/server"; +import fs from "fs"; +import { checkAdminAuth } from "@/lib/adminAuth"; +import { getAppLogPath, getAuditLogPath } from "@/lib/logPaths"; + +export const dynamic = "force-dynamic"; + +const MAX_LINES = 500; +const MAX_FILE_BYTES = 1024 * 512; // 512 KB por arquivo + +function readLastLines(filePath: string, maxLines: number): string[] { + try { + if (!fs.existsSync(filePath)) return []; + const stat = fs.statSync(filePath); + const size = Math.min(stat.size, MAX_FILE_BYTES); + const fd = fs.openSync(filePath, "r"); + const buffer = Buffer.alloc(size); + const start = Math.max(0, stat.size - size); + fs.readSync(fd, buffer, 0, size, start); + fs.closeSync(fd); + const text = buffer.toString("utf8", 0, size); + const lines = text.split(/\n/).filter((l) => l.length > 0); + return lines.slice(-maxLines); + } catch { + return []; + } +} + +/** + * GET /api/admin/logs?source=app|audit|both&lines=200 + * Header: X-Config-Key ou Authorization: Bearer CONFIG_SECRET ou ?key=CONFIG_SECRET + * Retorna últimas N linhas de app.log e/ou audit.log. + */ +export async function GET(req: Request) { + const auth = checkAdminAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + + const url = new URL(req.url); + const source = url.searchParams.get("source") ?? "both"; + const linesParam = url.searchParams.get("lines"); + const lines = Math.min(MAX_LINES, Math.max(1, parseInt(linesParam ?? "200", 10) || 200)); + + const result: { app: string[]; audit: string[]; error?: string } = { + app: [], + audit: [], + }; + + if (source === "app" || source === "both") { + result.app = readLastLines(getAppLogPath(), lines); + } + if (source === "audit" || source === "both") { + result.audit = readLastLines(getAuditLogPath(), lines); + } + + return NextResponse.json(result); +} diff --git a/app/api/admin/status/route.ts b/app/api/admin/status/route.ts new file mode 100644 index 0000000..b6fcd00 --- /dev/null +++ b/app/api/admin/status/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from "next/server"; +import { checkAdminAuth } from "@/lib/adminAuth"; +import { isStripeConfigured } from "@/lib/stripe"; +import { isSupabaseConfigured } from "@/lib/supabase"; +import { getPlatformFeePercent } from "@/lib/finance"; +import { getRateLimitConfig, getDailyLimitConfig } from "@/lib/usageLimits"; + +export const dynamic = "force-dynamic"; + +/** + * GET /api/admin/status + * Header: X-Config-Key ou Authorization: Bearer CONFIG_SECRET ou ?key=CONFIG_SECRET + * Retorna estado da plataforma (sem segredos): Stripe, Supabase, limites, taxa. + */ +export async function GET(req: Request) { + const auth = checkAdminAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + + const rateLimit = getRateLimitConfig(); + const dailyLimit = getDailyLimitConfig(); + + const status = { + stripe: isStripeConfigured(), + supabase: isSupabaseConfigured(), + rateLimitPerMinute: rateLimit.perMinute, + dailyAiLimit: dailyLimit, + platformFeePercent: getPlatformFeePercent(), + nodeEnv: process.env.NODE_ENV ?? "development", + }; + + return NextResponse.json(status); +} diff --git a/app/api/auth/callback/google/route.ts b/app/api/auth/callback/google/route.ts new file mode 100644 index 0000000..38895b7 --- /dev/null +++ b/app/api/auth/callback/google/route.ts @@ -0,0 +1,99 @@ +import { NextRequest, NextResponse } from "next/server"; +import { headers } from "next/headers"; +import { sessionCookieHeader } from "@/lib/auth"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +const GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"; +const GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo"; + +function getRedirectUri(headersList: Headers): string { + const host = headersList.get("x-forwarded-host") || headersList.get("host") || "localhost:3000"; + const proto = headersList.get("x-forwarded-proto") || (host.includes("localhost") ? "http" : "https"); + return `${proto}://${host}/api/auth/callback/google`; +} + +export async function GET(req: NextRequest) { + const clientId = process.env.GOOGLE_CLIENT_ID; + const clientSecret = process.env.GOOGLE_CLIENT_SECRET; + if (!clientId || !clientSecret) { + return NextResponse.redirect(new URL("/?error=google_not_configured", req.url)); + } + const { searchParams } = new URL(req.url); + const code = searchParams.get("code"); + const error = searchParams.get("error"); + if (error) { + return NextResponse.redirect(new URL(`/?error=${encodeURIComponent(error)}`, req.url)); + } + if (!code) { + return NextResponse.redirect(new URL("/?error=missing_code", req.url)); + } + const headersList = await headers(); + const redirectUri = getRedirectUri(headersList); + + let tokenRes: Response; + try { + tokenRes = await fetch(GOOGLE_TOKEN_URL, { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + code, + client_id: clientId, + client_secret: clientSecret, + redirect_uri: redirectUri, + grant_type: "authorization_code", + }), + }); + } catch (e) { + console.error("[auth/callback/google] token exchange failed:", e); + return NextResponse.redirect(new URL("/?error=token_exchange_failed", req.url)); + } + + if (!tokenRes.ok) { + const text = await tokenRes.text(); + console.error("[auth/callback/google] token error:", tokenRes.status, text); + return NextResponse.redirect(new URL("/?error=token_failed", req.url)); + } + + let tokenData: { access_token?: string }; + try { + tokenData = await tokenRes.json(); + } catch { + return NextResponse.redirect(new URL("/?error=token_parse", req.url)); + } + const accessToken = tokenData.access_token; + if (!accessToken) { + return NextResponse.redirect(new URL("/?error=no_access_token", req.url)); + } + + let userRes: Response; + try { + userRes = await fetch(GOOGLE_USERINFO_URL, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + } catch (e) { + console.error("[auth/callback/google] userinfo failed:", e); + return NextResponse.redirect(new URL("/?error=userinfo_failed", req.url)); + } + + if (!userRes.ok) { + return NextResponse.redirect(new URL("/?error=userinfo_failed", req.url)); + } + + let userData: { email?: string }; + try { + userData = await userRes.json(); + } catch { + return NextResponse.redirect(new URL("/?error=userinfo_parse", req.url)); + } + const email = typeof userData.email === "string" ? userData.email.trim() : ""; + if (!email) { + return NextResponse.redirect(new URL("/?error=no_email", req.url)); + } + + audit("login_google", email); + const res = NextResponse.redirect(new URL("/", req.url)); + res.headers.set("Set-Cookie", sessionCookieHeader({ email })); + return res; +} diff --git a/app/api/auth/google/route.ts b/app/api/auth/google/route.ts new file mode 100644 index 0000000..5554fdc --- /dev/null +++ b/app/api/auth/google/route.ts @@ -0,0 +1,36 @@ +import { NextResponse } from "next/server"; +import { headers } from "next/headers"; + +export const dynamic = "force-dynamic"; + +const SCOPE = "email profile"; +const GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; + +function getRedirectUri(headersList: Headers): string { + const host = headersList.get("x-forwarded-host") || headersList.get("host") || "localhost:3000"; + const proto = headersList.get("x-forwarded-proto") || (host.includes("localhost") ? "http" : "https"); + return `${proto}://${host}/api/auth/callback/google`; +} + +export async function GET(req: Request) { + const clientId = process.env.GOOGLE_CLIENT_ID; + if (!clientId) { + const url = new URL(req.url); + const origin = url.origin || "http://localhost:3000"; + return NextResponse.redirect(`${origin}/?error=google_not_configured`); + } + const headersList = await headers(); + const redirectUri = getRedirectUri(headersList); + const state = Buffer.from(JSON.stringify({ t: Date.now() })).toString("base64url"); + const params = new URLSearchParams({ + client_id: clientId, + redirect_uri: redirectUri, + response_type: "code", + scope: SCOPE, + state, + access_type: "offline", + prompt: "consent", + }); + const url = `${GOOGLE_AUTH_URL}?${params.toString()}`; + return NextResponse.redirect(url); +} diff --git a/app/api/auth/logout/route.ts b/app/api/auth/logout/route.ts new file mode 100644 index 0000000..59b73b6 --- /dev/null +++ b/app/api/auth/logout/route.ts @@ -0,0 +1,20 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { clearSessionCookieHeader } from "@/lib/auth"; +import { clearCreditsCookieHeader } from "@/lib/credits"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +export async function POST() { + const cookieStore = await cookies(); + const session = getSessionFromCookie(cookieStore.toString()); + if (session?.email) { + audit("logout", session.email); + } + const res = NextResponse.json({ ok: true }); + res.headers.append("Set-Cookie", clearSessionCookieHeader()); + res.headers.append("Set-Cookie", clearCreditsCookieHeader()); + return res; +} diff --git a/app/api/auth/send-code/route.ts b/app/api/auth/send-code/route.ts new file mode 100644 index 0000000..5161ee7 --- /dev/null +++ b/app/api/auth/send-code/route.ts @@ -0,0 +1,32 @@ +import { NextResponse } from "next/server"; +import { setOtp } from "@/lib/otpStore"; +import { sendVerificationCode } from "@/lib/email"; + +export const dynamic = "force-dynamic"; + +export async function POST(req: Request) { + const body = await req.json().catch(() => ({})); + const email = typeof body.email === "string" ? body.email.trim() : ""; + if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + return NextResponse.json( + { ok: false, error: "E-mail inválido." }, + { status: 400 } + ); + } + const code = setOtp(email); + + const sent = await sendVerificationCode(email, code); + if (sent.ok) { + return NextResponse.json({ ok: true, message: "Código enviado para seu e-mail." }); + } + + if (process.env.NODE_ENV !== "production") { + console.log("[darshan] OTP para", email, ":", code, "(e-mail não enviado:", sent.error, ")"); + return NextResponse.json({ ok: true, message: "Código enviado para seu e-mail." }); + } + + return NextResponse.json( + { ok: false, error: "Não foi possível enviar o e-mail. Tente novamente ou use Google para entrar." }, + { status: 503 } + ); +} diff --git a/app/api/auth/session/route.ts b/app/api/auth/session/route.ts new file mode 100644 index 0000000..72ad447 --- /dev/null +++ b/app/api/auth/session/route.ts @@ -0,0 +1,15 @@ +import { NextResponse } from "next/server"; +import { getSessionFromCookie } from "@/lib/auth"; +import { cookies } from "next/headers"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json({ ok: false, session: null }); + } + return NextResponse.json({ ok: true, session: { email: session.email } }); +} diff --git a/app/api/auth/verify/route.ts b/app/api/auth/verify/route.ts new file mode 100644 index 0000000..f4f2d01 --- /dev/null +++ b/app/api/auth/verify/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from "next/server"; +import { verifyOtp, isDevCode } from "@/lib/otpStore"; +import { sessionCookieHeader } from "@/lib/auth"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +export async function POST(req: Request) { + const body = await req.json().catch(() => ({})); + const email = typeof body.email === "string" ? body.email.trim() : ""; + const code = typeof body.code === "string" ? body.code : ""; + if (!email || !code) { + return NextResponse.json( + { ok: false, error: "E-mail e código são obrigatórios." }, + { status: 400 } + ); + } + const valid = verifyOtp(email, code) || isDevCode(email, code); + if (!valid) { + return NextResponse.json( + { ok: false, error: "Código inválido ou expirado." }, + { status: 401 } + ); + } + audit("login_email", email); + const res = NextResponse.json({ ok: true }); + res.headers.set("Set-Cookie", sessionCookieHeader({ email })); + return res; +} diff --git a/app/api/checkout/create/route.ts b/app/api/checkout/create/route.ts new file mode 100644 index 0000000..0b1a0f2 --- /dev/null +++ b/app/api/checkout/create/route.ts @@ -0,0 +1,106 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { headers } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { getStripe, isStripeConfigured } from "@/lib/stripe"; +import { CREDIT_PACKAGES } from "@/lib/credits"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/checkout/create + * Body: { packageId: "13" | "21" | "34" | "55" | "89" } + * Cria sessão Stripe Checkout (cartão, Google Pay e Stripe Link). + * Google Pay aparece automaticamente quando "Wallets" está ativo no Dashboard do Stripe. + */ +export async function POST(req: Request) { + if (!isStripeConfigured()) { + return NextResponse.json( + { error: "Pagamento não configurado. Defina STRIPE_SECRET_KEY." }, + { status: 503 } + ); + } + + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session?.email) { + return NextResponse.json( + { error: "Faça login para comprar créditos." }, + { status: 401 } + ); + } + + let body: { packageId?: string }; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body inválido." }, { status: 400 }); + } + + const packageId = typeof body.packageId === "string" ? body.packageId.trim() : ""; + const pkg = CREDIT_PACKAGES.find((p) => p.id === packageId); + if (!pkg) { + return NextResponse.json( + { error: "Pacote inválido. Use 13, 21, 34, 55 ou 89." }, + { status: 400 } + ); + } + + const stripe = getStripe(); + if (!stripe) { + return NextResponse.json( + { error: "Pagamento indisponível." }, + { status: 503 } + ); + } + + const headersList = await headers(); + const host = headersList.get("x-forwarded-host") || headersList.get("host") || "localhost:3000"; + const proto = headersList.get("x-forwarded-proto") || (host.includes("localhost") ? "http" : "https"); + const origin = `${proto}://${host}`; + + try { + const checkoutSession = await stripe.checkout.sessions.create({ + mode: "payment", + payment_method_types: ["card", "link"], + line_items: [ + { + price_data: { + currency: "brl", + product_data: { + name: pkg.label, + description: "Créditos para revelações com IA no Darshan.", + }, + unit_amount: pkg.priceCents, + }, + quantity: 1, + }, + ], + success_url: `${origin}/?checkout=success&session_id={CHECKOUT_SESSION_ID}`, + cancel_url: `${origin}/?checkout=cancelled`, + client_reference_id: session.email, + metadata: { + packageId: pkg.id, + credits: String(pkg.amount), + }, + }); + + const url = checkoutSession.url; + if (!url) { + return NextResponse.json( + { error: "Não foi possível criar a sessão de pagamento." }, + { status: 500 } + ); + } + + return NextResponse.json({ url }); + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error("[checkout/create]", message); + return NextResponse.json( + { error: "Erro ao criar sessão de pagamento. Tente novamente." }, + { status: 500 } + ); + } +} diff --git a/app/api/checkout/fulfill-mercadopago/route.ts b/app/api/checkout/fulfill-mercadopago/route.ts new file mode 100644 index 0000000..fb89c29 --- /dev/null +++ b/app/api/checkout/fulfill-mercadopago/route.ts @@ -0,0 +1,107 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { getCreditsFromCookie, creditsCookieHeader } from "@/lib/credits"; +import { getPayment, isMercadoPagoConfigured } from "@/lib/mercadopago"; +import { addCreditsForPurchase } from "@/lib/finance"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/checkout/fulfill-mercadopago + * Body: { payment_id: string } + * Verifica o pagamento no Mercado Pago e adiciona créditos ao usuário. + */ +export async function POST(req: Request) { + if (!isMercadoPagoConfigured()) { + return NextResponse.json( + { error: "Pagamento não configurado.", balance: null }, + { status: 503 } + ); + } + + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session?.email) { + return NextResponse.json( + { error: "Faça login para receber os créditos.", balance: null }, + { status: 401 } + ); + } + + let body: { payment_id?: string }; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body inválido.", balance: null }, { status: 400 }); + } + + const paymentId = typeof body.payment_id === "string" ? body.payment_id.trim() : ""; + if (!paymentId) { + return NextResponse.json( + { error: "ID do pagamento inválido.", balance: null }, + { status: 400 } + ); + } + + const payment = await getPayment(paymentId); + if (!payment) { + return NextResponse.json( + { error: "Pagamento não encontrado.", balance: null }, + { status: 400 } + ); + } + + if (payment.status !== "approved") { + return NextResponse.json( + { error: "Pagamento ainda não aprovado.", balance: null }, + { status: 400 } + ); + } + + const externalRef = payment.external_reference ?? ""; + const match = externalRef.match(/^darshan:(.+):(\d+)$/); + const emailFromRef = match?.[1] ?? ""; + const creditsFromRef = match?.[2] ? parseInt(match[2], 10) : 0; + const credits = creditsFromRef > 0 ? creditsFromRef : parseInt(String(payment.metadata?.credits ?? "0"), 10); + + if (emailFromRef !== session.email) { + return NextResponse.json( + { error: "Pagamento não corresponde ao usuário logado.", balance: null }, + { status: 403 } + ); + } + + if (!Number.isFinite(credits) || credits <= 0) { + return NextResponse.json( + { error: "Pacote de créditos inválido.", balance: null }, + { status: 400 } + ); + } + + const current = getCreditsFromCookie(cookieHeader); + const amountBrl = Number(payment.transaction_amount) ?? 0; + + const result = await addCreditsForPurchase( + session.email, + credits, + amountBrl, + "mercadopago", + String(payment.id), + current + ); + const newBalance = result.newBalance; + + audit("credits_add", session.email, { + amount: credits, + balanceBefore: current, + balanceAfter: newBalance, + source: "mercadopago_checkout", + }); + + const res = NextResponse.json({ ok: true, balance: newBalance }); + res.headers.set("Set-Cookie", creditsCookieHeader(newBalance)); + return res; +} diff --git a/app/api/checkout/fulfill/route.ts b/app/api/checkout/fulfill/route.ts new file mode 100644 index 0000000..97b54d4 --- /dev/null +++ b/app/api/checkout/fulfill/route.ts @@ -0,0 +1,117 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { getCreditsFromCookie, creditsCookieHeader } from "@/lib/credits"; +import { getStripe, isStripeConfigured } from "@/lib/stripe"; +import { addCreditsForPurchase } from "@/lib/finance"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/checkout/fulfill + * Body: { sessionId: string } + * Verifica o pagamento Stripe e adiciona créditos ao usuário (cookie e, se configurado, Supabase). + * Só credita se o e-mail da sessão atual coincidir com client_reference_id do Checkout. + */ +export async function POST(req: Request) { + if (!isStripeConfigured()) { + return NextResponse.json( + { error: "Pagamento não configurado.", balance: null }, + { status: 503 } + ); + } + + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session?.email) { + return NextResponse.json( + { error: "Faça login para receber os créditos.", balance: null }, + { status: 401 } + ); + } + + let body: { sessionId?: string }; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body inválido.", balance: null }, { status: 400 }); + } + + const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : ""; + if (!sessionId || !sessionId.startsWith("cs_")) { + return NextResponse.json( + { error: "Sessão de pagamento inválida.", balance: null }, + { status: 400 } + ); + } + + const stripe = getStripe(); + if (!stripe) { + return NextResponse.json( + { error: "Pagamento indisponível.", balance: null }, + { status: 503 } + ); + } + + try { + const checkoutSession = await stripe.checkout.sessions.retrieve(sessionId, { + expand: [], + }); + + if (checkoutSession.payment_status !== "paid") { + return NextResponse.json( + { error: "Pagamento ainda não confirmado.", balance: null }, + { status: 400 } + ); + } + + const email = checkoutSession.client_reference_id ?? ""; + if (email !== session.email) { + return NextResponse.json( + { error: "Sessão não corresponde ao usuário logado.", balance: null }, + { status: 403 } + ); + } + + const credits = parseInt(String(checkoutSession.metadata?.credits ?? "0"), 10); + if (!Number.isFinite(credits) || credits <= 0) { + return NextResponse.json( + { error: "Pacote de créditos inválido.", balance: null }, + { status: 400 } + ); + } + + const current = getCreditsFromCookie(cookieHeader); + const amountBrl = Number(checkoutSession.amount_total ?? 0) / 100; + + const result = await addCreditsForPurchase( + session.email, + credits, + amountBrl, + "stripe", + sessionId, + current + ); + const newBalance = result.newBalance; + + audit("credits_add", session.email, { + amount: credits, + balanceBefore: current, + balanceAfter: newBalance, + source: "stripe_checkout", + }); + + const res = NextResponse.json({ ok: true, balance: newBalance }); + res.headers.set("Set-Cookie", creditsCookieHeader(newBalance)); + return res; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error("[checkout/fulfill]", message); + return NextResponse.json( + { error: "Não foi possível confirmar o pagamento.", balance: null }, + { status: 500 } + ); + } +} diff --git a/app/api/checkout/mercadopago/route.ts b/app/api/checkout/mercadopago/route.ts new file mode 100644 index 0000000..10c2ddd --- /dev/null +++ b/app/api/checkout/mercadopago/route.ts @@ -0,0 +1,95 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { headers } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { createPreference, isMercadoPagoConfigured } from "@/lib/mercadopago"; +import { CREDIT_PACKAGES } from "@/lib/credits"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/checkout/mercadopago + * Body: { packageId: "13" | "21" | "34" | "55" | "89" } + * Cria preferência Checkout Pro e retorna URL de redirecionamento (PIX, cartão, etc.). + */ +export async function POST(req: Request) { + if (!isMercadoPagoConfigured()) { + return NextResponse.json( + { error: "Pagamento não configurado. Defina MERCADOPAGO_ACCESS_TOKEN." }, + { status: 503 } + ); + } + + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session?.email) { + return NextResponse.json( + { error: "Faça login para comprar créditos." }, + { status: 401 } + ); + } + + let body: { packageId?: string }; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body inválido." }, { status: 400 }); + } + + const packageId = typeof body.packageId === "string" ? body.packageId.trim() : ""; + const pkg = CREDIT_PACKAGES.find((p) => p.id === packageId); + if (!pkg) { + return NextResponse.json( + { error: "Pacote inválido. Use 13, 21, 34, 55 ou 89." }, + { status: 400 } + ); + } + + const headersList = await headers(); + const host = headersList.get("x-forwarded-host") || headersList.get("host") || "localhost:3000"; + const proto = headersList.get("x-forwarded-proto") || (host.includes("localhost") ? "http" : "https"); + const origin = `${proto}://${host}`; + + const preference = await createPreference({ + items: [ + { + title: pkg.label, + quantity: 1, + unit_price: pkg.priceCents / 100, + currency_id: "BRL", + }, + ], + payer: { email: session.email }, + back_urls: { + success: `${origin}/?checkout=success&provider=mercadopago`, + failure: `${origin}/?checkout=failure`, + pending: `${origin}/?checkout=pending`, + }, + auto_return: "approved", + notification_url: `${origin}/api/webhooks/mercadopago`, + external_reference: `darshan:${session.email}:${pkg.amount}`, + metadata: { + packageId: pkg.id, + credits: String(pkg.amount), + email: session.email, + }, + }); + + if (!preference?.id) { + return NextResponse.json( + { error: "Não foi possível criar a preferência de pagamento." }, + { status: 500 } + ); + } + + const url = preference.init_point ?? preference.sandbox_init_point ?? null; + if (!url) { + return NextResponse.json( + { error: "Não foi possível obter a URL de pagamento." }, + { status: 500 } + ); + } + + return NextResponse.json({ url }); +} diff --git a/app/api/config/route.ts b/app/api/config/route.ts new file mode 100644 index 0000000..0709fa2 --- /dev/null +++ b/app/api/config/route.ts @@ -0,0 +1,96 @@ +import { NextResponse } from "next/server"; +import { getConfig, setConfig, type AppConfig, type ConfigFieldMode } from "@/lib/configStore"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +function getSecretFromRequest(req: Request): string | null { + const key = req.headers.get("x-config-key"); + if (key) return key; + const auth = req.headers.get("authorization"); + if (auth?.startsWith("Bearer ")) return auth.slice(7).trim(); + return null; +} + +function checkAuth(req: Request): { ok: true } | { ok: false; status: number; error: string } { + const secret = process.env.CONFIG_SECRET; + if (!secret) { + return { + ok: false, + status: 503, + error: "Configuração desativada. Defina CONFIG_SECRET em .env.local e reinicie o servidor.", + }; + } + const provided = getSecretFromRequest(req); + if (provided !== secret) { + return { ok: false, status: 401, error: "Código inválido." }; + } + return { ok: true }; +} + +/** + * GET /api/config — retorna a configuração atual (requer CONFIG_SECRET no header). + */ +export async function GET(req: Request) { + const auth = checkAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + const config = getConfig(); + return NextResponse.json(config); +} + +/** + * PUT /api/config — atualiza configuração (merge parcial). Requer CONFIG_SECRET. + */ +export async function PUT(req: Request) { + const auth = checkAuth(req); + if (!auth.ok) { + return NextResponse.json({ error: auth.error }, { status: auth.status }); + } + let body: Partial; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body JSON inválido." }, { status: 400 }); + } + const mode = (v: unknown): ConfigFieldMode => + v === "replace" || v === "complement" ? v : "complement"; + const partial: Partial = {}; + if (body.masterPromptOverride !== undefined) { + partial.masterPromptOverride = + typeof body.masterPromptOverride === "string" ? body.masterPromptOverride : null; + } + if (body.masterPromptMode !== undefined) partial.masterPromptMode = mode(body.masterPromptMode); + if (body.revelationInstructionOverride !== undefined) { + partial.revelationInstructionOverride = + typeof body.revelationInstructionOverride === "string" ? body.revelationInstructionOverride : null; + } + if (body.revelationInstructionMode !== undefined) partial.revelationInstructionMode = mode(body.revelationInstructionMode); + if (body.mockMessagesOverride !== undefined) { + partial.mockMessagesOverride = Array.isArray(body.mockMessagesOverride) + ? body.mockMessagesOverride.filter((x): x is string => typeof x === "string") + : null; + } + if (body.mockMessagesMode !== undefined) partial.mockMessagesMode = mode(body.mockMessagesMode); + if (body.readingInstructionOverride !== undefined) { + partial.readingInstructionOverride = + typeof body.readingInstructionOverride === "string" ? body.readingInstructionOverride : null; + } + if (body.readingInstructionMode !== undefined) partial.readingInstructionMode = mode(body.readingInstructionMode); + if (body.creditsPerRevelation !== undefined) { + const n = Number(body.creditsPerRevelation); + partial.creditsPerRevelation = Number.isFinite(n) && n >= 0 ? n : null; + } + if (body.creditsPerReading !== undefined) { + const n = Number(body.creditsPerReading); + partial.creditsPerReading = Number.isFinite(n) && n >= 0 ? n : null; + } + if (body.pricePerCreditCents !== undefined) { + const n = Number(body.pricePerCreditCents); + partial.pricePerCreditCents = Number.isFinite(n) && n >= 0 ? n : null; + } + const updated = setConfig(partial); + audit("config_update", "admin", { keys: Object.keys(partial) }); + return NextResponse.json(updated); +} diff --git a/app/api/config/unlock/route.ts b/app/api/config/unlock/route.ts new file mode 100644 index 0000000..2f678c7 --- /dev/null +++ b/app/api/config/unlock/route.ts @@ -0,0 +1,97 @@ +import { NextResponse } from "next/server"; +import { getConfig } from "@/lib/configStore"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/config/unlock + * Body: { secretCode: string, recaptchaToken: string } + * Verifica o reCAPTCHA com o Google e, se o código secreto estiver correto, retorna a config. + */ +export async function POST(req: Request) { + const secret = process.env.CONFIG_SECRET; + if (!secret) { + return NextResponse.json( + { error: "Configuração desativada. Defina CONFIG_SECRET." }, + { status: 503 } + ); + } + + const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY; + if (!recaptchaSecret) { + return NextResponse.json( + { error: "reCAPTCHA não configurado. Defina RECAPTCHA_SECRET_KEY." }, + { status: 503 } + ); + } + + let body: { secretCode?: string; recaptchaToken?: string }; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Body JSON inválido." }, { status: 400 }); + } + + const token = typeof body.recaptchaToken === "string" ? body.recaptchaToken.trim() : ""; + if (!token) { + return NextResponse.json( + { error: "Complete o reCAPTCHA." }, + { status: 400 } + ); + } + + const secretCode = typeof body.secretCode === "string" ? body.secretCode.trim() : ""; + if (!secretCode) { + return NextResponse.json( + { error: "Digite o código secreto." }, + { status: 400 } + ); + } + + let verifyRes: Response; + try { + verifyRes = await fetch("https://www.google.com/recaptcha/api/siteverify", { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + secret: recaptchaSecret, + response: token, + }), + }); + } catch (e) { + console.error("[config/unlock] reCAPTCHA verify failed:", e); + return NextResponse.json( + { error: "Não foi possível verificar o reCAPTCHA. Tente novamente." }, + { status: 502 } + ); + } + + let verifyData: { success?: boolean; "error-codes"?: string[] }; + try { + verifyData = await verifyRes.json(); + } catch { + return NextResponse.json( + { error: "Resposta inválida do reCAPTCHA." }, + { status: 502 } + ); + } + + if (!verifyData.success) { + return NextResponse.json( + { error: "reCAPTCHA inválido ou expirado. Tente novamente." }, + { status: 400 } + ); + } + + if (secretCode !== secret) { + return NextResponse.json( + { error: "Código inválido." }, + { status: 401 } + ); + } + + audit("config_unlock", "admin"); + const config = getConfig(); + return NextResponse.json(config); +} diff --git a/app/api/credits/cost/route.ts b/app/api/credits/cost/route.ts new file mode 100644 index 0000000..be37da2 --- /dev/null +++ b/app/api/credits/cost/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from "next/server"; +import { CREDITS_PER_AI_REQUEST, CREDITS_PER_PERSONAL_MAP } from "@/lib/credits"; +import { getConfig } from "@/lib/configStore"; +import { getPlatformFeePercent } from "@/lib/finance"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + const config = getConfig(); + const creditsPerRevelation = config.creditsPerRevelation ?? CREDITS_PER_AI_REQUEST; + const creditsPerReading = config.creditsPerReading ?? CREDITS_PER_PERSONAL_MAP; + const pricePerCreditCents = config.pricePerCreditCents ?? null; + const platformFeePercent = getPlatformFeePercent(); + return NextResponse.json({ + creditsPerRevelation, + creditsPerReading, + pricePerCreditCents, + platformFeePercent, + description: `Créditos por revelação: ${creditsPerRevelation}; por leitura: ${creditsPerReading}. Margem: ${platformFeePercent}%.`, + }); +} diff --git a/app/api/credits/dev-decrease/route.ts b/app/api/credits/dev-decrease/route.ts new file mode 100644 index 0000000..358d78e --- /dev/null +++ b/app/api/credits/dev-decrease/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { + getCreditsFromCookie, + creditsCookieHeader, + CREDITS_PER_AI_REQUEST, +} from "@/lib/credits"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/credits/dev-decrease + * Apenas em NODE_ENV=development: diminui créditos em 1 revelação (para testar fluxo). + */ +export async function POST() { + if (process.env.NODE_ENV !== "development") { + return NextResponse.json( + { ok: false, error: "Disponível apenas em desenvolvimento." }, + { status: 403 } + ); + } + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json({ ok: false, error: "Não autenticado.", balance: 0 }, { status: 401 }); + } + const current = getCreditsFromCookie(cookieHeader); + const newBalance = Math.max(0, current - CREDITS_PER_AI_REQUEST); + const res = NextResponse.json({ ok: true, balance: newBalance }); + res.headers.set("Set-Cookie", creditsCookieHeader(newBalance)); + return res; +} diff --git a/app/api/credits/route.ts b/app/api/credits/route.ts new file mode 100644 index 0000000..faf5c49 --- /dev/null +++ b/app/api/credits/route.ts @@ -0,0 +1,43 @@ +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getSessionFromCookie } from "@/lib/auth"; +import { + getCreditsFromCookie, + creditsCookieHeader, +} from "@/lib/credits"; +import { getCreditsBalance } from "@/lib/finance"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json({ ok: false, error: "Não autenticado.", balance: 0 }, { status: 401 }); + } + const fromCookie = getCreditsFromCookie(cookieHeader); + const balance = await getCreditsBalance(session.email, fromCookie); + return NextResponse.json({ ok: true, balance }); +} + +export async function POST(req: Request) { + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json({ ok: false, error: "Não autenticado." }, { status: 401 }); + } + const body = await req.json().catch(() => ({})); + const amount = typeof body.amount === "number" ? Math.floor(body.amount) : 0; + if (amount <= 0) { + return NextResponse.json({ ok: false, error: "Valor inválido." }, { status: 400 }); + } + const current = getCreditsFromCookie(cookieHeader); + const newBalance = current + amount; + audit("credits_add", session.email, { amount, balanceBefore: current, balanceAfter: newBalance }); + const res = NextResponse.json({ ok: true, balance: newBalance }); + res.headers.set("Set-Cookie", creditsCookieHeader(newBalance)); + return res; +} diff --git a/app/api/darshan/check/route.ts b/app/api/darshan/check/route.ts new file mode 100644 index 0000000..d39ce8d --- /dev/null +++ b/app/api/darshan/check/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from "next/server"; +import { getConnector } from "@/lib/ai"; + +export const dynamic = "force-dynamic"; + +/** + * GET /api/darshan/check + * Valida se há um conector de IA configurado (chave em .env.local). + * Aceita ?mock=1 para retornar ok: true (frontend usa mock e permite interação sem API). + */ +export async function GET(req: Request) { + const url = new URL(req.url); + const mockParam = url.searchParams.get("mock"); + if (mockParam === "1" || mockParam === "true") { + return NextResponse.json({ ok: true, provider: "mock" }); + } + const connector = getConnector(); + if (!connector) { + return NextResponse.json( + { ok: false, message: "Nenhum provedor de IA configurado. Defina OPENAI_API_KEY, GOOGLE_AI_API_KEY ou ANTHROPIC_API_KEY em .env.local. Ou use o toggle 'AI desligada' (mock) no topo da página." }, + { status: 503 } + ); + } + return NextResponse.json({ ok: true, provider: connector.id }); +} diff --git a/app/api/darshan/route.ts b/app/api/darshan/route.ts index 2ba9271..8199800 100644 --- a/app/api/darshan/route.ts +++ b/app/api/darshan/route.ts @@ -1,67 +1,302 @@ import { NextResponse } from "next/server"; -import OpenAI from "openai"; -import { loadMasterPrompt } from "../../../lib/darshanPrompt"; +import { cookies } from "next/headers"; +import { getConnector } from "@/lib/ai"; +import { loadMasterPrompt } from "@/lib/darshanPrompt"; +import { getConfig } from "@/lib/configStore"; +import { PHASE_NAMES } from "@/lib/darshan"; +import { getOfflineRevelation } from "@/lib/oracleOffline"; +import { getSessionFromCookie } from "@/lib/auth"; +import { + getCreditsFromCookie, + creditsCookieHeader, + CREDITS_PER_AI_REQUEST, +} from "@/lib/credits"; +import { + getCreditsBalance, + debitCredits, + logAiUsage, + estimateCost, + refreshUsdToBrlCache, + type AiUsageProvider, +} from "@/lib/finance"; +import { logger } from "@/lib/logger"; +import { checkAndRecordRateLimit, checkDailyLimit, recordDailyRequest } from "@/lib/usageLimits"; +import { isSupabaseConfigured } from "@/lib/supabase"; -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, -}); +export const dynamic = "force-dynamic"; + +function buildUserContext(userProfile: { + fullName?: string; + birthDate?: string; + birthPlace?: string; + birthTime?: string; +}): string { + const parts: string[] = []; + if (userProfile.fullName?.trim()) parts.push(`Nome completo: ${userProfile.fullName.trim()}`); + if (userProfile.birthDate?.trim()) parts.push(`Data de nascimento: ${userProfile.birthDate.trim()}`); + if (userProfile.birthPlace?.trim()) parts.push(`Local de nascimento: ${userProfile.birthPlace.trim()}`); + if (userProfile.birthTime?.trim()) parts.push(`Horário de nascimento: ${userProfile.birthTime.trim()}`); + if (parts.length === 0) return ""; + return `\n\nMapa do usuário (dados para interpretação, NÃO para citar literalmente):\n${parts.join("\n")}\n\nUse o mapa para derivar aspectos astrológicos, arquétipos, traços de personalidade, samskaras, karmas ou kleshas que aqueles dados sugerem — fale sobre o que esse mapa traz como resultado/theme, não repita o dado em si. Pode chamar o usuário pelo nome (primeiro nome) às vezes. Evite referências diretas aos dados (ex.: não cite cidade, data ou horário) e evite trocadilhos com nomes ou lugares.`; +} + +function buildHistoryContext(history: { userMessage?: string; darshanMessage: string }[]): string { + if (!history.length) return ""; + const lines = history.map( + (t) => `${t.userMessage ? `Usuário: ${t.userMessage}\n` : ""}Darshan: ${t.darshanMessage}` + ); + return `\n\nHistórico desta sessão (use como semente para uma NOVA resposta coerente; NUNCA repita frases ou blocos já ditos):\n${lines.join("\n\n")}`; +} + +const REVELATION_INSTRUCTION = `Elabore UMA única mensagem em português, orgânica e dinâmica, inspirada no espírito do Darshan (presença, tempo vivo, corpo, silêncio, devolução à consciência). Não siga ordem fixa nem liste tópicos; deixe o texto fluir em torno do mesmo tema (a pergunta ou intenção do usuário). + +OBRIGATÓRIO — retorno e elo de sentido: +- Dê um retorno ao usuário a partir do que o mapa e a pergunta sugerem: fale sobre aspectos astrológicos, arquétipos, traços de personalidade, samskaras, karmas ou kleshas identificados — o resultado ou o tema que aqueles dados trazem, não os dados em si. +- Pode chamar o usuário pelo nome (primeiro nome) às vezes, com naturalidade. +- Evite referências diretas aos dados (não cite cidade, data, horário) e evite trocadilhos com nome, local ou qualquer dado do mapa. Crie elo de sentido entre pergunta, histórico e a interpretação (arquétipos, karma, kleshas, tendências), não com os dados crus. + +Use quebras de linha (\\n\\n) apenas quando fizer sentido separar um bloco do outro para leitura (telas de transição). De 1 a no máximo 7 blocos; cada bloco = no máximo 1 ou 2 frases curtas. Nunca repita o que já foi dito no histórico; use o histórico e a nova pergunta como semente para uma revelação coerente e diferente.`; + +const PHASE_ROLES: Record = { + 1: "Dê UMA frase-oráculo (Luz): curta, poética, que abra o presente.", + 2: "Dê UMA mensagem sobre o Pulso Jyotish do agora: qualidade do tempo, fase lunar, momento do dia. Se tiver mapa do usuário, use para considerações astrológicas sutis.", + 3: "Dê UMA mensagem sobre o Arquétipo Chinês do ciclo anual vigente.", + 4: "Dê UMA mensagem sobre o Elemento predominante (Ayurveda: Terra/Água/Fogo/Ar/Éter) para este momento.", + 5: "Dê UMA mensagem sobre Consciência / guna (Sattva/Rajas/Tamas) em linguagem humana.", + 6: "Dê UMA prática corporal segura e mínima (30–90 segundos).", + 7: "Dê UMA pergunta final que devolva à presença (ex.: O que em você já sabe?).", +}; + +const MOCK_MESSAGES = [ + "O momento pede presença.\n\nRespire e sinta o que já está aqui.\n\nO que em você já sabe?", + "A luz do tempo não aponta para fora.\n\nEla revela o que já pulsa em você.\n\nPermita-se pausar.", + "O oráculo não adivinha — ele devolve.\n\nTraga a pergunta ao corpo.\n\nO que o silêncio responde?", + "Cada fase lunar traz um tom.\n\nEste instante pede escuta, não resposta.\n\nO que em você quer ser ouvido?", + "O mapa não define — sugere.\n\nVocê é maior que qualquer aspecto.\n\nRespire e deixe o agora falar.", +]; + +function getMockMessage(messages: string[] = MOCK_MESSAGES): string { + const list = messages.length > 0 ? messages : MOCK_MESSAGES; + const i = Math.floor(Math.random() * list.length); + return list[i] ?? list[0]; +} export async function POST(req: Request) { - const body = await req.json(); - const question = body.question || ""; - const mode = question ? "question" : "now"; - const masterPrompt = loadMasterPrompt(); - const fallbackSteps = [ - "O tempo pede suavidade.", - "Lua crescente: algo quer nascer devagar.", - "O ciclo coletivo favorece transformação.", - "Hoje há Ar: mente sensível.", - "Rajas está alto: simplifique.", - "Respire 4 vezes lentamente e sinta os pés.", - "O que em você já sabe?", - ]; - let steps = fallbackSteps; - let safetyNote = ""; - - if (!process.env.OPENAI_API_KEY) { - safetyNote = "OPENAI_API_KEY not set; returned fallback steps."; - } else { - try { - const completion = await openai.responses.create({ - model: "gpt-4o-mini", - input: [ - { - role: "system", - content: masterPrompt, - }, - { - role: "user", - content: question || "Pulse of Now", - }, - ], - }); - - const text = completion.output_text ?? ""; - const parsedSteps = text - .split("\n") - .map((line) => line.trim()) - .filter(Boolean) - .slice(0, 7); - if (parsedSteps.length === 7) { - steps = parsedSteps; - } else { - safetyNote = "OpenAI response incomplete; returned fallback steps."; + const body = await req.json().catch(() => ({})); + const useMock = body.mock === true; + const revelation = body.revelation === true; + const phase = revelation ? 1 : Math.min(7, Math.max(1, Number(body.phase) || 1)); + const userMessage = typeof body.userMessage === "string" ? body.userMessage.trim() : ""; + const history: { userMessage?: string; darshanMessage: string }[] = Array.isArray(body.history) + ? body.history + .filter( + (t: unknown) => + t && typeof t === "object" && "darshanMessage" in t && typeof (t as { darshanMessage: unknown }).darshanMessage === "string" + ) + .map((t: { userMessage?: string; darshanMessage: string }) => ({ + userMessage: typeof (t as { userMessage?: string }).userMessage === "string" ? (t as { userMessage: string }).userMessage : undefined, + darshanMessage: String((t as { darshanMessage: string }).darshanMessage), + })) + : []; + + const userProfile = body.userProfile && typeof body.userProfile === "object" + ? { + fullName: typeof body.userProfile.fullName === "string" ? body.userProfile.fullName : undefined, + birthDate: typeof body.userProfile.birthDate === "string" ? body.userProfile.birthDate : undefined, + birthPlace: typeof body.userProfile.birthPlace === "string" ? body.userProfile.birthPlace : undefined, + birthTime: typeof body.userProfile.birthTime === "string" ? body.userProfile.birthTime : undefined, } - } catch (error) { - console.warn("OpenAI request failed; returning fallback steps.", error); - safetyNote = "OpenAI request failed; returned fallback steps."; - } + : {}; + + const config = getConfig(); + const mockMessages = + config.mockMessagesOverride?.length + ? config.mockMessagesMode === "replace" + ? config.mockMessagesOverride + : [...MOCK_MESSAGES, ...config.mockMessagesOverride] + : MOCK_MESSAGES; + + // IA desativada (mock): 100% offline — NÃO chama getConnector() nem APIs externas. + if (useMock) { + const lastRevelations = history.slice(-5).map((t) => t.darshanMessage); + const phrases = lastRevelations.flatMap((msg) => + msg.split(/\n\n/).map((s) => s.trim()).filter(Boolean) + ); + const recentlyUsedPhrases = phrases.flatMap((p) => + /o que em você já sabe/i.test(p) ? [p, "O que em você já sabe?"] : [p] + ); + const message = getOfflineRevelation(userProfile, userMessage, recentlyUsedPhrases); + return NextResponse.json({ message: message || getMockMessage(mockMessages), phase: 1 } satisfies { message: string; phase: number }); + } + + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json( + { message: "Faça login para usar a IA.", phase: 1, needsLogin: true }, + { status: 401 } + ); + } + if (!checkAndRecordRateLimit(session.email)) { + return NextResponse.json( + { message: "Muitas requisições. Aguarde um minuto e tente novamente.", phase: 1 }, + { status: 429 } + ); } + const dailyLimit = await checkDailyLimit(session.email); + if (!dailyLimit.allowed) { + return NextResponse.json( + { + message: `Limite diário de revelações atingido (${dailyLimit.count}/${dailyLimit.limit}). Volte amanhã.`, + phase: 1, + }, + { status: 429 } + ); + } + const creditsPerRevelation = config.creditsPerRevelation ?? CREDITS_PER_AI_REQUEST; + const balanceFromCookie = getCreditsFromCookie(cookieHeader); + const balance = await getCreditsBalance(session.email, balanceFromCookie); + if (balance < creditsPerRevelation) { + return NextResponse.json( + { message: "Créditos insuficientes. Adicione créditos para usar a IA.", phase: 1, needsCredits: true }, + { status: 402 } + ); + } + // Só chamamos a IA quando há créditos suficientes; o débito ocorre apenas após retorno com sucesso (mais abaixo). + + const defaultMaster = loadMasterPrompt(); + const masterOverride = config.masterPromptOverride?.trim(); + const masterPrompt = + !masterOverride + ? defaultMaster + : config.masterPromptMode === "replace" + ? masterOverride + : `${defaultMaster}\n\n${masterOverride}`; + const defaultRevelation = REVELATION_INSTRUCTION; + const revelationOverride = config.revelationInstructionOverride?.trim(); + const revelationInstruction = + !revelationOverride + ? defaultRevelation + : config.revelationInstructionMode === "replace" + ? revelationOverride + : `${defaultRevelation}\n\n${revelationOverride}`; + const userContext = buildUserContext(userProfile); + const historyContext = buildHistoryContext(history); + + const connector = getConnector(); + if (!connector) { + const fallback = "O tempo pede presença. Respire e sinta o agora."; + return NextResponse.json({ message: fallback, phase: 1 } satisfies { message: string; phase: number }); + } + + try { + let userContent: string; + if (revelation) { + userContent = `${revelationInstruction}\n\n`; + if (userMessage) userContent += `Pergunta ou intenção do usuário: ${userMessage}\n\n`; + if (userContext) userContent += userContext; + if (historyContext) userContent += historyContext; + userContent += `\nRetorne APENAS um JSON válido, sem markdown: {"message": "sua mensagem em português (use \\n\\n entre blocos; 1 a 7 blocos; cada bloco = 1 ou 2 frases curtas)"}. Fale sobre aspectos/arquétipos/samskaras/karmas/kleshas que o mapa e a pergunta sugerem; pode usar o nome do usuário às vezes. Não cite dados do mapa literalmente nem faça trocadilhos. Mensagem orgânica, sempre nova e diferente do histórico.`; + } else { + const phaseRole = PHASE_ROLES[phase] ?? PHASE_NAMES[phase] ?? `Fase ${phase}.`; + userContent = `Diretriz para esta etapa: ${phaseRole}\n\n`; + if (userMessage) userContent += (phase === 1 ? "Pergunta ou intenção do usuário: " : "O que o usuário disse agora: ") + `${userMessage}\n\n`; + if (userContext) userContent += userContext; + if (historyContext) userContent += historyContext; + userContent += `\nRetorne APENAS um JSON válido, sem markdown: {"message": "sua resposta em português (use \\n\\n entre blocos; 1 a 7 blocos; cada bloco = 1 ou 2 frases curtas)", "phase": ${phase}}. Fale sobre aspectos/arquétipos/samskaras/karmas/kleshas que o mapa sugere; pode usar o nome do usuário às vezes. Não cite dados do mapa literalmente nem faça trocadilhos. Mensagem orgânica e SEMPRE nova; nunca repita o histórico.`; + } + + const result = await connector.complete(masterPrompt, userContent); + const raw = result.text; + + if (!raw) { + // IA não retornou conteúdo — não debitar créditos. + return NextResponse.json({ + message: "O momento pede silêncio. Deixe a resposta nascer em você.", + phase: 1, + } satisfies { message: string; phase: number }); + } - return NextResponse.json({ - mode, - steps, - image_prompt: null, - safety: { flags: [], note: safetyNote }, - }); + let parsed: { message?: string; phase?: number }; + try { + const cleaned = raw.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim(); + parsed = JSON.parse(cleaned) as { message?: string; phase?: number }; + } catch { + // Resposta da IA inválida (JSON quebrado) — não debitar créditos. + return NextResponse.json({ + message: raw.slice(0, 2000) || "Respire. O que em você já sabe?", + phase: 1, + } satisfies { message: string; phase: number }); + } + + const message = typeof parsed.message === "string" ? parsed.message.trim() : ""; + + // Provedor para log/custo: google -> gemini + const provider: AiUsageProvider = connector.id === "google" ? "gemini" : (connector.id as AiUsageProvider); + const modelName = + connector.id === "openai" + ? (process.env.OPENAI_MODEL ?? "gpt-4o-mini") + : connector.id === "anthropic" + ? (process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514") + : (process.env.GOOGLE_MODEL ?? "gemini-2.5-flash"); + const inputTokens = result.usage?.input_tokens ?? Math.ceil(userContent.length / 4); + const outputTokens = result.usage?.output_tokens ?? Math.ceil((message || raw).length / 4); + const totalTokens = inputTokens + outputTokens; + await refreshUsdToBrlCache(); + const { costUsd, costBrl } = estimateCost(provider, inputTokens, outputTokens); + const mode = revelation ? "question" : "now"; + + const usageLogId = await logAiUsage( + { + provider, + model: modelName, + inputTokens, + outputTokens, + totalTokens, + costUsd, + costBrl, + creditsSpent: creditsPerRevelation, + mode, + questionLength: userMessage.length, + responseLength: (message || raw).length, + success: true, + }, + { userEmail: session.email } + ); + + const debitResult = await debitCredits(session.email, creditsPerRevelation, "darshan_call", { + relatedUsageId: usageLogId ?? undefined, + currentBalanceFromCookie: balance, + }); + + if (!isSupabaseConfigured()) { + recordDailyRequest(session.email); + } + const res = NextResponse.json({ + message: message || "Respire. O que em você já sabe?", + phase: revelation ? 1 : (parsed.phase ?? phase), + creditsUsed: creditsPerRevelation, + balance: debitResult.newBalance, + } satisfies { message: string; phase: number; creditsUsed?: number; balance?: number }); + res.headers.set("Set-Cookie", creditsCookieHeader(debitResult.newBalance)); + return res; + } catch (err) { + // Erro de rede, timeout ou exceção da IA — não debitar créditos. + const errMessage = err instanceof Error ? err.message : String(err); + const errCode = err && typeof err === "object" && "status" in err ? (err as { status?: number }).status : null; + logger.error("darshan IA request failed", { + connector: connector.id, + message: errMessage, + status: errCode ?? undefined, + }); + const is429 = errCode === 429 || /quota|exceeded|limit/i.test(errMessage); + const message = is429 + ? "Limite de uso da API atingido. Adicione créditos em platform.openai.com (Billing) ou use outro provedor no .env.local." + : "O tempo pede pausa. Respire e tente novamente quando se sentir pronto."; + return NextResponse.json({ + message, + phase: 1, + } satisfies { message: string; phase: number }); + } } diff --git a/app/api/map/personal/route.ts b/app/api/map/personal/route.ts new file mode 100644 index 0000000..6193adb --- /dev/null +++ b/app/api/map/personal/route.ts @@ -0,0 +1,299 @@ +/** + * Mapa pessoal — resumo completo por IA (Jyotish, numerologia, arquétipos). + * Custa 9 créditos; pode ser refeito quantas vezes o usuário quiser. + */ + +import { NextResponse } from "next/server"; +import { cookies } from "next/headers"; +import { getConnector } from "@/lib/ai"; +import { getSessionFromCookie } from "@/lib/auth"; +import { + getCreditsFromCookie, + creditsCookieHeader, + CREDITS_PER_PERSONAL_MAP, +} from "@/lib/credits"; +import { getCreditsBalance, debitCredits, logAiUsage, estimateCost, refreshUsdToBrlCache } from "@/lib/finance"; +import type { AiUsageProvider } from "@/lib/finance"; +import { logger } from "@/lib/logger"; +import { getConfig } from "@/lib/configStore"; +import { getOfflineReading } from "@/lib/readingOffline"; +import { computeVedicChartSimplified } from "@/lib/knowledge/vedic"; +import { getRulingNumberFromName, getNumberTraits } from "@/lib/knowledge/numerology"; +import { RASHI_NAMES } from "@/lib/knowledge/archetypes"; +import type { RashiKey, NakshatraKey } from "@/lib/knowledge/types"; + +export const dynamic = "force-dynamic"; + +const ARCHETYPE_NAMES: Record = { + pioneiro: "Pioneiro", + raiz: "Raiz", + mensageiro: "Mensageiro", + cuidador: "Cuidador", + soberano: "Soberano", + servidor: "Servidor", + alquimista: "Alquimista", + guerreiro: "Guerreiro", + sábio: "Sábio", + realizador: "Realizador", + humanitário: "Humanitário", + dissolvente: "Dissolvente", +}; + +const NAKSHATRA_NAMES: Record = { + ashwini: "Ashwini", + bharani: "Bharani", + krittika: "Krittika", + rohini: "Rohini", + mrigashira: "Mrigashira", + ardra: "Ardra", + punarvasu: "Punarvasu", + pushya: "Pushya", + ashlesha: "Ashlesha", + magha: "Magha", + "purva-phalguni": "Purva Phalguni", + "uttara-phalguni": "Uttara Phalguni", + hasta: "Hasta", + chitra: "Chitra", + swati: "Swati", + vishakha: "Vishakha", + anuradha: "Anuradha", + jyestha: "Jyestha", + mula: "Mula", + "purva-ashadha": "Purva Ashadha", + "uttara-ashadha": "Uttara Ashadha", + shravana: "Shravana", + dhanishta: "Dhanishta", + shatabhisha: "Shatabhisha", + "purva-bhadra": "Purva Bhadra", + "uttara-bhadra": "Uttara Bhadra", + revati: "Revati", +}; + +function buildMapContext(profile: { + fullName?: string; + birthDate?: string; + birthPlace?: string; + birthTime?: string; +}): string { + const chart = computeVedicChartSimplified({ + birthDate: profile.birthDate, + birthTime: profile.birthTime, + }); + const rulingNumber = getRulingNumberFromName(profile.fullName ?? ""); + const numberTraits = getNumberTraits(rulingNumber); + + const parts: string[] = []; + parts.push(`Nome: ${profile.fullName?.trim() || "(não informado)"}`); + parts.push(`Data de nascimento: ${profile.birthDate?.trim() || "(não informado)"}`); + if (profile.birthPlace?.trim()) parts.push(`Local: ${profile.birthPlace.trim()}`); + if (profile.birthTime?.trim()) parts.push(`Horário: ${profile.birthTime.trim()}`); + + parts.push(""); + parts.push("--- Jyotish (mapa védico simplificado) ---"); + if (chart.moonRashi) { + const rashiName = RASHI_NAMES[chart.moonRashi as RashiKey] ?? chart.moonRashi; + parts.push(`Lua no signo (Rashi): ${rashiName}`); + } + if (chart.moonNakshatra) { + const naksName = NAKSHATRA_NAMES[chart.moonNakshatra as NakshatraKey] ?? chart.moonNakshatra; + parts.push(`Lua na estação lunar (Nakshatra): ${naksName}`); + } + if (chart.archetypeKeys?.length) { + const archetypeNames = chart.archetypeKeys.map((k) => ARCHETYPE_NAMES[k] ?? k).join(", "); + parts.push(`Arquétipos sugeridos pelo mapa: ${archetypeNames}`); + } + + parts.push(""); + parts.push("--- Numerologia (Pitágoras) ---"); + parts.push(`Número regente: ${rulingNumber} — ${numberTraits.name}`); + parts.push(`Traço curto: ${numberTraits.shortTrait}`); + parts.push(`Tendências: ${numberTraits.tendencies.join("; ")}`); + parts.push(`Desafios: ${numberTraits.challenges.join("; ")}`); + + return parts.join("\n"); +} + +const SYSTEM_PROMPT = `Você é um intérprete do Darshan. Sua tarefa é gerar uma leitura pessoal em português, harmoniosa e não repetitiva. + +INÍCIO OBRIGATÓRIO (uma ou duas frases): Explique de forma breve, simples e agradável o que foi feito — que a leitura integra Sol regente, Lua, planetas, estações lunares (Nakshatras) e numerologia, em uma síntese única. + +CORPO DA LEITURA (fluido, sem listas longas): +- Percorra o Sol regente, a Lua, cada planeta relevante e a convergência com Nakshatras e numerologia, sem repetir a mesma ideia. +- Use poucos termos em sânscrito; quando usar, explique em uma palavra ou deixe o contexto claro. Prefira português. +- Parágrafos curtos, quebras de linha (\\n\\n) quando fizer sentido. Nada de bullet points ou listas numeradas longas. +- Não cite dados crus (cidade, data ou horário); use só como base para interpretação. +- Linguagem clara, acolhedora e útil. Cada leitura deve ser única. + +Retorne APENAS um JSON válido, sem markdown nem texto antes ou depois: {"message": "seu texto completo aqui"}.`; + +export async function POST(req: Request) { + const cookieStore = await cookies(); + const cookieHeader = cookieStore.toString(); + const session = getSessionFromCookie(cookieHeader); + if (!session) { + return NextResponse.json( + { error: "Faça login para adquirir sua leitura.", needsLogin: true }, + { status: 401 } + ); + } + + let body: { profile?: { fullName?: string; birthDate?: string; birthPlace?: string; birthTime?: string }; offline?: boolean } = {}; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Corpo inválido." }, { status: 400 }); + } + + const profile = body.profile ?? {}; + const fullName = typeof profile.fullName === "string" ? profile.fullName.trim() : ""; + const birthDate = typeof profile.birthDate === "string" ? profile.birthDate.trim() : ""; + if (!fullName && !birthDate) { + return NextResponse.json( + { error: "Adicione nome e data de nascimento no seu perfil antes de gerar a leitura." }, + { status: 400 } + ); + } + + const useOffline = body.offline === true; + if (useOffline) { + const message = getOfflineReading(profile); + const balanceFromCookie = getCreditsFromCookie(cookieHeader); + const balance = await getCreditsBalance(session.email, balanceFromCookie); + return NextResponse.json({ + message, + balance, + creditsUsed: 0, + offline: true, + }); + } + + const readingConfig = getConfig(); + const creditsPerReading = readingConfig.creditsPerReading ?? CREDITS_PER_PERSONAL_MAP; + const balanceFromCookie = getCreditsFromCookie(cookieHeader); + const balance = await getCreditsBalance(session.email, balanceFromCookie); + if (balance < creditsPerReading) { + return NextResponse.json( + { + error: `Créditos insuficientes. A leitura custa ${creditsPerReading} créditos.`, + needsCredits: true, + balance, + }, + { status: 402 } + ); + } + + const connector = getConnector(); + if (!connector) { + // Em dev: retorna mock para teste sem debitar créditos + if (process.env.NODE_ENV === "development") { + const mapContext = buildMapContext(profile); + const mockMessage = `[Mock — dev] Leitura pessoal com base nos dados do perfil.\n\n${mapContext}\n\n--- Fim do mock. Em produção a IA gera o texto completo (Sol, Lua, planetas, Nakshatras, numerologia). ---`; + return NextResponse.json({ + message: mockMessage, + balance, + creditsUsed: 0, + }); + } + return NextResponse.json( + { error: "Serviço de IA indisponível. Tente mais tarde." }, + { status: 503 } + ); + } + + const mapContext = buildMapContext(profile); + const userContent = `Gere uma leitura pessoal completa com base no contexto abaixo.\n\n${mapContext}`; + + const readingOverride = readingConfig.readingInstructionOverride?.trim(); + const systemPrompt = + !readingOverride + ? SYSTEM_PROMPT + : readingConfig.readingInstructionMode === "replace" + ? readingOverride + : `${SYSTEM_PROMPT}\n\n${readingOverride}`; + + try { + const result = await connector.complete(systemPrompt, userContent); + const raw = result.text; + + if (!raw) { + return NextResponse.json( + { error: "A IA não retornou conteúdo. Tente novamente." }, + { status: 500 } + ); + } + + function extractMessageFromRaw(raw: string): string { + let s = raw.trim().replace(/^=>\s*/i, "").replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim(); + try { + const parsed = JSON.parse(s) as { message?: string }; + if (typeof parsed.message === "string") return parsed.message.trim(); + } catch { + const idx = s.search(/"message"\s*:\s*"/i); + if (idx !== -1) { + const start = s.indexOf('"', idx + 10) + 1; + let end = start; + for (let i = start; i < s.length; i++) { + if (s[i] === "\\" && s[i + 1] === '"') { i++; continue; } + if (s[i] === '"') { end = i; break; } + } + const extracted = s.slice(start, end).replace(/\\"/g, '"').replace(/\\n/g, "\n"); + if (extracted.length > 0) return extracted; + } + } + return s.replace(/\}\s*$/, "").trim(); + } + + const message = extractMessageFromRaw(raw).slice(0, 15000) || raw.slice(0, 15000); + + const provider: AiUsageProvider = connector.id === "google" ? "gemini" : (connector.id as AiUsageProvider); + const modelName = + connector.id === "openai" + ? (process.env.OPENAI_MODEL ?? "gpt-4o-mini") + : connector.id === "anthropic" + ? (process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-20250514") + : (process.env.GOOGLE_MODEL ?? "gemini-2.5-flash"); + const inputTokens = result.usage?.input_tokens ?? Math.ceil((systemPrompt.length + userContent.length) / 4); + const outputTokens = result.usage?.output_tokens ?? Math.ceil(message.length / 4); + const totalTokens = inputTokens + outputTokens; + await refreshUsdToBrlCache(); + const { costUsd, costBrl } = estimateCost(provider, inputTokens, outputTokens); + + const usageLogId = await logAiUsage( + { + provider, + model: modelName, + inputTokens, + outputTokens, + totalTokens, + costUsd, + costBrl, + creditsSpent: creditsPerReading, + mode: "personal_map", + questionLength: userContent.length, + responseLength: message.length, + success: true, + }, + { userEmail: session.email } + ); + + const debitResult = await debitCredits(session.email, creditsPerReading, "personal_map", { + relatedUsageId: usageLogId ?? undefined, + currentBalanceFromCookie: balance, + }); + + const res = NextResponse.json({ + message, + balance: debitResult.newBalance, + creditsUsed: creditsPerReading, + }); + res.headers.set("Set-Cookie", creditsCookieHeader(debitResult.newBalance)); + return res; + } catch (err) { + const errMessage = err instanceof Error ? err.message : String(err); + logger.error("personal map IA request failed", { message: errMessage }); + return NextResponse.json( + { error: "Não foi possível gerar a leitura. Tente novamente." }, + { status: 500 } + ); + } +} diff --git a/app/api/webhooks/mercadopago/route.ts b/app/api/webhooks/mercadopago/route.ts new file mode 100644 index 0000000..458336c --- /dev/null +++ b/app/api/webhooks/mercadopago/route.ts @@ -0,0 +1,94 @@ +import { NextResponse } from "next/server"; +import { getPayment, isMercadoPagoConfigured } from "@/lib/mercadopago"; +import { addCreditsForPurchase } from "@/lib/finance"; +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +type WebhookBody = { + type?: string; + data?: { id?: string }; +}; + +/** + * POST /api/webhooks/mercadopago + * Recebe notificações do Mercado Pago (payment). Busca o pagamento, verifica se está + * aprovado e se ainda não foi processado; em seguida adiciona créditos ao usuário. + * Responde 200 rapidamente para evitar retentativas desnecessárias. + */ +export async function POST(req: Request) { + if (!isMercadoPagoConfigured()) { + return NextResponse.json({ received: true }, { status: 200 }); + } + + let body: WebhookBody; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Invalid body" }, { status: 400 }); + } + + if (body.type !== "payment" || typeof body.data?.id !== "string") { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const paymentId = String(body.data.id).trim(); + if (!paymentId) return NextResponse.json({ received: true }, { status: 200 }); + + const payment = await getPayment(paymentId); + if (!payment) { + return NextResponse.json({ received: true }, { status: 200 }); + } + + if (payment.status !== "approved") { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const supabase = getSupabase(); + if (supabase && isSupabaseConfigured()) { + const { data: existing } = await supabase + .from("payments") + .select("id") + .eq("provider", "mercadopago") + .eq("external_id", paymentId) + .maybeSingle(); + if (existing) { + return NextResponse.json({ received: true }, { status: 200 }); + } + } + + const externalRef = payment.external_reference ?? ""; + const match = externalRef.match(/^darshan:(.+):(\d+)$/); + const emailFromRef = match?.[1] ?? ""; + const creditsFromRef = match?.[2] ? parseInt(match[2], 10) : 0; + const credits = + creditsFromRef > 0 ? creditsFromRef : parseInt(String(payment.metadata?.credits ?? "0"), 10); + + if (!emailFromRef || !Number.isFinite(credits) || credits <= 0) { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const amountBrl = Number(payment.transaction_amount) ?? 0; + const currentBalance = 0; + + try { + await addCreditsForPurchase( + emailFromRef, + credits, + amountBrl, + "mercadopago", + String(payment.id), + currentBalance + ); + audit("credits_add", emailFromRef, { + amount: credits, + source: "mercadopago_webhook", + payment_id: paymentId, + }); + } catch (err) { + console.error("[webhooks/mercadopago]", err); + } + + return NextResponse.json({ received: true }, { status: 200 }); +} diff --git a/app/api/webhooks/stripe/route.ts b/app/api/webhooks/stripe/route.ts new file mode 100644 index 0000000..c98ea99 --- /dev/null +++ b/app/api/webhooks/stripe/route.ts @@ -0,0 +1,89 @@ +import { NextResponse } from "next/server"; +import Stripe from "stripe"; +import { getStripe, isStripeConfigured } from "@/lib/stripe"; +import { addCreditsForPurchase } from "@/lib/finance"; +import { getSupabase, isSupabaseConfigured } from "@/lib/supabase"; +import { audit } from "@/lib/audit"; + +export const dynamic = "force-dynamic"; + +/** + * POST /api/webhooks/stripe + * Recebe eventos do Stripe (ex.: checkout.session.completed). + * Credita o usuário mesmo se ele não retornar à página de sucesso (Google Pay, fechou aba, etc.). + * Configure STRIPE_WEBHOOK_SECRET no Stripe Dashboard (Webhooks > Add endpoint > signing secret). + */ +export async function POST(req: Request) { + const secret = process.env.STRIPE_WEBHOOK_SECRET; + if (!secret?.trim() || !isStripeConfigured()) { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const stripe = getStripe(); + if (!stripe) return NextResponse.json({ received: true }, { status: 200 }); + + let payload: string; + try { + payload = await req.text(); + } catch { + return NextResponse.json({ error: "Invalid body" }, { status: 400 }); + } + + const sig = req.headers.get("stripe-signature"); + if (!sig) { + return NextResponse.json({ error: "Missing stripe-signature" }, { status: 400 }); + } + + let event: Stripe.Event; + try { + event = stripe.webhooks.constructEvent(payload, sig.trim(), secret.trim()); + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + console.error("[webhooks/stripe] Signature verification failed:", message); + return NextResponse.json({ error: "Webhook signature verification failed" }, { status: 400 }); + } + + if (event.type !== "checkout.session.completed") { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const session = event.data.object as Stripe.Checkout.Session; + if (session.payment_status !== "paid") { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const sessionId = session.id ?? ""; + const email = typeof session.client_reference_id === "string" ? session.client_reference_id : ""; + const credits = parseInt(String(session.metadata?.credits ?? "0"), 10); + const amountBrl = Number(session.amount_total ?? 0) / 100; + + if (!email || !Number.isFinite(credits) || credits <= 0) { + return NextResponse.json({ received: true }, { status: 200 }); + } + + const supabase = getSupabase(); + if (supabase && isSupabaseConfigured()) { + const { data: existing } = await supabase + .from("payments") + .select("id") + .eq("provider", "stripe") + .eq("external_id", sessionId) + .maybeSingle(); + if (existing) { + return NextResponse.json({ received: true }, { status: 200 }); + } + } + + try { + await addCreditsForPurchase(email, credits, amountBrl, "stripe", sessionId, 0); + audit("credits_add", email, { + amount: credits, + source: "stripe_webhook", + session_id: sessionId, + }); + } catch (err) { + console.error("[webhooks/stripe]", err); + } + + return NextResponse.json({ received: true }, { status: 200 }); +} diff --git a/app/components/CreditsModal.tsx b/app/components/CreditsModal.tsx new file mode 100644 index 0000000..2de13a3 --- /dev/null +++ b/app/components/CreditsModal.tsx @@ -0,0 +1,139 @@ +"use client"; + +import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + CREDIT_PACKAGES, + formatPriceBRL, +} from "../../lib/credits"; + +type Props = { + isOpen: boolean; + onClose: () => void; + onPurchased: (newBalance: number) => void; + /** Créditos por revelação (configurável na página secreta). */ + creditsPerRevelation: number; + /** Créditos por leitura (configurável na página secreta). */ + creditsPerReading: number; +}; + +export default function CreditsModal({ isOpen, onClose, onPurchased, creditsPerRevelation, creditsPerReading }: Props) { + const [selectedId, setSelectedId] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + async function handlePurchase() { + const pkg = CREDIT_PACKAGES.find((p) => p.id === selectedId); + if (!pkg) { + setError("Escolha um pacote."); + return; + } + setError(""); + setLoading(true); + try { + const body = { packageId: pkg.id }; + const opts = { + method: "POST" as const, + headers: { "Content-Type": "application/json" }, + credentials: "include" as const, + body: JSON.stringify(body), + }; + let res = await fetch("/api/checkout/mercadopago", opts); + let data: { url?: string; error?: string } = await res.json().catch(() => ({})); + if (!res.ok || !data?.url) { + res = await fetch("/api/checkout/create", opts); + data = await res.json().catch(() => ({})); + } + if (res.ok && typeof data?.url === "string") { + window.location.href = data.url; + return; + } + setError(data?.error ?? "Nenhum pagamento configurado. Configure Stripe ou Mercado Pago."); + } catch { + setError("Erro de conexão. Tente novamente."); + } finally { + setLoading(false); + } + } + + if (!isOpen) return null; + + return ( + <> + +
+ e.stopPropagation()} + > +
+

Recarregar créditos

+ +
+

+ Revelação com IA: {creditsPerRevelation} crédito{creditsPerRevelation !== 1 ? "s" : ""}. Leitura completa: {creditsPerReading} créditos. +

+ +
+ {CREDIT_PACKAGES.map((pkg) => ( + + ))} +
+ + {error &&

{error}

} + +
+ + +
+ +

+ Pagamento seguro: Mercado Pago (PIX, cartão) ou Stripe (cartão, Google Pay). Você será redirecionado ao checkout. +

+
+
+ + ); +} diff --git a/app/components/CrystalOrb.tsx b/app/components/CrystalOrb.tsx index 19c64a0..038717a 100644 --- a/app/components/CrystalOrb.tsx +++ b/app/components/CrystalOrb.tsx @@ -1,9 +1,31 @@ -export default function CrystalOrb() { +"use client"; + +import { motion } from "framer-motion"; + +type Props = { + isRevealing?: boolean; + onClick?: () => void; + clickable?: boolean; +}; + +export default function CrystalOrb({ isRevealing = false, onClick, clickable = false }: Props) { + const Wrapper = clickable ? "button" : "div"; return ( -
-
+ -
+ ); } diff --git a/app/components/DarshanMessage.tsx b/app/components/DarshanMessage.tsx new file mode 100644 index 0000000..258cd7e --- /dev/null +++ b/app/components/DarshanMessage.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { useState, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; + +const FADE_DURATION_MS = 600; +const FADE_DURATION_S = FADE_DURATION_MS / 1000; +const MAX_STEPS = 7; + +type Props = { + message: string; + onComplete?: () => void; +}; + +function splitIntoSteps(message: string): string[] { + const trimmed = message.trim(); + if (!trimmed) return []; + const raw = trimmed.split(/\n\n+/).map((s) => s.trim()).filter(Boolean); + const steps = raw.length > 0 ? raw : [trimmed]; + if (steps.length <= MAX_STEPS) return steps; + return [ + ...steps.slice(0, MAX_STEPS - 1), + steps.slice(MAX_STEPS - 1).join("\n\n"), + ]; +} + +export default function DarshanMessage({ message, onComplete }: Props) { + const steps = splitIntoSteps(message); + const [currentIndex, setCurrentIndex] = useState(0); + const [exiting, setExiting] = useState(false); + const onCompleteRef = useRef(onComplete); + onCompleteRef.current = onComplete; + + const isLast = currentIndex >= steps.length - 1; + + useEffect(() => { + if (!exiting) return; + const t = setTimeout(() => { + onCompleteRef.current?.(); + }, FADE_DURATION_MS); + return () => clearTimeout(t); + }, [exiting]); + + function handleClick() { + if (isLast) { + setExiting(true); + } else { + setCurrentIndex((i) => i + 1); + } + } + + if (steps.length === 0) { + onComplete?.(); + return null; + } + + return ( +
+ + !exiting && e.key === "Enter" && handleClick()} + initial={{ opacity: 0, y: 16 }} + animate={exiting ? { opacity: 0, y: -12 } : { opacity: 1, y: 0 }} + exit={{ opacity: 0, y: -12 }} + transition={{ duration: FADE_DURATION_S, ease: [0.25, 0.1, 0.25, 1] }} + className="text-center w-full cursor-pointer rounded-2xl px-6 py-8 focus:outline-none" + aria-label={exiting ? undefined : isLast ? "Toque para voltar" : "Toque para a próxima"} + > +

+ {steps[currentIndex]} +

+
+
+
+ ); +} diff --git a/app/components/DarshanReveal.tsx b/app/components/DarshanReveal.tsx index e49f560..c740965 100644 --- a/app/components/DarshanReveal.tsx +++ b/app/components/DarshanReveal.tsx @@ -1,17 +1,42 @@ "use client"; +import { useEffect } from "react"; +import { motion } from "framer-motion"; + +const REVEAL_DELAY_MS = 900; +const DURATION_MS = 600; + type Props = { steps: string[]; + onComplete?: () => void; }; -export default function DarshanReveal({ steps }: Props) { +export default function DarshanReveal({ steps, onComplete }: Props) { + useEffect(() => { + if (!onComplete || steps.length === 0) return; + const lastIndex = steps.length - 1; + const totalMs = lastIndex * REVEAL_DELAY_MS + DURATION_MS; + const t = setTimeout(onComplete, totalMs); + return () => clearTimeout(t); + }, [steps.length, onComplete]); + return ( -
+
    {steps.map((line, index) => ( -

    + {line} -

    + ))} -
+ ); } diff --git a/app/components/IconMetatronCube.tsx b/app/components/IconMetatronCube.tsx new file mode 100644 index 0000000..ea7e48a --- /dev/null +++ b/app/components/IconMetatronCube.tsx @@ -0,0 +1,36 @@ +"use client"; + +/** Ícone vetorial simplificado do cubo de Metatron (geometria sagrada). */ +export default function IconMetatronCube({ className }: { className?: string }) { + return ( + + {/* Centro */} + + {/* 6 círculos em hexágono ao redor do centro */} + + + + + + + {/* Linhas do centro aos 6 vértices */} + + + + + + + {/* Hexágono externo (conecta os 6 círculos) */} + + + ); +} diff --git a/app/components/IconPlus.tsx b/app/components/IconPlus.tsx new file mode 100644 index 0000000..1c41317 --- /dev/null +++ b/app/components/IconPlus.tsx @@ -0,0 +1,18 @@ +"use client"; + +/** Plus minimalista — duas linhas finas cruzadas. */ +export default function IconPlus({ className }: { className?: string }) { + return ( + + + + ); +} diff --git a/app/components/LoginModal.tsx b/app/components/LoginModal.tsx new file mode 100644 index 0000000..f001d00 --- /dev/null +++ b/app/components/LoginModal.tsx @@ -0,0 +1,200 @@ +"use client"; + +import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import Tooltip from "./Tooltip"; + +type Step = "email" | "code"; + +type Props = { + isOpen: boolean; + onClose: () => void; + onSuccess: () => void; +}; + +export default function LoginModal({ isOpen, onClose, onSuccess }: Props) { + const [step, setStep] = useState("email"); + const [email, setEmail] = useState(""); + const [code, setCode] = useState(""); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + async function handleSendCode(e: React.FormEvent) { + e.preventDefault(); + setError(""); + setLoading(true); + try { + const res = await fetch("/api/auth/send-code", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: email.trim() }), + }); + const data = await res.json(); + if (!res.ok) { + setError(data.error ?? "Erro ao enviar código."); + return; + } + setStep("code"); + } catch { + setError("Erro de conexão."); + } finally { + setLoading(false); + } + } + + async function handleVerify(e: React.FormEvent) { + e.preventDefault(); + setError(""); + setLoading(true); + try { + const res = await fetch("/api/auth/verify", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email: email.trim(), code: code.trim() }), + credentials: "include", + }); + const data = await res.json(); + if (!res.ok) { + setError(data.error ?? "Código inválido."); + return; + } + onSuccess(); + onClose(); + } catch { + setError("Erro de conexão."); + } finally { + setLoading(false); + } + } + + if (!isOpen) return null; + + return ( + <> + +
+ e.stopPropagation()} + > +
+

Entrar

+ +
+

+ Sem senha: informe seu e-mail e use o código que enviarmos para acessar. +

+ + + {step === "email" ? ( + + setEmail(e.target.value)} + placeholder="seu@email.com" + required + className="w-full bg-transparent border-0 border-b border-white/25 rounded-none px-0 py-2.5 text-sm text-mist placeholder:text-white/40 outline-none focus:border-white/50 transition-colors mb-4" + /> + {error &&

{error}

} + +
+ ) : ( + +

Código enviado para {email}

+ setCode(e.target.value.replace(/\D/g, ""))} + placeholder="000000" + required + className="w-full bg-transparent border-0 border-b border-white/25 rounded-none px-0 py-2.5 text-sm text-mist placeholder:text-white/40 outline-none focus:border-white/50 transition-colors mb-4 tracking-widest" + /> + {error &&

{error}

} +
+ + +
+

+ Em desenvolvimento: use código 123456 para testar. +

+
+ )} +
+ +
+

Ou entre com

+
+ + + Google + + + + + +
+
+
+
+ + ); +} diff --git a/app/components/PersonalMapIcon.tsx b/app/components/PersonalMapIcon.tsx new file mode 100644 index 0000000..2e9648c --- /dev/null +++ b/app/components/PersonalMapIcon.tsx @@ -0,0 +1,42 @@ +"use client"; + +import { motion } from "framer-motion"; +import Tooltip from "./Tooltip"; + +type Props = { + onClick: () => void; +}; + +/** Ícone minimalista de mapa (documento/folha) — posicionado abaixo do ícone de cadastro. */ +export default function PersonalMapIcon({ onClick }: Props) { + return ( + + + + + + + + + + + + ); +} diff --git a/app/components/PersonalMapModal.tsx b/app/components/PersonalMapModal.tsx new file mode 100644 index 0000000..3dedb4b --- /dev/null +++ b/app/components/PersonalMapModal.tsx @@ -0,0 +1,331 @@ +"use client"; + +import { useState } from "react"; +import { motion } from "framer-motion"; +import type { UserProfile } from "@/lib/userProfile"; + +/** Ícone minimalista de copiar (dois retângulos). */ +function IconCopy({ className }: { className?: string }) { + return ( + + + + + ); +} + +/** Ícone minimalista de check (copiado). */ +function IconCheck({ className }: { className?: string }) { + return ( + + + + ); +} + +/** Remove prefixos/sufixos de JSON que às vezes vêm na resposta da IA (ex.: => {"message": ... }). */ +function cleanReadingMessage(raw: string): string { + let s = raw.trim().replace(/^=>\s*/i, "").trim(); + try { + const parsed = JSON.parse(s) as { message?: string }; + if (typeof parsed.message === "string") return parsed.message.trim(); + } catch { + // Tentar extrair o valor de "message" manualmente + const idx = s.search(/"message"\s*:\s*"/i); + if (idx !== -1) { + const start = s.indexOf('"', idx + 10) + 1; + let end = start; + for (let i = start; i < s.length; i++) { + if (s[i] === "\\" && s[i + 1] === '"') { i++; continue; } + if (s[i] === '"') { end = i; break; } + } + const extracted = s.slice(start, end).replace(/\\"/g, '"').replace(/\\n/g, "\n"); + if (extracted.length > 0) return extracted; + } + } + return s.replace(/\}\s*$/, "").trim(); +} + +type Props = { + isOpen: boolean; + onClose: () => void; + profile: UserProfile; + credits: number; + /** Créditos por leitura (configurável na página secreta). */ + creditsPerReading: number; + /** Quando true, envia offline: true e a leitura é gerada sem IA e sem custo. */ + offlineMode?: boolean; + onBalanceUpdate: (newBalance: number) => void; + onOpenCredits: () => void; +}; + +export default function PersonalMapModal({ + isOpen, + onClose, + profile, + credits, + creditsPerReading, + offlineMode = false, + onBalanceUpdate, + onOpenCredits, +}: Props) { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + const [result, setResult] = useState(null); + const [copyDone, setCopyDone] = useState(false); + const [confirmNewReading, setConfirmNewReading] = useState(false); + + const hasEnoughCredits = credits >= creditsPerReading; + const canGenerate = + (offlineMode || hasEnoughCredits) && profile && (profile.fullName?.trim() || profile.birthDate?.trim()); + + async function handleGenerate() { + if (!canGenerate) { + if (!hasEnoughCredits) onOpenCredits(); + return; + } + setConfirmNewReading(false); + setError(""); + setLoading(true); + setResult(null); + try { + const res = await fetch("/api/map/personal", { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ + profile: { + fullName: profile.fullName ?? undefined, + birthDate: profile.birthDate ?? undefined, + birthPlace: profile.birthPlace ?? undefined, + birthTime: profile.birthTime ?? undefined, + }, + offline: offlineMode, + }), + }); + const data = await res.json(); + + if (res.status === 401) { + setError("Faça login para adquirir sua leitura."); + return; + } + if (res.status === 402) { + setError(`A leitura custa ${creditsPerReading} créditos. Adicione créditos para continuar.`); + if (typeof data.balance === "number") onBalanceUpdate(data.balance); + return; + } + if (!res.ok) { + setError(data?.error ?? "Não foi possível gerar a leitura. Tente novamente."); + return; + } + + const rawMessage = typeof data.message === "string" ? data.message : ""; + setResult(cleanReadingMessage(rawMessage) || rawMessage); + if (typeof data.balance === "number") onBalanceUpdate(data.balance); + } catch { + setError("Erro de conexão. Tente novamente."); + } finally { + setLoading(false); + } + } + + function handleClose() { + setError(""); + setResult(null); + setConfirmNewReading(false); + onClose(); + } + + function handleNewReadingClick() { + if (offlineMode) { + handleGenerate(); + return; + } + if (!hasEnoughCredits) { + onOpenCredits(); + return; + } + setConfirmNewReading(true); + } + + function handleConfirmNewReading() { + handleGenerate(); + } + + if (!isOpen) return null; + + return ( + <> + +
+ e.stopPropagation()} + > +
+

Leitura

+ +
+ +
+ {!result ? ( + <> +
+

+ Resumo completo considerando Sol regente, Lua, planetas, Nakshatras e numerologia — com exemplos e detalhes práticos. Você pode gerar quantas vezes quiser; cada vez será um novo resultado. +

+
+ {!confirmNewReading ? ( + <> +
+ {!offlineMode && ( + {creditsPerReading} créditos + )} + +
+ + ) : ( +
+

+ Esta operação vai consumir {creditsPerReading} créditos de seu saldo. Confirmar? +

+
+ + +
+
+ )} +
+
+ {error && ( +

+ {error} +

+ )} + + ) : ( + <> +
+
+ {result} +
+
+
+
+ +
+
+ {!offlineMode && ( + {creditsPerReading} créditos + )} + {!confirmNewReading ? ( + + ) : ( +
+

+ Esta operação vai consumir {creditsPerReading} créditos de seu saldo. Confirmar? +

+
+ + +
+
+ )} +
+
+
+ {error && ( +

+ {error} +

+ )} + + )} +
+ +
+ + ); +} diff --git a/app/components/ProfileIcon.tsx b/app/components/ProfileIcon.tsx new file mode 100644 index 0000000..ec8f30d --- /dev/null +++ b/app/components/ProfileIcon.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { motion } from "framer-motion"; +import Tooltip from "./Tooltip"; +import IconPlus from "./IconPlus"; + +type Props = { + onClick: () => void; + hasData?: boolean; +}; + +export default function ProfileIcon({ onClick, hasData }: Props) { + return ( + + {hasData ? ( + + + + ) : ( + + + + )} + + ); +} diff --git a/app/components/ProfilePanel.tsx b/app/components/ProfilePanel.tsx new file mode 100644 index 0000000..fd0b26f --- /dev/null +++ b/app/components/ProfilePanel.tsx @@ -0,0 +1,171 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import type { UserProfile } from "@/lib/userProfile"; +import { toBrDate, maskBrDate, fromBrDate, maskBrTime, fromBrTime } from "@/lib/dateFormatBr"; + +type Props = { + isOpen: boolean; + onClose: () => void; + profile: UserProfile; + onSave: (profile: UserProfile) => void; + onDeleteAccount: () => void; +}; + +const inputClass = + "w-full bg-transparent border-0 border-b border-white/25 rounded-none px-0 py-2.5 text-sm text-mist placeholder:text-white/40 outline-none focus:border-white/50 transition-colors"; + +export default function ProfilePanel({ isOpen, onClose, profile, onSave, onDeleteAccount }: Props) { + const [fullName, setFullName] = useState(profile.fullName ?? ""); + const [birthDateBr, setBirthDateBr] = useState(""); + const [birthPlace, setBirthPlace] = useState(profile.birthPlace ?? ""); + const [birthTimeBr, setBirthTimeBr] = useState(""); + + useEffect(() => { + if (isOpen) { + setFullName(profile.fullName ?? ""); + setBirthDateBr(profile.birthDate ? toBrDate(profile.birthDate) : ""); + setBirthPlace(profile.birthPlace ?? ""); + setBirthTimeBr(profile.birthTime ?? ""); + } + }, [isOpen, profile.fullName, profile.birthDate, profile.birthPlace, profile.birthTime]); + + function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + const isoDate = birthDateBr ? fromBrDate(birthDateBr) : undefined; + const time = birthTimeBr ? fromBrTime(birthTimeBr) : undefined; + onSave({ + fullName: fullName.trim() || undefined, + birthDate: isoDate || undefined, + birthPlace: birthPlace.trim() || undefined, + birthTime: time || undefined, + }); + onClose(); + } + + function handleClear() { + setFullName(""); + setBirthDateBr(""); + setBirthPlace(""); + setBirthTimeBr(""); + onSave({}); + } + + async function handleDeleteAccount() { + if (!confirm("Excluir sua conta? Seus dados locais serão apagados e você precisará entrar novamente.")) return; + onDeleteAccount(); + onClose(); + } + + return ( + + {isOpen && ( + <> + + +
+

Seu mapa

+ +
+
+

+ Dados opcionais para respostas mais precisas (astrologia e numerologia). +

+ + + setFullName(e.target.value)} + placeholder="" + className={inputClass} + /> + + + setBirthDateBr(maskBrDate(e.target.value))} + placeholder="DD/MM/AAAA" + maxLength={10} + className={inputClass} + /> + + + setBirthPlace(e.target.value)} + placeholder="" + className={inputClass} + /> + + + setBirthTimeBr(maskBrTime(e.target.value))} + placeholder="HH:mm" + maxLength={5} + className={inputClass} + /> + +
+ + +
+ + + +
+
+ + )} +
+ ); +} diff --git a/app/components/SupportModal.tsx b/app/components/SupportModal.tsx new file mode 100644 index 0000000..f341959 --- /dev/null +++ b/app/components/SupportModal.tsx @@ -0,0 +1,80 @@ +"use client"; + +import { motion, AnimatePresence } from "framer-motion"; + +type Props = { + isOpen: boolean; + onClose: () => void; + creditsPerRevelation: number; +}; + +export default function SupportModal({ isOpen, onClose, creditsPerRevelation }: Props) { + return ( + + {isOpen && ( + <> + +
+ e.stopPropagation()} + > +
+ ? + +
+ +

+ As informações que você recebe aqui não são determinísticas. O mapa, o oráculo e a IA oferecem leituras interpretativas — sugestões de sentido, não previsões fixas. O que importa é o que ressoa em você. +

+ +
+
+

Créditos e consumo

+

+ Cada revelação com IA consome {creditsPerRevelation} créditos. O valor cobre o custo do uso da IA e uma margem para o app. O modo “AI desligada” usa o oráculo offline e não consome créditos. +

+
+
+

Como adicionar créditos

+

+ Toque no ícone de créditos no topo da tela. Escolha a quantidade de créditos. Pagamento seguro: cartão, Google Pay ou Stripe Link (Stripe); ou PIX/cartão (Mercado Pago). +

+
+
+

Login sem senha

+

+ Seu acesso é pelo e-mail (código de uso único) ou com conta Google. Não guardamos senha. Em breve: login por biometria. +

+
+
+

Contato

+

+ Dúvidas ou problemas: envie um e-mail para suporte indicado nas configurações do app ou na página de divulgação. +

+
+
+
+
+ + )} +
+ ); +} diff --git a/app/components/TimeHeader.tsx b/app/components/TimeHeader.tsx index d8d7947..3ced3a2 100644 --- a/app/components/TimeHeader.tsx +++ b/app/components/TimeHeader.tsx @@ -1,16 +1,97 @@ +"use client"; + +import { getMoonPhaseHover } from "@/lib/knowledge/moonPhases"; +import Tooltip from "./Tooltip"; + +const HOVER_SUNRISE = "Nascer do sol"; +const HOVER_SUNSET = "Pôr do sol"; + type Props = { sunrise: string; sunset: string; moonPhase: string; }; +const iconClass = "w-4 h-4 shrink-0 text-white/70"; + +function IconSun() { + return ( + + + + + ); +} + +function IconMoon() { + return ( + + + + ); +} + +function IconCrescent() { + return ( + + + + ); +} + +const itemClass = "flex items-center gap-2 text-[11px] uppercase tracking-widest text-white/60"; + export default function TimeHeader({ sunrise, sunset, moonPhase }: Props) { return ( -
-
- 🌅 Sunrise: {sunrise} · 🌇 Sunset: {sunset} -
-
🌙 Moon: {moonPhase}
+
+ + + + {sunrise} + + + + + + {sunset} + + + + + + {moonPhase} + + +
+ ); +} + +export function TimeHeaderSunrise({ sunrise }: { sunrise: string }) { + return ( + + + + {sunrise} + + + ); +} + +export function TimeHeaderSunsetMoon({ sunset, moonPhase }: { sunset: string; moonPhase: string }) { + return ( +
+ + + + {sunset} + + + + + + {moonPhase} + +
); } diff --git a/app/components/Tooltip.tsx b/app/components/Tooltip.tsx new file mode 100644 index 0000000..ea693d1 --- /dev/null +++ b/app/components/Tooltip.tsx @@ -0,0 +1,45 @@ +"use client"; + +type Props = { + text: string; + children: React.ReactNode; + /** Alinhamento do tooltip em relação ao elemento. default: left */ + align?: "left" | "right" | "center"; + /** Nome do group para hover (deve ser único no mesmo pai). */ + groupName?: string; + /** Quando definido, o wrapper usa esta classe (ex.: para botão fixo). */ + wrapperClassName?: string; +}; + +const tooltipBase = + "absolute top-full mt-1 text-[10px] text-white/70 opacity-0 pointer-events-none transition-opacity duration-150 z-50 max-w-[200px] break-words "; + +export default function Tooltip({ + text, + children, + align = "left", + groupName = "tip", + wrapperClassName, +}: Props) { + const position = + align === "right" + ? "right-0" + : align === "center" + ? "left-1/2 -translate-x-1/2" + : "left-0"; + const Wrapper = wrapperClassName ? "div" : "span"; + const wrapperClass = wrapperClassName + ? `${wrapperClassName} group` + : "relative group inline-flex"; + return ( + + {children} + + {text} + + + ); +} diff --git a/app/config/page.tsx b/app/config/page.tsx new file mode 100644 index 0000000..e308b36 --- /dev/null +++ b/app/config/page.tsx @@ -0,0 +1,477 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import Script from "next/script"; +import Link from "next/link"; + +type ConfigFieldMode = "replace" | "complement"; + +type AppConfig = { + masterPromptOverride?: string | null; + masterPromptMode?: ConfigFieldMode | null; + revelationInstructionOverride?: string | null; + revelationInstructionMode?: ConfigFieldMode | null; + mockMessagesOverride?: string[] | null; + mockMessagesMode?: ConfigFieldMode | null; + readingInstructionOverride?: string | null; + readingInstructionMode?: ConfigFieldMode | null; + creditsPerRevelation?: number | null; + creditsPerReading?: number | null; + pricePerCreditCents?: number | null; + updatedAt?: string | null; +}; + +declare global { + interface Window { + grecaptcha?: { + render: (container: HTMLElement, options: { sitekey: string; callback?: (token: string) => void }) => number; + reset: (widgetId?: number) => void; + }; + onRecaptchaLoad?: () => void; + } +} + +const RECAPTCHA_SITE_KEY = typeof process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY === "string" + ? process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY.trim() + : ""; + +export default function ConfigPage() { + const [secretCode, setSecretCode] = useState(""); + const [recaptchaToken, setRecaptchaToken] = useState(""); + const recaptchaWidgetId = useRef(null); + const recaptchaContainerRef = useRef(null); + const [configKey, setConfigKey] = useState(""); + const [config, setConfig] = useState(null); + const [unlocked, setUnlocked] = useState(false); + const [error, setError] = useState(""); + const [saving, setSaving] = useState(false); + + const headers = () => ({ "X-Config-Key": configKey }); + + const resetRecaptcha = useCallback(() => { + setRecaptchaToken(""); + if (typeof window !== "undefined" && window.grecaptcha && recaptchaWidgetId.current !== null) { + window.grecaptcha.reset(recaptchaWidgetId.current); + } + }, []); + + /** Valida reCAPTCHA e código secreto; se corretos, desbloqueia a página e carrega a config. */ + const unlock = useCallback(async () => { + const code = secretCode.trim(); + if (!code) { + setError("Digite o código secreto."); + return; + } + if (RECAPTCHA_SITE_KEY && !recaptchaToken) { + setError("Complete o reCAPTCHA (marque \"Não sou um robô\" e, se aparecer, as imagens)."); + return; + } + setError(""); + try { + const res = RECAPTCHA_SITE_KEY + ? await fetch("/api/config/unlock", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ secretCode: code, recaptchaToken }), + }) + : await fetch("/api/config", { headers: { "X-Config-Key": code } }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + setError(data?.error || "Código inválido."); + resetRecaptcha(); + return; + } + const data = await res.json(); + setConfigKey(code); + setConfig(data); + setUnlocked(true); + } catch { + setError("Erro de conexão. Tente novamente."); + resetRecaptcha(); + } + }, [secretCode, recaptchaToken, resetRecaptcha]); + + const save = useCallback(async () => { + if (!configKey.trim() || !config) return; + setSaving(true); + setError(""); + try { + const res = await fetch("/api/config", { + method: "PUT", + headers: { "Content-Type": "application/json", ...headers() }, + body: JSON.stringify({ + masterPromptOverride: config.masterPromptOverride ?? null, + masterPromptMode: config.masterPromptMode ?? "complement", + revelationInstructionOverride: config.revelationInstructionOverride ?? null, + revelationInstructionMode: config.revelationInstructionMode ?? "complement", + mockMessagesOverride: config.mockMessagesOverride ?? null, + mockMessagesMode: config.mockMessagesMode ?? "complement", + readingInstructionOverride: config.readingInstructionOverride ?? null, + readingInstructionMode: config.readingInstructionMode ?? "complement", + creditsPerRevelation: config.creditsPerRevelation ?? null, + creditsPerReading: config.creditsPerReading ?? null, + pricePerCreditCents: config.pricePerCreditCents ?? null, + }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + setError(data?.error || "Erro ao salvar."); + return; + } + const data = await res.json(); + setConfig(data); + } catch { + setError("Erro ao salvar. Verifique a conexão."); + } finally { + setSaving(false); + } + }, [configKey, config]); + + const mockMessagesText = + Array.isArray(config?.mockMessagesOverride) && config.mockMessagesOverride.length > 0 + ? config.mockMessagesOverride.join("\n\n") + : ""; + + const setMockMessagesText = (text: string) => { + const list = text + .split(/\n\n+/) + .map((s) => s.trim()) + .filter(Boolean); + setConfig((c) => (c ? { ...c, mockMessagesOverride: list.length ? list : null } : null)); + }; + + useEffect(() => { + if (!RECAPTCHA_SITE_KEY) return; + window.onRecaptchaLoad = () => { + setTimeout(() => { + if (recaptchaContainerRef.current && typeof window !== "undefined" && window.grecaptcha) { + try { + recaptchaWidgetId.current = window.grecaptcha.render(recaptchaContainerRef.current, { + sitekey: RECAPTCHA_SITE_KEY, + callback: (token: string) => setRecaptchaToken(token), + }); + } catch (e) { + console.error("[config] reCAPTCHA render error:", e); + } + } + }, 0); + }; + return () => { + window.onRecaptchaLoad = undefined; + }; + }, []); + + if (!unlocked) { + return ( +
+ {RECAPTCHA_SITE_KEY && ( +