diff --git a/.dockerignore b/.dockerignore index c4b9c33..8b7caad 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,11 +2,15 @@ node_modules/ **/node_modules/ -# Build +# Test files (not needed at runtime) +**/__tests__/ +**/*.test.ts + +# Build artifacts dist/ **/dist/ -# Git +# Version control .git/ .gitignore @@ -14,8 +18,12 @@ dist/ .vscode/ .idea/ -# Misc -*.md +# Environment files .env .env.* !.env.example + +# Documentation & scripts +*.md +docs/ +scripts/ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..be2bc8e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ +## Summary + +## Motivation + +## Changes Made + +## Testing Instructions + +## Screenshots or Evidence + +## Pre-Submission Checklist + +- [ ] All tests pass — `deno test -A` +- [ ] No type errors — `deno check` +- [ ] No lint warnings — `deno lint` +- [ ] Tests added or updated for new functionality +- [ ] Branch is rebased on latest `dev` +- [ ] Commit history tells a legible story + +## Technical Decisions + +## Related Work + +## Reviewer Notes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 807a42f..d657f7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: branches: [master] jobs: - check: + backend: runs-on: ubuntu-latest services: @@ -38,34 +38,41 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 + - uses: denoland/setup-deno@v2 with: - version: 9.15.0 + # 2.9.0, not 2.7.14 (what the Dockerfiles pin) — the Node-compat + # layer needed for jsdom's undici dependency to work isn't present + # until 2.8.0. Test-tooling only; production images are unaffected + # since they never run `deno test`. + deno-version: "2.9.0" - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile + - name: Cache dependencies + run: deno cache apps/backend/src/index.ts apps/backend/src/jobs/worker.ts - - name: Build packages - run: pnpm build:packages + - name: Format check + run: deno fmt --check - name: Lint - run: pnpm lint + # Excludes apps/frontend — Biome is the linter of record for it, + # checked in the frontend job. Avoids double-linting the same files + # with two different rulesets. + run: deno lint --ignore=apps/frontend - name: Type check - run: pnpm typecheck + run: deno check apps/backend/src/index.ts - name: Run migrations - run: pnpm --filter @repo/db db:push:ci + run: deno run --allow-env --allow-net --allow-read --allow-sys apps/backend/src/db/migrate.ts env: DATABASE_URL: postgres://test:test@localhost:5432/test - name: Test - run: pnpm test + # Excludes apps/frontend deliberately — its jsdom-based tests run in + # the frontend job, in a separate process. A frontend DOM-testing + # crash previously took down unrelated backend test results by + # corrupting the shared fetch/undici runtime when run in the same + # `deno test` invocation. + run: deno test -A --ignore=apps/frontend env: NODE_ENV: test DATABASE_URL: postgres://test:test@localhost:5432/test @@ -73,4 +80,33 @@ jobs: BETTER_AUTH_SECRET: test-secret-for-ci-at-least-32-chars BETTER_AUTH_URL: http://localhost:9999 SERVER_URL: http://localhost:9999 - FRONTEND_URL: http://localhost:5173 + FRONTEND_URL: http://localhost:3000 + + frontend: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: denoland/setup-deno@v2 + with: + deno-version: "2.9.0" + + - name: Install frontend dependencies + working-directory: apps/frontend + run: deno install + + - name: Frontend lint + run: deno run -A npm:@biomejs/biome@2.3.7 ci apps/frontend + + - name: Frontend type check + working-directory: apps/frontend + run: deno task typecheck + + - name: Frontend build + working-directory: apps/frontend + run: deno task build + + - name: Frontend test + working-directory: apps/frontend + run: deno task test diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 13f0257..3d60650 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,19 +6,17 @@ on: env: REGISTRY: ghcr.io - IMAGE_NAME: orctatech-engineering-team/orcta-backend + BACKEND_IMAGE: orctatech-engineering-team/orcta-backend + FRONTEND_IMAGE: orctatech-engineering-team/orcta-frontend jobs: - build-and-push: - name: Build & push Docker image + build-backend: + name: Build & push backend image runs-on: ubuntu-latest permissions: contents: read packages: write - outputs: - image_tag: ${{ steps.meta.outputs.version }} - steps: - uses: actions/checkout@v4 @@ -33,7 +31,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }} tags: | type=sha,prefix=,format=short type=raw,value=latest @@ -49,10 +47,45 @@ jobs: build-args: | SERVICE_VERSION=${{ github.sha }} + build-frontend: + name: Build & push frontend image + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract image metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }} + tags: | + type=sha,prefix=,format=short + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: apps/frontend/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + deploy: name: Deploy to VPS runs-on: ubuntu-latest - needs: build-and-push + needs: [build-backend, build-frontend] environment: production steps: @@ -65,25 +98,15 @@ jobs: username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_SSH_KEY }} script: | - # Fail fast: - # -e → exit on error - # -u → error on undefined variables - # -o pipefail → fail if any command in a pipeline fails set -euo pipefail - # Static deployment configuration REPO_NAME="orcta-stack" APP_DIR="/srv/apps/$REPO_NAME" - - # Branch that triggered the workflow (master in your case) BRANCH="${{ github.ref_name }}" - # Ensure application directory exists (idempotent) mkdir -p "$APP_DIR" cd "$APP_DIR" - # Sync repository state - # Clone only once; subsequent deploys pull latest changes if [ ! -d ".git" ]; then echo "Cloning repository..." git clone git@github.com:Orctatech-Engineering-Team/$REPO_NAME.git . @@ -94,35 +117,28 @@ jobs: git pull origin "$BRANCH" fi - # Hard stop if production environment file is missing - # Prevents accidental boot with empty credentials/secrets if [ ! -f ".env.production" ]; then echo "Error: .env.production file not found." exit 1 fi - # Authenticate with GitHub Container Registry (GHCR) - # Token is piped via stdin to avoid shell history leakage echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin - # Pull latest backend image built by CI + # Pull latest images built by CI docker pull ghcr.io/orctatech-engineering-team/orcta-backend:latest + docker pull ghcr.io/orctatech-engineering-team/orcta-frontend:latest - # Start infrastructure dependencies first - # --env-file ensures Compose-time variable interpolation works + # Start infrastructure dependencies IMAGE_TAG=latest docker compose --env-file .env.production -f docker-compose.prod.yml up -d db redis - # Show container states (useful for debugging in CI logs) docker compose --env-file .env.production -f docker-compose.prod.yml ps + # Run database migrations (deno image with the repo checkout + # mounted — the compiled backend image has no deno executable + # or source tree to run migrate.ts from) + IMAGE_TAG=latest docker compose --env-file .env.production -f docker-compose.prod.yml run --rm migrate - # Run database migrations using the NEW backend image - # --rm prevents orphaned containers - IMAGE_TAG=latest docker compose --env-file .env.production -f docker-compose.prod.yml run --rm backend node src/db/migrate.js - - # Update backend container - # Only backend is recreated → DB/Redis remain untouched - IMAGE_TAG=latest docker compose --env-file .env.production -f docker-compose.prod.yml up -d backend + # Recreate backend and frontend + IMAGE_TAG=latest docker compose --env-file .env.production -f docker-compose.prod.yml up -d backend frontend - # Remove dangling/unused images to control disk usage docker image prune -f diff --git a/.gitignore b/.gitignore index 3491995..dfe578c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,14 @@ dist/ .tsbuildinfo tsconfig.tsbuildinfo +# pnpm (migrated to Deno) +pnpm-lock.yaml + # Environment files .env .env.local .env.*.local +.env.production # IDE # Ignore personal VS Code files; track shared workspace config. @@ -35,4 +39,7 @@ coverage/ # Misc .cache/ -.tanstack/ \ No newline at end of file +.tanstack/ + +# Generated +**/routeTree.gen.ts \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3862542..0974e40 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,16 +1,18 @@ { - // Extensions recommended for everyone working in this repo. - // VS Code will prompt teammates to install these when they open the workspace. - "recommendations": [ - // Biome — formatter, linter, import sorter. Replaces Prettier + ESLint. - "biomejs.biome", - // TanStack Router — file-based route generation awareness - "tanstack.router-vscode-plugin", - // Tailwind CSS IntelliSense — autocomplete for utility classes - "bradlc.vscode-tailwindcss", - // Prisma / Drizzle don't have great tooling yet, but these help with SQL - "inferrinizzard.prettier-sql-vscode", - // Dot-env syntax highlighting - "mikestead.dotenv" - ] + // Extensions recommended for everyone working in this repo. + // VS Code will prompt teammates to install these when they open the workspace. + "recommendations": [ + // Deno — runtime, language server, and type support for backend files. + "denoland.vscode-deno", + // Biome — formatter, linter, import sorter. Replaces Prettier + ESLint. + "biomejs.biome", + // TanStack Router — file-based route generation awareness + "tanstack.router-vscode-plugin", + // Tailwind CSS IntelliSense — autocomplete for utility classes + "bradlc.vscode-tailwindcss", + // Prisma / Drizzle don't have great tooling yet, but these help with SQL + "inferrinizzard.prettier-sql-vscode", + // Dot-env syntax highlighting + "mikestead.dotenv" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 76afa82..098746b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,80 +1,51 @@ { - // ─── Biome ─────────────────────────────────────────────────────────────── - // - // Only activate Biome LSP for workspace folders that contain a biome.json. - // This prevents Biome from trying to lint unrelated folders you may open. - "biome.requireConfiguration": true, + // ─── Biome ─────────────────────────────────────────────────────────────── + // + // Only activate Biome LSP for workspace folders that contain a biome.json. + // This prevents Biome from trying to lint unrelated folders you may open. + "biome.requireConfiguration": true, - // ─── Editor: format & fix on save ──────────────────────────────────────── - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - // Apply all safe Biome fixes (no-unused-vars removal, etc.) on save. - "source.fixAll.biome": "explicit", - // Sort imports via Biome's organizeImports on save. - // This replaces the need for a separate import-sorter extension. - "source.organizeImports.biome": "explicit" - }, + // ─── Editor: format & fix on save ──────────────────────────────────────── + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + // Apply all safe Biome fixes (no-unused-vars removal, etc.) on save. + "source.fixAll.biome": "explicit", + // Sort imports via Biome's organizeImports on save. + // This replaces the need for a separate import-sorter extension. + "source.organizeImports.biome": "explicit" + }, - // ─── Default formatter per language ────────────────────────────────────── - // - // Biome handles: JS · TS · JSX · TSX · JSON · JSONC · CSS · GraphQL. - // Setting it explicitly per language avoids the "multiple formatters" - // prompt and ensures Biome wins even if Prettier is installed globally. - "[javascript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[typescript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[json]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[jsonc]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[css]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[graphql]": { - "editor.defaultFormatter": "biomejs.biome" - }, + // ─── Deno ────────────────────────────────────────────────────────────────── + // Enable the Deno language server for the backend. + "deno.enable": true, + "deno.lint": true, + // Deno config — points to the root deno.json with import maps. + "deno.config": "./deno.json", - // ─── TypeScript ─────────────────────────────────────────────────────────── - // Use the workspace TypeScript version (from node_modules) rather than - // VS Code's bundled one. Keeps type-checking consistent with tsc in CI. - "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, + // ─── Tailwind CSS IntelliSense ──────────────────────────────────────────── + // Tailwind v4 uses CSS @theme blocks instead of tailwind.config.js. + // Point the extension at index.css so it finds the @theme source. + "tailwindCSS.experimental.configFile": "apps/frontend/src/index.css", + // Treat clsx() and cn() arguments as Tailwind class lists for completion. + "tailwindCSS.classFunctions": ["clsx", "cn", "cva", "cx"], - // ─── Tailwind CSS IntelliSense ──────────────────────────────────────────── - // Tailwind v4 uses CSS @theme blocks instead of tailwind.config.js. - // Point the extension at index.css so it finds the @theme source. - "tailwindCSS.experimental.configFile": "apps/frontend/src/index.css", - // Treat clsx() and cn() arguments as Tailwind class lists for completion. - "tailwindCSS.classFunctions": ["clsx", "cn", "cva", "cx"], + // ─── Files ──────────────────────────────────────────────────────────────── + // Don't show generated/build artefacts in the explorer; Biome also skips + // linting these (see overrides in biome.json). + "files.exclude": { + "**/node_modules": true, + "**/.git": true + }, + "search.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/.turbo": true, + "apps/frontend/src/routeTree.gen.ts": true, + "packages/db/migrations": true, + "pnpm-lock.yaml": true + }, - // ─── Files ──────────────────────────────────────────────────────────────── - // Don't show generated/build artefacts in the explorer; Biome also skips - // linting these (see overrides in biome.json). - "files.exclude": { - "**/node_modules": true, - "**/.git": true - }, - "search.exclude": { - "**/node_modules": true, - "**/dist": true, - "**/.turbo": true, - "apps/frontend/src/routeTree.gen.ts": true, - "packages/db/migrations": true, - "pnpm-lock.yaml": true - }, - - // ─── Explorer ───────────────────────────────────────────────────────────── - // Compact single-child folders (e.g. apps/frontend/src) in the tree. - "explorer.compactFolders": false + // ─── Explorer ───────────────────────────────────────────────────────────── + // Compact single-child folders (e.g. apps/frontend/src) in the tree. + "explorer.compactFolders": false } diff --git a/AGENTS.md b/AGENTS.md index 6239fad..1d3874f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,24 +1,38 @@ # AGENTS.md -Instructions for AI agents working in this codebase. Read this before touching anything. +Instructions for AI agents working in this codebase. Read this before touching +anything. -Also read: [`CLAUDE.md`](CLAUDE.md) for commands and architecture, [`docs/WRITING.md`](docs/WRITING.md) for documentation voice, [`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md) for the beliefs behind every decision. +Also read: [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) for deployment, +[`docs/DENO_WORKSPACE_SCOPE.md`](docs/DENO_WORKSPACE_SCOPE.md) for workspace +architecture, [`docs/WRITING.md`](docs/WRITING.md) for documentation voice, +[`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md) for the beliefs behind every +decision. --- ## Philosophy -This codebase follows a simple principle: **simple is not easy, but it's the only thing that scales**. +This codebase follows a simple principle: **simple is not easy, but it's the +only thing that scales**. -We take inspiration from 37signals, DHH, Jason Fried, and the Primeagen. Bias toward the boring solution. Complexity is a cost you pay with every future change. Don't add indirection that doesn't earn its keep. Don't build for hypothetical requirements. Don't make ten changes when one would do. +We take inspiration from 37signals, DHH, Jason Fried, and the Primeagen. Bias +toward the boring solution. Complexity is a cost you pay with every future +change. Don't add indirection that doesn't earn its keep. Don't build for +hypothetical requirements. Don't make ten changes when one would do. -The four principles — **principles over tools**, **progressive abstraction**, **craftsmanship**, **human experience first** — are documented in full in [`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md). Read it once. It's the question to ask before you add anything. +The four principles — **principles over tools**, **progressive abstraction**, +**craftsmanship**, **human experience first** — are documented in full in +[`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md). Read it once. It's the question to +ask before you add anything. --- ## The Core Rule: Work in Coherent Units -A coherent unit is one logical change with a clear boundary. It has a name. It could be described in a single sentence. It has tests. It gets a commit. If it's non-trivial, it gets a branch and a PR. +A coherent unit is one logical change with a clear boundary. It has a name. It +could be described in a single sentence. It has tests. It gets a commit. If it's +non-trivial, it gets a branch and a PR. **A coherent unit is:** @@ -34,13 +48,15 @@ A coherent unit is one logical change with a clear boundary. It has a name. It c - "All the things I noticed while looking at the file" - Every change across the entire repo triggered by one small ask -When you find something adjacent that needs fixing, note it. Finish the current unit. Then address it separately. +When you find something adjacent that needs fixing, note it. Finish the current +unit. Then address it separately. --- ## Branching -Never commit directly to `main` or `dev` for anything beyond a typo fix. New work = new branch. +Never commit directly to `main` or `dev` for anything beyond a typo fix. New +work = new branch. ```bash git checkout dev @@ -50,15 +66,16 @@ git checkout -b / Branch naming: -| Type | Pattern | Example | -|------|---------|---------| -| New feature | `feature/` | `feature/post-pagination` | -| Bug fix | `fix/` | `fix/session-cookie-expiry` | -| Refactor | `refactor/` | `refactor/error-handler-cleanup` | -| Chore | `chore/` | `chore/update-drizzle` | -| Documentation | `docs/` | `docs/better-auth-schema` | +| Type | Pattern | Example | +| ------------- | ----------------- | -------------------------------- | +| New feature | `feature/` | `feature/post-pagination` | +| Bug fix | `fix/` | `fix/session-cookie-expiry` | +| Refactor | `refactor/` | `refactor/error-handler-cleanup` | +| Chore | `chore/` | `chore/update-drizzle` | +| Documentation | `docs/` | `docs/better-auth-schema` | -One branch = one coherent unit. Don't accumulate unrelated changes on a branch because they happen to be open at the same time. +One branch = one coherent unit. Don't accumulate unrelated changes on a branch +because they happen to be open at the same time. --- @@ -93,9 +110,12 @@ fixed some things and also added the new feature and updated docs wip ``` -Commit size: one concern per commit. If the message needs "and", it's probably two commits. If the diff spans five unrelated files, stop and ask what the actual unit is. +Commit size: one concern per commit. If the message needs "and", it's probably +two commits. If the diff spans five unrelated files, stop and ask what the +actual unit is. -Keep commits logical even on a feature branch — they tell the story of how you got there. +Keep commits logical even on a feature branch — they tell the story of how you +got there. --- @@ -103,30 +123,34 @@ Keep commits logical even on a feature branch — they tell the story of how you Tests are part of the work unit, not an afterthought. -- Write or update tests **before committing** — don't leave a commit that breaks the suite -- For new backend modules: at minimum, a scaffolded `handlers.test.ts` with the first test passing +- Write or update tests **before committing** — don't leave a commit that breaks + the suite +- For new backend modules: at minimum, a scaffolded `handlers.test.ts` with the + first test passing - For logic that branches: test every branch - For bug fixes: write the test that would have caught it first, then fix it ```bash -pnpm test # All tests -pnpm typecheck # TypeScript -pnpm lint # Biome +deno test -A # All tests +deno check # TypeScript +deno lint # Lint backend ``` -All three must pass before you commit. CI will catch it anyway — save the round trip. +All three must pass before you commit. CI will catch it anyway — save the round +trip. --- ## Pull Requests -PR per coherent unit. Not per day. Not "everything I did this session". One unit, one PR. +PR per coherent unit. Not per day. Not "everything I did this session". One +unit, one PR. **Before opening a PR:** -- [ ] All tests pass (`pnpm test`) -- [ ] No type errors (`pnpm typecheck`) -- [ ] No lint warnings (`pnpm lint`) +- [ ] All tests pass (`deno test -A`) +- [ ] No type errors (`deno check`) +- [ ] No lint warnings (`deno lint`) - [ ] Branch is rebased on latest `dev` - [ ] Commit history tells a legible story @@ -134,22 +158,30 @@ PR per coherent unit. Not per day. Not "everything I did this session". One unit ```md ## What + Brief description of the change. One to three sentences. ## Why + The user problem or technical need this solves. Link to issue if one exists. ## How -Key architectural or implementation decisions. What alternatives were considered. + +Key architectural or implementation decisions. What alternatives were +considered. ## Testing + How to verify this works. Specific steps, not "it works". ## Notes -Anything a reviewer should know. Technical debt introduced. Follow-up work needed. + +Anything a reviewer should know. Technical debt introduced. Follow-up work +needed. ``` -PR size: reviewable in under 30 minutes. If the diff is 1000+ lines, it's probably two PRs. Split by layer or by phase of the work. +PR size: reviewable in under 30 minutes. If the diff is 1000+ lines, it's +probably two PRs. Split by layer or by phase of the work. --- @@ -157,29 +189,52 @@ PR size: reviewable in under 30 minutes. If the diff is 1000+ lines, it's probab These are the failure modes to actively avoid: -**Don't batch unrelated changes.** If you're asked to add pagination to the posts endpoint, don't also fix the users handler, rename a variable you noticed, and update three docs files. Do the thing asked. Commit it. Note the rest. +**Don't batch unrelated changes.** If you're asked to add pagination to the +posts endpoint, don't also fix the users handler, rename a variable you noticed, +and update three docs files. Do the thing asked. Commit it. Note the rest. -**Don't refactor while adding a feature.** If the existing code is messy, open a refactor PR first, then build the feature on top of clean ground. Mixing the two makes both harder to review and harder to revert. +**Don't refactor while adding a feature.** If the existing code is messy, open a +refactor PR first, then build the feature on top of clean ground. Mixing the two +makes both harder to review and harder to revert. -**Don't create summary documents.** Don't create a `CHANGES.md`, `SUMMARY.md`, or `TODO.md` unless explicitly asked. If you need to track state across a long task, use the task list in your head or ask the user. +**Don't create summary documents.** Don't create a `CHANGES.md`, `SUMMARY.md`, +or `TODO.md` unless explicitly asked. If you need to track state across a long +task, use the task list in your head or ask the user. -**Don't touch files you weren't asked to touch** unless they're directly load-bearing for the change. Noticing something is not the same as being asked to fix it. +**Don't touch files you weren't asked to touch** unless they're directly +load-bearing for the change. Noticing something is not the same as being asked +to fix it. -**Don't leave the codebase in a half-done state.** A partially implemented feature is worse than no feature — it creates confusion and merge conflicts. Either complete the unit or don't start it. If something is larger than expected, surface that before diving in. +**Don't leave the codebase in a half-done state.** A partially implemented +feature is worse than no feature — it creates confusion and merge conflicts. +Either complete the unit or don't start it. If something is larger than +expected, surface that before diving in. --- ## Code Patterns -The patterns are documented; use them. Don't invent new ones without documenting them. +The patterns are documented; use them. Don't invent new ones without documenting +them. -**Error handling:** every function that can fail returns `Result`. No `throw` in repository or use-case code. Infrastructure catches go through `tryInfra`. Domain errors are typed discriminated unions in `*.errors.ts`. See [`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). +**Error handling:** every function that can fail returns `Result`. No +`throw` in repository or use-case code. Infrastructure catches go through +`tryInfra`. Domain errors are typed discriminated unions in `*.errors.ts`. See +[`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). -**Module structure:** use the scaffolder. `pnpm new:module ` generates the correct file layout. The pattern is: route → handler → repository → error types → use-cases. See [`apps/backend/docs/ARCHITECTURE.md`](apps/backend/docs/ARCHITECTURE.md). +**Module structure:** use the scaffolder. `./scripts/new-module.sh ` +generates the correct file layout. The pattern is: route → handler → repository +→ error types → use-cases. See +[`apps/backend/docs/ARCHITECTURE.md`](apps/backend/docs/ARCHITECTURE.md). -**Frontend components:** Base UI primitives wrapped with CVA variants. Design tokens from CSS custom properties (`--color-primary`, etc.). No hardcoded `gray-*` Tailwind classes. See [`apps/frontend/docs/DECISIONS.md`](apps/frontend/docs/DECISIONS.md). +**Frontend components:** Base UI primitives wrapped with CVA variants. Design +tokens from CSS custom properties (`--color-primary`, etc.). No hardcoded +`gray-*` Tailwind classes. See +[`apps/frontend/docs/DECISIONS.md`](apps/frontend/docs/DECISIONS.md). -**Adding a Better Auth plugin:** run schema generation before migrating. See the Better Auth section in [`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). +**Adding a Better Auth plugin:** run schema generation before migrating. See the +Better Auth section in +[`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). --- @@ -194,14 +249,21 @@ You're done with a unit when: 5. The branch is clean and rebased 6. The PR description would let a stranger understand the change in five minutes -Quality is not completeness. Shipping ten half-finished things is worse than shipping three finished ones. Finish what you start. Leave the codebase better than you found it. +Quality is not completeness. Shipping ten half-finished things is worse than +shipping three finished ones. Finish what you start. Leave the codebase better +than you found it. --- ## Reference -- [Engineering Philosophy](https://adjanour.github.io/docs-site/engineering/engineering-philosophy/) — principles that anchor all decisions -- [Engineering Playbook](https://adjanour.github.io/docs-site/engineering/engineering-playbook/) — practices and rituals -- [Git Workflow](https://adjanour.github.io/docs-site/engineering/git-workflow/) — branching and commit conventions -- [PR Guidelines](https://adjanour.github.io/docs-site/engineering/pr-guidelines/) — what a good PR looks like -- [Orcta Workflow](https://docs.orctatech.com/orcta-workflow.html) — end-to-end development workflow +- [Engineering Philosophy](https://adjanour.github.io/docs-site/engineering/engineering-philosophy/) + — principles that anchor all decisions +- [Engineering Playbook](https://adjanour.github.io/docs-site/engineering/engineering-playbook/) + — practices and rituals +- [Git Workflow](https://adjanour.github.io/docs-site/engineering/git-workflow/) + — branching and commit conventions +- [PR Guidelines](https://adjanour.github.io/docs-site/engineering/pr-guidelines/) + — what a good PR looks like +- [Orcta Workflow](https://docs.orctatech.com/orcta-workflow.html) — end-to-end + development workflow diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index a05d952..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,114 +0,0 @@ -# CLAUDE.md - -Quick reference for working with this codebase. Read [`AGENTS.md`](AGENTS.md) for work discipline, branching, and commit rules before starting any task. - -## Docs - -| Doc | What it covers | -|-----|----------------| -| `AGENTS.md` | **Work discipline, branching, commits, PRs — read first** | -| `docs/PHILOSOPHY.md` | The beliefs behind every decision — why the codebase is shaped this way | -| `CONTRIBUTING.md` | Full workflow for humans and agents | -| `docs/BATTERIES.md` | Built-in utilities (auth, jobs, caching, etc.) | -| `docs/DEPLOYMENT.md` | Production deployment guide | -| `docs/WRITING.md` | Writing voice, style guide, and influences | -| `apps/backend/docs/` | Backend architecture, patterns, decisions | -| `apps/frontend/docs/` | Frontend patterns and decisions | - -## Commands - -```bash -pnpm dev # Run backend (:9999) + frontend (:5173) -pnpm test # Run tests -pnpm lint # Lint with Biome -pnpm typecheck # Type check everything -pnpm db:migrate # Apply database migrations -pnpm db:generate # Generate migration from schema changes -pnpm new:module NAME # Scaffold a new backend module -``` - -## Architecture - -Backend uses clean architecture. The key rule: - -**Use-cases return discriminated unions, not exceptions.** - -```typescript -type Result = - | { type: "SUCCESS"; data: T } - | { type: "NOT_FOUND" } - | { type: "ALREADY_EXISTS" }; -``` - -Handlers switch on `result.type` and map to HTTP responses. - -## File Locations - -| What | Where | -|------|-------| -| Backend modules | `apps/backend/src/modules/{name}/` | -| Database schemas | `packages/db/src/schema/` | -| Frontend pages | `apps/frontend/src/routes/` | -| Shared types | `packages/shared/src/` | -| Backend utilities | `apps/backend/src/lib/` | - -## Module Structure - -```bash -modules/{name}/ - routes.ts # OpenAPI route definitions + exported route types - handlers.ts # HTTP handlers (imperative shell) - index.ts # Creates router, wires routes → handlers - {name}.errors.ts # Domain error type variants - {name}.repository.ts # Data access — tryInfra, Result, never throws - usecases/ - {name}.usecases.ts # Pure business logic — no DB, no async - __tests__/ - handlers.test.ts # Integration tests via Hono testClient -``` - -Generate with `pnpm new:module `. The `usecases/` file is optional — add it when business rules are worth testing in isolation. - -## Common Patterns - -### Adding a route - -1. Define in `routes.ts` with Zod schemas -2. Create handler in `handlers.ts` -3. Wire in `index.ts` -4. Register module in `apps/backend/src/routes/index.ts` - -### Adding a database table - -1. Create schema in `packages/db/src/schema/{table}.ts` -2. Export from `packages/db/src/schema/index.ts` -3. Run `pnpm db:generate && pnpm db:migrate` - -### Using batteries - -```typescript -// File uploads -import { getUploadUrl } from "@/lib/storage"; - -// WebSockets -import { wsManager } from "@/lib/ws"; - -// Rate limiting -import { rateLimit } from "@/lib/rate-limit"; - -// Background jobs -import { addJob } from "@/jobs"; -``` - -## Environment - -Required in `.env`: - -- `DATABASE_URL` -- `BETTER_AUTH_SECRET` (32+ chars) - -Optional: - -- `REDIS_URL` — for jobs/caching -- `S3_*` — for file uploads -- `RESEND_API_KEY` — for email diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 97ea016..91f070e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,9 +2,12 @@ Thanks for helping out. Here's how to do it well. -Before diving in, read [`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md) — the beliefs behind every decision in this codebase. The workflow below will make more sense once you understand the *why*. +Before diving in, read [`docs/PHILOSOPHY.md`](docs/PHILOSOPHY.md) — the beliefs +behind every decision in this codebase. The workflow below will make more sense +once you understand the _why_. -If you're working with an AI agent on this project, also read [`AGENTS.md`](AGENTS.md). +If you're working with an AI agent on this project, also read +[`AGENTS.md`](AGENTS.md). --- @@ -13,13 +16,22 @@ If you're working with an AI agent on this project, also read [`AGENTS.md`](AGEN This is a GitHub template repo. You already have your own copy. ```bash -pnpm setup # Install dependencies + generate .env +deno --version # Ensure Deno 2+ is installed +pnpm install # Install frontend dependencies +./scripts/setup.sh # Generate .env with auth secret docker compose up -d -pnpm db:migrate -pnpm dev +deno task db:migrate +deno task dev # Backend on :9999 ``` -Backend on [localhost:9999/docs](http://localhost:9999/docs). Frontend on [localhost:5173](http://localhost:5173). +In a separate terminal: + +```bash +deno task dev:frontend # Frontend on :3000 via Vite +``` + +Backend on [localhost:9999/docs](http://localhost:9999/docs). Frontend on +[localhost:3000](http://localhost:3000). Run both together or independently. --- @@ -35,28 +47,33 @@ git checkout -b feature/your-feature-name Never work directly on `main` or `dev`. Every change gets its own branch. -| Type | Branch pattern | -|------|----------------| -| New feature | `feature/` | -| Bug fix | `fix/` | -| Refactor | `refactor/` | -| Chore / deps | `chore/` | -| Docs | `docs/` | +| Type | Branch pattern | +| ------------ | ----------------- | +| New feature | `feature/` | +| Bug fix | `fix/` | +| Refactor | `refactor/` | +| Chore / deps | `chore/` | +| Docs | `docs/` | ### 2. Build one thing -A feature, a fix, a refactor — not all three at once. If you notice something adjacent that needs fixing, note it and address it separately. Mixing concerns makes every change harder to review, harder to revert, and harder to understand later. +A feature, a fix, a refactor — not all three at once. If you notice something +adjacent that needs fixing, note it and address it separately. Mixing concerns +makes every change harder to review, harder to revert, and harder to understand +later. -For anything non-trivial: think through the approach before writing code. A five-minute outline is faster than a misdirected hour of implementation. +For anything non-trivial: think through the approach before writing code. A +five-minute outline is faster than a misdirected hour of implementation. ### 3. Write tests with the code -Tests are part of the feature, not something you add at the end. Before you commit: +Tests are part of the feature, not something you add at the end. Before you +commit: ```bash -pnpm test # All tests -pnpm typecheck # TypeScript -pnpm lint # Biome +deno test -A # All tests +deno check # TypeScript +deno lint # Lint backend ``` CI runs these on every PR. It will catch it — fix it locally first. @@ -71,7 +88,7 @@ Conventional commits: ``` -Types: `feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `perf` +Types: `feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `perf`\ Scopes: `backend`, `frontend`, `shared`, `db`, `auth`, `jobs`, `scripts` ```bash @@ -97,30 +114,39 @@ Use this template: ```md ## What + Brief description. One to three sentences. ## Why + The user problem or technical need. Link to issue if one exists. ## How + Key decisions made. What alternatives were considered. ## Testing + How to verify this works. Specific steps. ## Notes -Technical debt introduced. Follow-up work needed. Anything a reviewer should know. + +Technical debt introduced. Follow-up work needed. Anything a reviewer should +know. ``` ### 6. Address review feedback -Respond to all comments within 24 hours — even a simple "done" or "disagree because X". If you disagree, explain why. Reviews are collaborative, not adversarial. +Respond to all comments within 24 hours — even a simple "done" or "disagree +because X". If you disagree, explain why. Reviews are collaborative, not +adversarial. Mark conversations as resolved when addressed. Request re-review when ready. ### 7. After merging -Monitor your changes in production for at least an hour. Delete the feature branch. Close related issues. +Monitor your changes in production for at least an hour. Delete the feature +branch. Close related issues. --- @@ -128,9 +154,13 @@ Monitor your changes in production for at least an hour. Delete the feature bran ### Backend -- **Errors are values.** Use-cases and repositories return `Result` — never throw. See [`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). -- **Handlers are thin.** They read input, call the repository or use-case, map the Result to HTTP. No business logic in handlers. -- **Infrastructure has one catch boundary.** All DB/Redis calls go through `tryInfra`. That's the only `try/catch` in repository code. +- **Errors are values.** Use-cases and repositories return `Result` — + never throw. See + [`apps/backend/docs/DECISIONS.md`](apps/backend/docs/DECISIONS.md). +- **Handlers are thin.** They read input, call the repository or use-case, map + the Result to HTTP. No business logic in handlers. +- **Infrastructure has one catch boundary.** All DB/Redis calls go through + `tryInfra`. That's the only `try/catch` in repository code. ```typescript // Good @@ -151,13 +181,17 @@ try { ### Frontend -- **Server state lives in React Query.** UI state lives in Zustand. Don't create a context for server data. -- **Auth guards go in `beforeLoad`**, not inside the component. No flash of unauthenticated content. -- **Components wrap Base UI primitives.** Style with design tokens (`--color-primary`, etc.) not hardcoded Tailwind colors. +- **Server state lives in React Query.** UI state lives in Zustand. Don't create + a context for server data. +- **Auth guards go in `beforeLoad`**, not inside the component. No flash of + unauthenticated content. +- **Components wrap Base UI primitives.** Style with design tokens + (`--color-primary`, etc.) not hardcoded Tailwind colors. ### General -- No `any`. Configure a `biome-ignore` comment with a reason if you absolutely need one. +- No `any`. Configure a `// deno-lint-ignore` comment (backend) or + `// biome-ignore` comment (frontend) with a reason if you absolutely need one. - Name things clearly. `getUserById` not `get` or `fetchData`. - Delete dead code. Don't comment it out. - Leave the codebase better than you found it — but save it for its own commit. @@ -169,30 +203,32 @@ try { ### A backend module ```bash -pnpm new:module posts +./scripts/new-module.sh posts ``` -Then register in `apps/backend/src/routes/index.ts`. See the [README](README.md) for the full walkthrough. +Then register in `apps/backend/src/routes/index.ts`. See the [README](README.md) +for the full walkthrough. ### A database table ```bash # Edit packages/db/src/schema/your-table.ts -pnpm db:generate # Generates migration -pnpm db:migrate # Applies it +deno task db:generate # Generates migration +deno task db:migrate # Applies it ``` ### A Better Auth plugin ```bash -npx @better-auth/cli generate # Updates schema from auth config -pnpm db:generate -pnpm db:migrate +deno run -A npm:@better-auth/cli generate # Updates schema from auth config +deno task db:generate +deno task db:migrate ``` ### A frontend page -Create `apps/frontend/src/routes/your-page.tsx`. TanStack Router picks it up automatically. +Create `apps/frontend/src/routes/your-page.tsx`. TanStack Router picks it up +automatically. --- @@ -206,7 +242,8 @@ You're done with a unit when: - The commit message describes what and why - A stranger could understand the PR in five minutes -Finish what you start. Shipping three complete things beats ten half-done ones every time. +Finish what you start. Shipping three complete things beats ten half-done ones +every time. --- diff --git a/README.md b/README.md index b43d7c0..65b1d03 100644 --- a/README.md +++ b/README.md @@ -3,63 +3,75 @@ A production-ready TypeScript monorepo. Ship fast, sleep well. ```bash -pnpm setup && docker compose up -d && pnpm db:migrate && pnpm dev +./scripts/setup.sh && docker compose up -d && deno task db:migrate && deno task dev ``` -Backend runs on [localhost:9999](http://localhost:9999/docs). Frontend on [localhost:5173](http://localhost:5173). +Backend runs on [localhost:9999](http://localhost:9999/docs). Frontend on +[localhost:3000](http://localhost:3000). ## What's Inside -**Backend** — Hono, Drizzle, PostgreSQL, better-auth -**Frontend** — React 19, TanStack Router, Tailwind v4 -**Extras** — File uploads, WebSockets, background jobs, rate limiting +**Backend** — Hono, Drizzle, PostgreSQL, better-auth **Frontend** — React 19, +TanStack Router, Tailwind v4 **Extras** — File uploads, WebSockets, background +jobs, rate limiting ## Get Started -This is a GitHub template. Click **Use this template** → **Create a new repository** on GitHub, then clone your new repo. +This is a GitHub template. Click **Use this template** → **Create a new +repository** on GitHub, then clone your new repo. -You need Node 20+ and pnpm. +You need Deno 2+ and pnpm. ```bash git clone https://github.com// my-app cd my-app -pnpm setup +deno --version # Verify Deno 2+ +pnpm install # Install frontend deps +./scripts/setup.sh # Generate .env with auth secret ``` -`pnpm setup` installs dependencies and writes a `.env` file with a generated auth secret. +`./scripts/setup.sh` writes a `.env` file with a generated auth secret. Start the database: ```bash -docker compose up -d # Starts PostgreSQL + Redis -pnpm db:migrate # Creates the initial tables +docker compose up -d # Starts PostgreSQL + Redis +deno task db:migrate # Creates the initial tables ``` -> **Note on auth tables:** Better Auth manages its own tables (users, sessions, accounts). The initial migration already includes them. If you add Better Auth plugins later (2FA, API keys, organisations, etc.), regenerate the schema first: +> **Note on auth tables:** Better Auth manages its own tables (users, sessions, +> accounts). The initial migration already includes them. If you add Better Auth +> plugins later (2FA, API keys, organisations, etc.), regenerate the schema +> first: > > ```bash -> npx @better-auth/cli generate # Updates packages/db/src/schema/ from your auth config -> pnpm db:generate # Creates the migration -> pnpm db:migrate # Applies it +> deno run -A npm:@better-auth/cli generate # Updates packages/db/src/schema/ from your auth config +> deno task db:generate # Creates the migration +> deno task db:migrate # Applies it > ``` > -> See the [Better Auth database docs](https://www.better-auth.com/docs/concepts/database) for the full reference. +> See the +> [Better Auth database docs](https://www.better-auth.com/docs/concepts/database) +> for the full reference. Run everything: ```bash -pnpm dev +deno task dev # Backend on :9999 +deno task dev:frontend # Frontend on :3000 ``` -Open [localhost:5173](http://localhost:5173). You're live. +Open [localhost:3000](http://localhost:3000). You're live. ## Daily Commands ```bash -pnpm dev # Run everything -pnpm test # Run tests -pnpm lint # Check code -pnpm typecheck # Check types +deno task dev # Run backend +deno task dev:frontend # Run frontend +deno test -A # Run backend + shared/db/email-templates tests +deno task test:frontend # Run frontend tests +deno lint # Check code +deno check # Check types ``` ## Build Something @@ -67,20 +79,20 @@ pnpm typecheck # Check types ### Add a Backend Module ```bash -pnpm new:module posts +./scripts/new-module.sh posts ``` This scaffolds a complete module at `apps/backend/src/modules/posts/`: -| File | Purpose | -|------|---------| -| `routes.ts` | OpenAPI route definitions with Zod schemas | -| `handlers.ts` | HTTP handlers — reads input, calls repo, maps Result to response | -| `posts.repository.ts` | Data access — uses `tryInfra`, returns `Result`, never throws | -| `posts.errors.ts` | Typed domain error variants (`PostNotFound`, etc.) | -| `usecases/` | Pure business logic — no DB, no async, fully unit-testable | -| `__tests__/` | Integration test stubs | -| `index.ts` | Wires routes to handlers, exports the router | +| File | Purpose | +| --------------------- | ---------------------------------------------------------------- | +| `routes.ts` | OpenAPI route definitions with Zod schemas | +| `handlers.ts` | HTTP handlers — reads input, calls repo, maps Result to response | +| `posts.repository.ts` | Data access — uses `tryInfra`, returns `Result`, never throws | +| `posts.errors.ts` | Typed domain error variants (`PostNotFound`, etc.) | +| `posts.usecases.ts` | Pure business logic — no DB, no async, fully unit-testable | +| `__tests__/` | Integration test stubs | +| `index.ts` | Wires routes to handlers, exports the router | Register it in `apps/backend/src/routes/index.ts`: @@ -94,7 +106,8 @@ export const routes = [users, posts]; export const publicRoutes = [health, posts]; ``` -Then flesh out the repository with real Drizzle queries and add your DB schema to `packages/db/src/schema/`. +Then flesh out the repository with real Drizzle queries and add your DB schema +to `packages/db/src/schema/`. ### Add a Frontend Page @@ -115,8 +128,8 @@ Done. TanStack Router handles the rest. Edit `packages/db/src/schema/` and run: ```bash -pnpm db:generate # Creates migration -pnpm db:migrate # Applies it +deno task db:generate # Creates migration +deno task db:migrate # Applies it ``` ## Use the Batteries @@ -124,7 +137,7 @@ pnpm db:migrate # Applies it ### Upload Files ```typescript -import { getUploadUrl, generateKey } from "@/lib/storage"; +import { generateKey, getUploadUrl } from "@/lib/storage"; // Generate presigned upload URL const key = generateKey("photo.jpg", "avatars"); @@ -158,12 +171,13 @@ await addJob("email", { }); ``` -Run workers: `pnpm --filter backend jobs` +Run workers: +`deno run --env-file=apps/backend/.env -A apps/backend/src/jobs/worker.ts` ### Rate Limit Routes ```typescript -import { rateLimit, authRateLimit } from "@/lib/rate-limit"; +import { authRateLimit, rateLimit } from "@/lib/rate-limit"; // 100 requests per minute app.use("/api/*", rateLimit()); @@ -174,49 +188,58 @@ app.post("/api/auth/login", authRateLimit, loginHandler); ## Deploy -**Backend** → Docker on any VPS, or Railway/Render -**Frontend** → Vercel (zero config) -**Database** → Supabase, Neon, or Railway +**Full stack** → `docker compose up -d` (Caddy + backend on one VPS) +**Frontend only** → Vercel (zero config) **Database** → Supabase, Neon, or +Railway See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) for the full guide. ## Project Layout ```bash -apps/backend/src/ - modules/ ← Your features go here - lib/ ← Reusable utilities - jobs/ ← Background workers - middlewares/ ← Auth, etc. +apps/backend/ + Dockerfile ← Standalone backend image (Deno compile → Alpine) + src/ ← Modules, lib, jobs, middlewares -apps/frontend/src/ - routes/ ← Pages (file-based routing) - lib/ ← API client, helpers - components/ ← UI components +apps/frontend/ + Dockerfile ← Deno builder + Caddy runner image + Caddyfile ← SPA + /api/* reverse proxy + src/ ← Routes, lib, components packages/ db/ ← Database schemas shared/ ← Types shared everywhere email-templates/ ← Email builders + +docker-compose.yml ← Full stack (Caddy :80 + backend :9999 + Postgres + Redis) ``` ## Learn More -- [Architecture Guide](apps/backend/docs/ARCHITECTURE.md) — How the backend is structured -- [Backend Decisions](apps/backend/docs/DECISIONS.md) — Why each backend choice was made -- [Frontend Decisions](apps/frontend/docs/DECISIONS.md) — Why each frontend choice was made -- [Batteries Included](docs/BATTERIES.md) — All built-in utilities with usage examples +- [Architecture Guide](apps/backend/docs/ARCHITECTURE.md) — How the backend is + structured +- [Backend Decisions](apps/backend/docs/DECISIONS.md) — Why each backend choice + was made +- [Frontend Decisions](apps/frontend/docs/DECISIONS.md) — Why each frontend + choice was made +- [Batteries Included](docs/BATTERIES.md) — All built-in utilities with usage + examples - [Deployment Guide](docs/DEPLOYMENT.md) — Ship to production - [Writing Style Guide](docs/WRITING.md) — How we write docs and articles - [API Docs](http://localhost:9999/docs) — Auto-generated from your code ## This is a Template, Not a Framework -When you create a repo from this template, you own it. There is no upstream to pull from. Delete what you don't need, rename what makes sense to rename, and diverge freely. +When you create a repo from this template, you own it. There is no upstream to +pull from. Delete what you don't need, rename what makes sense to rename, and +diverge freely. -What to keep: the `packages/shared` Result type, the `tryInfra` pattern, the module scaffolder, the Biome config. +What to keep: the `packages/shared` Result type, the `tryInfra` pattern, the +module scaffolder, the Deno config (`deno.json`), and the Biome config (frontend +linting). -What to replace: the example `users` module with your own domain, the license, this README. +What to replace: the example `users` module with your own domain, the license, +this README. --- @@ -224,4 +247,5 @@ What to replace: the example `users` module with your own domain, the license, t Proprietary. Copyright © 2026 Orcta. All rights reserved. -This codebase is not open source. Do not distribute, sublicense, or use outside the organisation without written permission. +This codebase is not open source. Do not distribute, sublicense, or use outside +the organisation without written permission. diff --git a/apps/backend/.swcrc b/apps/backend/.swcrc deleted file mode 100644 index d97f262..0000000 --- a/apps/backend/.swcrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": true, - "decorators": true, - "dynamicImport": true - }, - "paths": { - "@/*": ["./src/*"] - }, - "baseUrl": "./", - "target": "es2022", - "transform": { - "legacyDecorator": true, - "decoratorMetadata": true - } - }, - "module": { - "type": "es6" - }, - "sourceMaps": true -} diff --git a/apps/backend/Dockerfile b/apps/backend/Dockerfile index 5f3bba3..7a9198d 100644 --- a/apps/backend/Dockerfile +++ b/apps/backend/Dockerfile @@ -1,42 +1,49 @@ -# Build stage -FROM node:20-alpine AS builder -RUN corepack enable pnpm +# ─── Build ───────────────────────────────────────────────────────────── +FROM denoland/deno:debian-2.7.14 AS builder WORKDIR /app -# Copy workspace files -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ -COPY tsconfig.base.json ./ -COPY packages ./packages -COPY apps/backend ./apps/backend +# Layer 1: Configuration (changes infrequently — preserves dep cache) +COPY deno.json deno.lock ./ +COPY apps/backend/deno.json ./apps/backend/ +COPY apps/frontend/deno.json ./apps/frontend/ +COPY packages/shared/deno.json ./packages/shared/ +COPY packages/db/deno.json ./packages/db/ +COPY packages/email-templates/deno.json ./packages/email-templates/ -# Install dependencies -RUN pnpm install --frozen-lockfile +# Layer 2: Shared packages (change less often than app code) +COPY packages/shared/ ./packages/shared/ +COPY packages/db/ ./packages/db/ +COPY packages/email-templates/ ./packages/email-templates/ -# Build packages and backend -RUN pnpm build:packages -RUN pnpm --filter backend build +# Layer 3: Application source +COPY apps/backend/ ./apps/backend/ -# Produce a flat, symlink-free node_modules for the backend only. -# This is the canonical pnpm approach for Docker — avoids broken symlinks -# when the workspace virtual store is copied without its sibling package dirs. -RUN pnpm --filter backend deploy --prod /app/deploy +# Cache dependencies (uses lockfile + import maps for npm resolution) +RUN deno cache apps/backend/src/index.ts -# Production stage -FROM node:20-alpine AS runner +# Compile to standalone binary (includes Deno runtime + all deps) +RUN deno compile \ + --allow-env \ + --allow-net \ + --allow-read \ + --allow-sys \ + --allow-ffi \ + --output /usr/local/bin/orcta-backend \ + apps/backend/src/index.ts -WORKDIR /app +# ─── Run ────────────────────────────────────────────────────────────── +FROM alpine:3.20 + +RUN apk add --no-cache ca-certificates && \ + addgroup -S app && adduser -S app -G app + +COPY --from=builder /usr/local/bin/orcta-backend /usr/local/bin/orcta-backend -# Compiled application sources -COPY --from=builder /app/apps/backend/dist ./ -# Flat node_modules from pnpm deploy (includes all workspace packages resolved) -COPY --from=builder /app/deploy/node_modules ./node_modules -# SQL migration files (needed by src/db/migrate.js at runtime) -COPY --from=builder /app/packages/db/migrations ./migrations +USER app -ENV NODE_ENV=production ENV PORT=9999 EXPOSE 9999 -CMD ["node", "src/index.js"] +CMD ["orcta-backend"] diff --git a/apps/backend/deno.json b/apps/backend/deno.json new file mode 100644 index 0000000..aae5e68 --- /dev/null +++ b/apps/backend/deno.json @@ -0,0 +1,98 @@ +{ + "name": "backend", + "version": "0.1.0", + "exports": "./src/index.ts", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + }, + "module": "NodeNext", + "moduleResolution": "NodeNext", + "skipLibCheck": true + }, + "tasks": { + "dev": "deno run --watch --env-file=.env -A src/index.ts", + "start": "deno run --env-file=.env -A src/index.ts", + "worker": "deno run --env-file=.env -A src/jobs/worker.ts", + "test": "deno test --env-file=.env -A", + "db:migrate": "deno run --env-file=.env --allow-env --allow-net --allow-read --allow-sys src/db/migrate.ts", + "db:studio": "deno run --env-file=.env -A npm:drizzle-kit studio", + "db:generate": "deno run --env-file=.env -A npm:drizzle-kit generate" + }, + "imports": { + "@/": "./src/", + "@/app": "./src/app.ts", + "@/db": "./src/db/index.ts", + "@/env": "./src/env.ts", + "@/lib/auth": "./src/lib/auth.ts", + "@/lib/create-app": "./src/lib/create-app.ts", + "@/lib/configure-open-api": "./src/lib/configure-open-api.ts", + "@/lib/types": "./src/lib/types.ts", + "@/lib/redis": "./src/lib/redis.ts", + "@/lib/error": "./src/lib/error.ts", + "@/lib/infra": "./src/lib/infra.ts", + "@/lib/cache": "./src/lib/cache.ts", + "@/lib/storage": "./src/lib/storage.ts", + "@/lib/rate-limit": "./src/lib/rate-limit.ts", + "@/lib/ws": "./src/lib/ws.ts", + "@/lib/http-status-codes": "./src/lib/http-status-codes.ts", + "@/lib/http-status-phrases": "./src/lib/http-status-phrases.ts", + "@/middlewares/auth": "./src/middlewares/auth.ts", + "@/middlewares/wide-event": "./src/middlewares/wide-event.ts", + "@/modules/health": "./src/modules/health/index.ts", + "@/modules/health/handlers": "./src/modules/health/handlers.ts", + "@/modules/health/routes": "./src/modules/health/routes.ts", + "@/modules/users": "./src/modules/users/index.ts", + "@/modules/users/handlers": "./src/modules/users/handlers.ts", + "@/modules/users/routes": "./src/modules/users/routes.ts", + "@/modules/users/users.repository": "./src/modules/users/users.repository.ts", + "@/modules/users/users.errors": "./src/modules/users/users.errors.ts", + "@/modules/users/users.usecases": "./src/modules/users/users.usecases.ts", + "@/jobs/index": "./src/jobs/index.ts", + "@/jobs/worker": "./src/jobs/worker.ts", + "@/lib/email": "./src/lib/email.ts", + "hono": "npm:hono", + "hono/cors": "npm:hono/cors", + "hono/dev": "npm:hono/dev", + "hono/ws": "npm:hono/ws", + "@hono/zod-openapi": "npm:@hono/zod-openapi", + "@hono/swagger-ui": "npm:@hono/swagger-ui", + "@hono/zod-validator": "npm:@hono/zod-validator", + "@scalar/hono-api-reference": "npm:@scalar/hono-api-reference", + "better-auth": "npm:better-auth", + "better-auth/adapters": "npm:better-auth/adapters", + "better-auth/plugins": "npm:better-auth/plugins", + "better-auth/plugins/two-factor": "npm:better-auth/plugins/two-factor", + "ioredis": "npm:ioredis", + "bullmq": "npm:bullmq", + "pino": "npm:pino", + "pino-pretty": "npm:pino-pretty", + "hono-pino": "npm:hono-pino", + "stoker": "npm:stoker", + "stoker/middlewares": "npm:stoker/middlewares", + "stoker/openapi": "npm:stoker/openapi", + "@aws-sdk/client-s3": "npm:@aws-sdk/client-s3", + "@aws-sdk/s3-request-presigner": "npm:@aws-sdk/s3-request-presigner", + "zod": "npm:zod", + "drizzle-kit": "npm:drizzle-kit", + "drizzle-orm": "npm:drizzle-orm", + "drizzle-orm/pg-core": "npm:drizzle-orm/pg-core", + "drizzle-orm/postgres-js": "npm:drizzle-orm/postgres-js", + "drizzle-orm/postgres-js/migrator": "npm:drizzle-orm/postgres-js/migrator", + "postgres": "npm:postgres", + "drizzle-zod": "npm:drizzle-zod", + "@node-rs/argon2": "npm:@node-rs/argon2", + "resend": "npm:resend", + "dotenv": "npm:dotenv", + "dotenv-expand": "npm:dotenv-expand", + "@axiomhq/pino": "npm:@axiomhq/pino" + }, + "lint": { + "rules": { + "exclude": ["no-explicit-any", "no-non-null-assertion"] + } + }, + "test": { + "include": ["src/**/*.test.ts"] + } +} diff --git a/apps/backend/docs/ARCHITECTURE.md b/apps/backend/docs/ARCHITECTURE.md index 777ae11..91e3517 100644 --- a/apps/backend/docs/ARCHITECTURE.md +++ b/apps/backend/docs/ARCHITECTURE.md @@ -10,12 +10,16 @@ Simple rules, predictable code. Request → Route → Handler → [Use-Case →] Repository → Database ``` -**Route** — Validates input with Zod, defines OpenAPI spec -**Handler** — Imperative shell. Calls use-case or repository, maps `Result` to HTTP -**Use-Case** — Functional core. Pure business logic. No HTTP, no direct DB calls. -**Repository** — Imperative shell. Data access via `tryInfra`. Never throws. Returns `Result`. +**Route** — Validates input with Zod, defines OpenAPI spec\ +**Handler** — Imperative shell. Calls use-case or repository, maps `Result` to +HTTP\ +**Use-Case** — Functional core. Pure business logic. No HTTP, no direct DB +calls.\ +**Repository** — Imperative shell. Data access via `tryInfra`. Never throws. +Returns `Result`. -The use-case layer is **optional per route**, not optional per module. Use it when business logic exists. Skip it when a handler is just calling a repository. +The use-case layer is **optional per route**, not optional per module. Use it +when business logic exists. Skip it when a handler is just calling a repository. --- @@ -24,14 +28,17 @@ The use-case layer is **optional per route**, not optional per module. Use it wh The architecture enforces this boundary at the type level. **Imperative shell** (handlers, repositories) — talks to the outside world: + - Receives HTTP requests, persists data, calls external services - Produces `Result` values from messy reality **Functional core** (use-cases) — pure functions over domain values: + - Receives already-loaded data as arguments - Applies business rules - Returns `Result` — no side effects, no async DB calls -- Trivially testable: call with plain values, assert on the returned `Result` — no mocks, no DB +- Trivially testable: call with plain values, assert on the returned `Result` — + no mocks, no DB ``` ┌──────────────────────────────────────────────┐ @@ -53,10 +60,10 @@ The architecture enforces this boundary at the type level. **Never throw. Encode all failures in the return type.** -| Category | Type | Produced by | Handled by | -|---|---|---|---| -| Domain | Typed discriminated union | Repository functions | Handlers — switch/match exhaustively | -| Infrastructure | `InfrastructureError` | `tryInfra()` | Handlers — `isInfraError()` guard → 500 | +| Category | Type | Produced by | Handled by | +| -------------- | ------------------------- | -------------------- | --------------------------------------- | +| Domain | Typed discriminated union | Repository functions | Handlers — switch/match exhaustively | +| Infrastructure | `InfrastructureError` | `tryInfra()` | Handlers — `isInfraError()` guard → 500 | ```typescript // ❌ Don't — hidden control flow, nothing typed at the call site @@ -70,8 +77,9 @@ async function getPost(id: string): Promise { async function findPostById( id: string, ): Promise> { - const result = await tryInfra("find post by id", () => - db.query.posts.findFirst({ where: eq(posts.id, id) }), + const result = await tryInfra( + "find post by id", + () => db.query.posts.findFirst({ where: eq(posts.id, id) }), ); if (!result.ok) return result; if (!result.value) return err({ type: "POST_NOT_FOUND", lookup: id }); @@ -94,74 +102,111 @@ export const getPostHandler: AppRouteHandler = async (c) => { ok: (post) => c.json(success(post), OK), err: (e) => isInfraError(e) - ? c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR) - : c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND), + ? c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ) + : c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ), }); }; ``` **Add it** when any of these are true: + - Logic spans multiple repository results - A rule can be expressed as a pure function over domain values - The logic is worth testing in isolation, without touching the DB ```typescript // posts.usecases.ts — pure functions, no imports from @/db -import type { User, Post } from "@repo/db"; +import type { Post, User } from "@repo/db"; import type { NotPostAuthor } from "./posts.errors"; -import { ok, err, type Result } from "@repo/shared"; +import { err, ok, type Result } from "@repo/shared"; export function authorizePostUpdate( user: User, post: Post, ): Result { - if (post.authorId !== user.id) + if (post.authorId !== user.id) { return err({ type: "NOT_POST_AUTHOR", userId: user.id, postId: post.id }); + } return ok(post); } ``` ```typescript // posts/handlers.ts — handler runs the imperative shell, calls use-case for the rule -export const updatePostHandler: AppRouteHandler = async (c) => { +export const updatePostHandler: AppRouteHandler = async ( + c, +) => { const user = c.get("user"); const { id } = c.req.valid("param"); const body = c.req.valid("json"); const found = await findPostById(id); - if (!found.ok) - return c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND); + if (!found.ok) { + return c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ); + } const authorized = authorizePostUpdate(user, found.value); - if (!authorized.ok) - return c.json(failure({ code: "FORBIDDEN", message: "Not your post" }), FORBIDDEN); + if (!authorized.ok) { + return c.json( + failure({ code: "FORBIDDEN", message: "Not your post" }), + FORBIDDEN, + ); + } const result = await updatePost(id, body); return match(result, { ok: (post) => c.json(success(post), OK), - err: () => c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR), + err: () => + c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ), }); }; ``` -Use-cases receive already-loaded data as arguments — they never import from `@/db` or call repository functions. The handler is the orchestrator: it calls the repositories (imperative shell), then passes the results into the use-case (functional core). Because use-cases are pure functions, they require no mocking to test: call them with plain values and assert on the `Result`. +Use-cases receive already-loaded data as arguments — they never import from +`@/db` or call repository functions. The handler is the orchestrator: it calls +the repositories (imperative shell), then passes the results into the use-case +(functional core). Because use-cases are pure functions, they require no mocking +to test: call them with plain values and assert on the `Result`. --- ## Result Helpers ```typescript -import { ok, err, map, andThen, andThenAsync, match, isOk, isErr } from "@repo/shared"; +import { + andThen, + andThenAsync, + err, + isErr, + isOk, + map, + match, + ok, +} from "@repo/shared"; ``` -| Helper | Use when | -|---|---| -| `map(result, fn)` | Transform the value, pass error through unchanged | -| `andThen(result, fn)` | Chain a sync Result-returning function | -| `andThenAsync(result, fn)` | Chain an async Result-returning function | +| Helper | Use when | +| ---------------------------- | ----------------------------------------------------------- | +| `map(result, fn)` | Transform the value, pass error through unchanged | +| `andThen(result, fn)` | Chain a sync Result-returning function | +| `andThenAsync(result, fn)` | Chain an async Result-returning function | | `match(result, { ok, err })` | Handle both branches exhaustively — primary handler pattern | -The `if (!result.ok) return result` pattern is still fine inside repositories when you need to inspect intermediate values. Use helpers when they reduce noise, not to be clever. +The `if (!result.ok) return result` pattern is still fine inside repositories +when you need to inspect intermediate values. Use helpers when they reduce +noise, not to be clever. ```typescript // match in a handler — both branches handled, compiler enforces exhaustiveness @@ -169,8 +214,14 @@ return match(result, { ok: (user) => c.json(success(user), OK), err: (e) => isInfraError(e) - ? c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR) - : c.json(failure({ code: "NOT_FOUND", message: "User not found" }), NOT_FOUND), + ? c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ) + : c.json( + failure({ code: "NOT_FOUND", message: "User not found" }), + NOT_FOUND, + ), }); // andThenAsync — chain two async repo calls, short-circuits on first error @@ -215,38 +266,43 @@ modules/posts/ handlers.test.ts ← HTTP integration tests (full stack) ``` -Test files live in `__tests__/` alongside the module they test, not in a top-level `tests/` directory. The vitest config uses `src/**/*.test.ts` to pick them up automatically. +Test files live in `__tests__/` alongside the module they test, not in a +top-level `tests/` directory. The Deno test config (in `deno.json`) uses +`**/*.test.ts` to pick them up automatically. --- ## Key Primitives -| Import | From | Purpose | -|---|---|---| -| `Result`, `ok`, `err`, `map`, `andThen`, `andThenAsync`, `match`, `isOk`, `isErr` | `@repo/shared` | Result type and combinators | -| `apiSuccessSchema(dataSchema)` | `@repo/shared` | Canonical `{ success: true, data }` Zod schema for route responses | -| `apiErrorSchema` | `@repo/shared` | Canonical `{ success: false, error }` Zod schema for route responses | -| `tryInfra` | `@/lib/infra` | Single catch boundary for all repositories | -| `InfrastructureError` | `@/lib/error` | Wraps unknown infrastructure throws | -| `isInfraError` | `@/lib/types` | Type guard for handlers | -| `success`, `failure` | `@/lib/types` | HTTP response shape helpers | -| `jsonRes(schema, description)` | `@/lib/types` | Collapse `{ content: { "application/json": { schema } }, description }` in route definitions | -| `jsonBody(schema)` | `@/lib/types` | Collapse `{ content: { "application/json": { schema } } }` for request bodies | -| `OK`, `CREATED`, `NOT_FOUND`, `INTERNAL_SERVER_ERROR`, … | `@/lib/types` | Named HTTP status constants — re-exported from `src/lib/http-status-codes.ts` | -| `AppRouteHandler` | `@/lib/types` | Type-safe handler type | -| `WideEvent` | `@/lib/types` | Type for the per-request canonical log event | -| `addToEvent(c, fields)` | `@/lib/types` | Merge business context into the in-flight wide event | -| `authMiddleware` | `@/middlewares/auth` | Auth middleware (plain function, not factory) | -| `requireRole` | `@/middlewares/auth` | Role guard (factory — takes role strings) | -| `wideEventMiddleware` | `@/middlewares/wide-event` | Canonical log line accumulator — emits one wide event per request | +| Import | From | Purpose | +| --------------------------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------- | +| `Result`, `ok`, `err`, `map`, `andThen`, `andThenAsync`, `match`, `isOk`, `isErr` | `@repo/shared` | Result type and combinators | +| `apiSuccessSchema(dataSchema)` | `@repo/shared` | Canonical `{ success: true, data }` Zod schema for route responses | +| `apiErrorSchema` | `@repo/shared` | Canonical `{ success: false, error }` Zod schema for route responses | +| `tryInfra` | `@/lib/infra` | Single catch boundary for all repositories | +| `InfrastructureError` | `@/lib/error` | Wraps unknown infrastructure throws | +| `isInfraError` | `@/lib/types` | Type guard for handlers | +| `success`, `failure` | `@/lib/types` | HTTP response shape helpers | +| `jsonRes(schema, description)` | `@/lib/types` | Collapse `{ content: { "application/json": { schema } }, description }` in route definitions | +| `jsonBody(schema)` | `@/lib/types` | Collapse `{ content: { "application/json": { schema } } }` for request bodies | +| `OK`, `CREATED`, `NOT_FOUND`, `INTERNAL_SERVER_ERROR`, … | `@/lib/types` | Named HTTP status constants — re-exported from `src/lib/http-status-codes.ts` | +| `AppRouteHandler` | `@/lib/types` | Type-safe handler type | +| `WideEvent` | `@/lib/types` | Type for the per-request canonical log event | +| `addToEvent(c, fields)` | `@/lib/types` | Merge business context into the in-flight wide event | +| `authMiddleware` | `@/middlewares/auth` | Auth middleware (plain function, not factory) | +| `requireRole` | `@/middlewares/auth` | Role guard (factory — takes role strings) | +| `wideEventMiddleware` | `@/middlewares/wide-event` | Canonical log line accumulator — emits one wide event per request | --- ## Observability -This codebase uses the **wide event / canonical log line** pattern from [loggingsucks.com](https://loggingsucks.com). +This codebase uses the **wide event / canonical log line** pattern from +[loggingsucks.com](https://loggingsucks.com). -Instead of emitting many small log statements throughout a request, a single rich event is built up over the request lifecycle and emitted once at the end with everything needed to answer any debugging question. +Instead of emitting many small log statements throughout a request, a single +rich event is built up over the request lifecycle and emitted once at the end +with everything needed to answer any debugging question. ``` Request arrives @@ -269,7 +325,9 @@ wideEventMiddleware ← appends: status_code, duration_ms, outcome → logg ```typescript import { addToEvent } from "@/lib/types"; -export const createOrderHandler: AppRouteHandler = async (c) => { +export const createOrderHandler: AppRouteHandler = async ( + c, +) => { const body = c.req.valid("json"); const result = await createOrder(body); @@ -281,25 +339,31 @@ export const createOrderHandler: AppRouteHandler = async (c) = return c.json(success(result.value), CREATED); } - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); }; ``` -Every authenticated request automatically carries `user.id` and `user.role` — `authMiddleware` adds them without the handler needing to. +Every authenticated request automatically carries `user.id` and `user.role` — +`authMiddleware` adds them without the handler needing to. ### Sampling -The wide event middleware uses **tail-based sampling** — the decision is made after the request completes: +The wide event middleware uses **tail-based sampling** — the decision is made +after the request completes: -| Condition | Kept? | -|---|---| -| `status_code >= 500` | Always | -| `outcome === "error"` | Always | -| `duration_ms > 2000` | Always | -| `user.role === "admin"` | Always | -| Everything else | 5% random sample | +| Condition | Kept? | +| ----------------------- | ---------------- | +| `status_code >= 500` | Always | +| `outcome === "error"` | Always | +| `duration_ms > 2000` | Always | +| `user.role === "admin"` | Always | +| Everything else | 5% random sample | -This keeps Axiom ingest costs low while guaranteeing 100% capture of the events that matter. +This keeps Axiom ingest costs low while guaranteeing 100% capture of the events +that matter. ### Axiom setup @@ -312,4 +376,6 @@ SERVICE_VERSION= REGION=eu-west-1 ``` -In development these vars are absent and logs go to stdout with `pino-pretty`. In production, pino writes to both stdout (captured by Docker) and Axiom simultaneously. +In development these vars are absent and logs go to stdout with `pino-pretty`. +In production, pino writes to both stdout (captured by Docker) and Axiom +simultaneously. diff --git a/apps/backend/docs/DECISIONS.md b/apps/backend/docs/DECISIONS.md index 3a670f9..ce050e5 100644 --- a/apps/backend/docs/DECISIONS.md +++ b/apps/backend/docs/DECISIONS.md @@ -10,15 +10,20 @@ Why we chose what we chose. **Why**: -1. **Native TypeScript** — Written in TypeScript, not typed after the fact. Types are accurate and complete. +1. **Native TypeScript** — Written in TypeScript, not typed after the fact. + Types are accurate and complete. -2. **Web Standards** — Uses `Request`/`Response` from the Fetch API. Your code works in Node, Deno, Bun, and edge runtimes without changes. +2. **Web Standards** — Uses `Request`/`Response` from the Fetch API. Your code + works in Node, Deno, Bun, and edge runtimes without changes. -3. **First-class OpenAPI** — `@hono/zod-openapi` integrates Zod schemas directly into route definitions. One source of truth for validation and docs. +3. **First-class OpenAPI** — `@hono/zod-openapi` integrates Zod schemas directly + into route definitions. One source of truth for validation and docs. -4. **Performance** — Faster than Express. Comparable to Fastify. Uses a fast RegExp-based router. +4. **Performance** — Faster than Express. Comparable to Fastify. Uses a fast + RegExp-based router. -5. **Minimal** — No opinions about structure. No magic. You see exactly what's happening. +5. **Minimal** — No opinions about structure. No magic. You see exactly what's + happening. **Trade-offs**: @@ -30,20 +35,24 @@ Why we chose what we chose. ```typescript // Express — types are bolted on, validation is separate -app.post('/users', validateBody(schema), (req: Request, res: Response) => { +app.post("/users", validateBody(schema), (req: Request, res: Response) => { const body = req.body; // any, unless you cast }); // Hono — types flow from schema, validation is declarative const route = createRoute({ - method: 'post', - path: '/users', - request: { body: { content: { 'application/json': { schema: userSchema } } } }, - responses: { 201: { content: { 'application/json': { schema: userResponseSchema } } } }, + method: "post", + path: "/users", + request: { + body: { content: { "application/json": { schema: userSchema } } }, + }, + responses: { + 201: { content: { "application/json": { schema: userResponseSchema } } }, + }, }); app.openapi(route, (c) => { - const body = c.req.valid('json'); // Fully typed from schema + const body = c.req.valid("json"); // Fully typed from schema }); ``` @@ -55,30 +64,35 @@ app.openapi(route, (c) => { **Why**: -1. **SQL-first** — Drizzle queries look like SQL. If you know SQL, you know Drizzle. +1. **SQL-first** — Drizzle queries look like SQL. If you know SQL, you know + Drizzle. ```typescript // Drizzle — reads like SQL const users = await db .select() .from(usersTable) - .where(eq(usersTable.status, 'active')) + .where(eq(usersTable.status, "active")) .limit(10); // Prisma — proprietary API const users = await prisma.user.findMany({ - where: { status: 'active' }, + where: { status: "active" }, take: 10, }); ``` -1. **No code generation** — Prisma requires `prisma generate` after schema changes. Drizzle schemas are just TypeScript. Change and go. +1. **No code generation** — Prisma requires `prisma generate` after schema + changes. Drizzle schemas are just TypeScript. Change and go. -2. **Lightweight** — No engine binary. Drizzle is ~50KB. Prisma ships a Rust query engine. +2. **Lightweight** — No engine binary. Drizzle is ~50KB. Prisma ships a Rust + query engine. -3. **Better types** — `$inferSelect` and `$inferInsert` give you exact types from your schema. No drift. +3. **Better types** — `$inferSelect` and `$inferInsert` give you exact types + from your schema. No drift. -4. **Migrations as SQL** — Drizzle generates plain SQL migrations. You can read and edit them. Prisma migrations are harder to customize. +4. **Migrations as SQL** — Drizzle generates plain SQL migrations. You can read + and edit them. Prisma migrations are harder to customize. **Trade-offs**: @@ -94,7 +108,8 @@ const users = await prisma.user.findMany({ **Why**: -1. **Explicit outcomes** — Every possible result is in the type signature. No hidden `throw` statements. +1. **Explicit outcomes** — Every possible result is in the type signature. No + hidden `throw` statements. ```typescript // Exceptions — caller doesn't know what can go wrong @@ -105,9 +120,9 @@ async function createUser(data): Promise { // Discriminated unions — outcomes are explicit async function createUser(data): Promise< - | { type: 'CREATED'; user: User } - | { type: 'EMAIL_EXISTS' } - | { type: 'INVALID_DATA'; errors: string[] } + | { type: "CREATED"; user: User } + | { type: "EMAIL_EXISTS" } + | { type: "INVALID_DATA"; errors: string[] } > { // Caller knows exactly what to handle } @@ -117,17 +132,22 @@ async function createUser(data): Promise< ```typescript switch (result.type) { - case 'CREATED': return c.json(result.user, 201); - case 'EMAIL_EXISTS': return c.json({ error: 'Email taken' }, 409); - // TypeScript: "Property 'INVALID_DATA' is missing" + case "CREATED": + return c.json(result.user, 201); + case "EMAIL_EXISTS": + return c.json({ error: "Email taken" }, 409); + // TypeScript: "Property 'INVALID_DATA' is missing" } ``` -1. **No try-catch chains** — Exceptions bubble up. You need try-catch everywhere or risk unhandled errors. Unions are handled where they're used. +1. **No try-catch chains** — Exceptions bubble up. You need try-catch everywhere + or risk unhandled errors. Unions are handled where they're used. -2. **Better for async** — Exceptions in async code are easy to lose. Forgotten `await`, missing `.catch()`. Unions are just data. +2. **Better for async** — Exceptions in async code are easy to lose. Forgotten + `await`, missing `.catch()`. Unions are just data. -3. **Testing is simpler** — Return values are easier to assert than thrown errors. +3. **Testing is simpler** — Return values are easier to assert than thrown + errors. **Trade-offs**: @@ -143,13 +163,15 @@ switch (result.type) { **Why**: -The classic port/adapter split (a TypeScript interface + a separate Drizzle implementation) adds indirection without payoff at this scale: +The classic port/adapter split (a TypeScript interface + a separate Drizzle +implementation) adds indirection without payoff at this scale: - We never swap the underlying database at runtime - The DB can run locally in tests — mocking is not needed - One extra file per module accumulates quickly -Instead, repositories are plain async functions in a single `*.repository.ts` file. Each function: +Instead, repositories are plain async functions in a single `*.repository.ts` +file. Each function: - Returns `Result` — never throws - Wraps all DB calls in `tryInfra` — single catch boundary @@ -160,8 +182,9 @@ Instead, repositories are plain async functions in a single `*.repository.ts` fi export async function findUserById( id: string, ): Promise> { - const result = await tryInfra(`fetch user ${id}`, () => - db.query.users.findFirst({ where: eq(users.id, id) }), + const result = await tryInfra( + `fetch user ${id}`, + () => db.query.users.findFirst({ where: eq(users.id, id) }), ); if (!result.ok) return result; if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: id }); @@ -169,18 +192,23 @@ export async function findUserById( } ``` -**Testing without mocks**: repositories are integration-tested against a real local DB. -The `Result` return type makes assertions straightforward without any mock setup: +**Testing without mocks**: repositories are integration-tested against a real +local DB. The `Result` return type makes assertions straightforward without any +mock setup: ```typescript const result = await findUserById("nonexistent"); -expect(result).toEqual({ ok: false, error: { type: "USER_NOT_FOUND", lookup: "nonexistent" } }); +expect(result).toEqual({ + ok: false, + error: { type: "USER_NOT_FOUND", lookup: "nonexistent" }, +}); ``` **Trade-offs**: - No ability to inject a fake repo (not needed — real DB is fast and simple) -- Tight coupling to Drizzle (acceptable — swap cost is low when it's just functions) +- Tight coupling to Drizzle (acceptable — swap cost is low when it's just + functions) --- @@ -190,48 +218,104 @@ expect(result).toEqual({ ok: false, error: { type: "USER_NOT_FOUND", lookup: "no **Why**: -1. **Database agnostic** — Works with any database via adapters. Drizzle adapter included. +1. **Database agnostic** — Works with any database via adapters. Drizzle adapter + included. -2. **Session-based by default** — JWTs are stateless but hard to revoke. Sessions are simpler and more secure. +2. **Session-based by default** — JWTs are stateless but hard to revoke. + Sessions are simpler and more secure. -3. **Built-in features** — Email/password, OAuth, email verification, password reset. No assembly required. +3. **Built-in features** — Email/password, OAuth, email verification, password + reset. No assembly required. 4. **TypeScript native** — Types are correct and complete. -5. **Framework agnostic** — Works with Hono, Express, anything with Request/Response. +5. **Framework agnostic** — Works with Hono, Express, anything with + Request/Response. **Configuration**: ```typescript export const auth = betterAuth({ - database: drizzleAdapter(db, { provider: 'pg' }), + database: drizzleAdapter(db, { provider: "pg" }), emailAndPassword: { enabled: true }, session: { expiresIn: 60 * 60 * 24 * 7, // 7 days - updateAge: 60 * 60 * 24, // Refresh daily + updateAge: 60 * 60 * 24, // Refresh daily }, }); ``` **Schema generation**: -Better Auth manages its own tables: `user`, `session`, `account`, `verification`. The initial migration in `packages/db/migrations/` already includes these. If you add plugins (2FA, API keys, organisations, passkeys, etc.), each plugin adds its own tables. Regenerate the schema before migrating: +Better Auth manages its own tables: `user`, `session`, `account`, +`verification`. The initial migration in `packages/db/migrations/` already +includes these. If you add plugins (2FA, API keys, organisations, passkeys, +etc.), each plugin adds its own tables. Regenerate the schema before migrating: ```bash -npx @better-auth/cli generate # Introspects auth config, updates packages/db/src/schema/ -pnpm db:generate # Drizzle diffing → new migration file -pnpm db:migrate # Applies migration to the database +deno run -A npm:@better-auth/cli generate # Introspects auth config, updates packages/db/src/schema/ +deno task db:generate # Drizzle diffing → new migration file +deno task db:migrate # Applies migration to the database ``` -Run this any time you add or remove a Better Auth plugin. The CLI reads your auth config from `apps/backend/src/lib/auth.ts` directly — it doesn't need a running server. +Run this any time you add or remove a Better Auth plugin. The CLI reads your +auth config from `apps/backend/src/lib/auth.ts` directly — it doesn't need a +running server. + +See the +[Better Auth database docs](https://www.better-auth.com/docs/concepts/database) +for the full table reference and plugin schema additions. -See the [Better Auth database docs](https://www.better-auth.com/docs/concepts/database) for the full table reference and plugin schema additions. +**Gotcha: rate limiting silently no-ops without Redis** + +better-auth ships a built-in rate-limit rule — 3 requests per 10 seconds on +`/sign-in`, `/sign-up`, `/change-password`, `/change-email` — that overrides +whatever `max` you configure at the top level. This project points +`rateLimit.storage` at `"secondary-storage"`: + +```typescript +rateLimit: { + enabled: env.NODE_ENV !== "test", + window: 10, + max: 100, + storage: "secondary-storage", +}, +``` + +`"secondary-storage"` means better-auth calls `ctx.options.secondaryStorage` +to persist rate-limit counters. In this codebase, `secondaryStorage` is only +defined when `REDIS_URL` is set: + +```typescript +const secondaryStorage = redis + ? { get: ..., set: ..., delete: ... } + : undefined; +``` + +Without Redis, better-auth's own rate-limit code calls +`ctx.options.secondaryStorage?.get(key)` — optional chaining on `undefined` +resolves to `undefined` and short-circuits. `get()` always reports "no prior +request," `set()` is a silent no-op. The 3-per-10s rule is configured but +never actually enforced. + +I found this the hard way: `apps/backend/src/modules/users/__tests__/handlers.test.ts` +passed locally for months because nobody runs the test suite with `REDIS_URL` +set by hand. CI provisions Redis unconditionally, so the rule went from +inert to real the moment it ran there — and a test suite that legitimately +signs up a dozen users in a few seconds blew through it instantly, turning +into 429s that the tests then read as 401s (no session cookie, because the +sign-up/sign-in call that was supposed to produce one got rate-limited +instead). `enabled: env.NODE_ENV !== "test"` keeps the protection in every +real environment and turns it off for the one environment where Redis being +present or absent was only ever a test-infra accident, never a security +decision. **Trade-offs**: - Newer library, smaller community than Auth.js/Lucia - Some advanced features still in development -- Schema is Better Auth-owned — don't add custom columns to auth tables; extend the `user` table via the Drizzle schema separately +- Schema is Better Auth-owned — don't add custom columns to auth tables; extend + the `user` table via the Drizzle schema separately --- @@ -241,7 +325,8 @@ See the [Better Auth database docs](https://www.better-auth.com/docs/concepts/da **Why**: -1. **One schema, many uses** — Validate requests, generate OpenAPI, infer TypeScript types. +1. **One schema, many uses** — Validate requests, generate OpenAPI, infer + TypeScript types. ```typescript const userSchema = z.object({ @@ -249,7 +334,7 @@ const userSchema = z.object({ name: z.string().min(2), }); -type User = z.infer; // TypeScript type +type User = z.infer; // TypeScript type // Also used for request validation and OpenAPI spec ``` @@ -280,11 +365,11 @@ const publicUserSchema = userSchema.omit({ email: true }); Not all errors are equal: -| Type | Example | Handling | -|------|---------|----------| +| Type | Example | Handling | +| -------------- | --------------------------- | ------------------------------ | | Infrastructure | DB timeout, network failure | 500, log, don't expose details | -| Domain | User not found, email taken | 4xx, return specific message | -| Validation | Invalid email format | 400, return field errors | +| Domain | User not found, email taken | 4xx, return specific message | +| Validation | Invalid email format | 400, return field errors | **Implementation**: @@ -306,13 +391,13 @@ class InfrastructureError extends Error { ```typescript switch (result.type) { - case 'USER_NOT_FOUND': + case "USER_NOT_FOUND": // Domain error — tell them what happened - return c.json({ error: 'User not found' }, 404); - case 'INFRASTRUCTURE_ERROR': + return c.json({ error: "User not found" }, 404); + case "INFRASTRUCTURE_ERROR": // Infrastructure — log it, give generic response logger.error(result.error); - return c.json({ error: 'Internal error' }, 500); + return c.json({ error: "Internal error" }, 500); } ``` @@ -324,9 +409,11 @@ switch (result.type) { **Why**: -1. **Colocation** — Everything for a feature is in one folder. No jumping between `/controllers`, `/services`, `/repositories`. +1. **Colocation** — Everything for a feature is in one folder. No jumping + between `/controllers`, `/services`, `/repositories`. -2. **Predictability** — Same structure everywhere. Once you've seen one module, you've seen them all. +2. **Predictability** — Same structure everywhere. Once you've seen one module, + you've seen them all. 3. **Encapsulation** — Modules can be moved, deleted, or extracted to a package. @@ -346,18 +433,19 @@ modules/users/ handlers.test.ts # HTTP integration tests (full stack via app.request) ``` -The `usecases.ts` file is **optional** — add it when business rules exist that are worth testing in isolation. Skip it for pure CRUD modules. +The `usecases.ts` file is **optional** — add it when business rules exist that +are worth testing in isolation. Skip it for pure CRUD modules. **Naming conventions**: -| File | Purpose | -|------|---------| -| `*.errors.ts` | Domain error discriminated union types | -| `*.repository.ts` | Data access — `tryInfra`, `Result`, never throws | -| `*.usecases.ts` | Pure business logic — no DB imports, no async | -| `routes.ts` | `createRoute` definitions + exported route types | -| `handlers.ts` | `AppRouteHandler` implementations | -| `index.ts` | Creates router, wires routes → handlers, default export | +| File | Purpose | +| ----------------- | ------------------------------------------------------- | +| `*.errors.ts` | Domain error discriminated union types | +| `*.repository.ts` | Data access — `tryInfra`, `Result`, never throws | +| `*.usecases.ts` | Pure business logic — no DB imports, no async | +| `routes.ts` | `createRoute` definitions + exported route types | +| `handlers.ts` | `AppRouteHandler` implementations | +| `index.ts` | Creates router, wires routes → handlers, default export | --- @@ -367,35 +455,51 @@ The `usecases.ts` file is **optional** — add it when business rules exist that **Why**: -Repositories and use-cases are module-level functions, not classes. Their dependencies (the `db` connection, `env` config) are imported directly at module scope. +Repositories and use-cases are module-level functions, not classes. Their +dependencies (the `db` connection, `env` config) are imported directly at module +scope. This works because: - The DB is a single Postgres connection pool shared across the process -- Tests use environment-specific config (`.env.test` → different DB URL in CI if needed) +- Tests use environment-specific config (`.env.test` → different DB URL in CI if + needed) - Use-cases are pure functions with no dependencies at all - There is nothing to swap at runtime -Adding a DI container (tsyringe, inversify, etc.) would require decorators, a reflect-metadata polyfill, class-based repositories, and configuration that provides no practical benefit over direct imports at this scale. +Adding a DI container (tsyringe, inversify, etc.) would require decorators, a +reflect-metadata polyfill, class-based repositories, and configuration that +provides no practical benefit over direct imports at this scale. **Trade-offs**: - No runtime swapping of implementations -- Tighter coupling between repository functions and the `db` singleton (acceptable — it's the intended deployment model) +- Tighter coupling between repository functions and the `db` singleton + (acceptable — it's the intended deployment model) --- ## Observability: Wide Events over Logs + Metrics -**Choice**: One structured wide event per request → Axiom via `@axiomhq/pino`, with tail-based sampling. +**Choice**: One structured wide event per request → Axiom via `@axiomhq/pino`, +with tail-based sampling. ### Sources This decision is grounded in two bodies of work: -1. **[loggingsucks.com](https://loggingsucks.com)** by Boris Tane (2024) — a practical synthesis of the wide events pattern with a concrete implementation walkthrough. The article coined the framing we use: "instead of logging what your code is doing, log what happened to this request." +1. **[loggingsucks.com](https://loggingsucks.com)** by Boris Tane (2024) — a + practical synthesis of the wide events pattern with a concrete implementation + walkthrough. The article coined the framing we use: "instead of logging what + your code is doing, log what happened to this request." -2. **Charity Majors** (CTO, Honeycomb) — the originator of the "high-cardinality, high-dimensionality observability" approach. Her key argument: traditional APM and log aggregators are optimized for *writing* (counters, pre-aggregated metrics, plain strings), not *querying*. When something breaks, you don't know in advance which dimensions you'll need to slice on. You need to be able to ask arbitrary questions of your production data. +2. **Charity Majors** (CTO, Honeycomb) — the originator of the + "high-cardinality, high-dimensionality observability" approach. Her key + argument: traditional APM and log aggregators are optimized for _writing_ + (counters, pre-aggregated metrics, plain strings), not _querying_. When + something breaks, you don't know in advance which dimensions you'll need to + slice on. You need to be able to ask arbitrary questions of your production + data. The pattern is also known as the **Canonical Log Line**, popularised by Stripe. @@ -403,7 +507,8 @@ The pattern is also known as the **Canonical Log Line**, popularised by Stripe. ### The problem this solves -Traditional logging against a single checkout request generates ~17 scattered log lines: +Traditional logging against a single checkout request generates ~17 scattered +log lines: ``` [INFO] Request received from 192.168.1.50 @@ -413,9 +518,14 @@ Traditional logging against a single checkout request generates ~17 scattered lo [INFO] Request completed status=200 ``` -These lines cannot be correlated without a trace_id. They don't contain the information you need to answer "why did user X's checkout fail?" — you'd need to grep across them, inferring context from timestamps. At 10,000 requests/second, that's 170,000 log lines/second, most of them saying nothing useful. +These lines cannot be correlated without a trace_id. They don't contain the +information you need to answer "why did user X's checkout fail?" — you'd need to +grep across them, inferring context from timestamps. At 10,000 requests/second, +that's 170,000 log lines/second, most of them saying nothing useful. -The core failure is that **logs are optimised for writing, not querying**. They're written by developers at 9am thinking "this might be useful" — not by someone debugging at 2am. +The core failure is that **logs are optimised for writing, not querying**. +They're written by developers at 9am thinking "this might be useful" — not by +someone debugging at 2am. --- @@ -423,33 +533,52 @@ The core failure is that **logs are optimised for writing, not querying**. They' These principles run through every implementation decision below: -**1. High cardinality** — the ability to filter by any unique value: `user_id`, `request_id`, `trace_id`, `session_id`. Legacy systems (ELK, hosted Datadog with metrics) pre-aggregate data and discard the individual values, making it impossible to answer "was this specific user affected?". +**1. High cardinality** — the ability to filter by any unique value: `user_id`, +`request_id`, `trace_id`, `session_id`. Legacy systems (ELK, hosted Datadog with +metrics) pre-aggregate data and discard the individual values, making it +impossible to answer "was this specific user affected?". -**2. High dimensionality** — many fields per event. A wide event with 40 fields can answer 40 independent questions. A 3-field log line can answer three. +**2. High dimensionality** — many fields per event. A wide event with 40 fields +can answer 40 independent questions. A 3-field log line can answer three. -**3. One event per request, emitted once** — not 17 log lines that require manual correlation. Build the event throughout the request lifecycle, emit in the `finally` block. +**3. One event per request, emitted once** — not 17 log lines that require +manual correlation. Build the event throughout the request lifecycle, emit in +the `finally` block. -**4. Structured events are non-negotiable** — JSON only, always. String-search treats logs as bags of characters. Structured querying treats them as rows in a table. +**4. Structured events are non-negotiable** — JSON only, always. String-search +treats logs as bags of characters. Structured querying treats them as rows in a +table. -**5. Tail-based sampling over head-based** — make the sampling decision *after* the request completes, when you know the outcome. Head-based (random at start) has a 90% chance of dropping the specific error you need at 1% sample rate. Tail-based keeps 100% of errors at any sample rate. +**5. Tail-based sampling over head-based** — make the sampling decision _after_ +the request completes, when you know the outcome. Head-based (random at start) +has a 90% chance of dropping the specific error you need at 1% sample rate. +Tail-based keeps 100% of errors at any sample rate. -**6. OTel is plumbing, not observability** — OpenTelemetry standardises delivery. It does not decide what to capture. You can emit bad telemetry in a standardised format. The mental model shift (emit events not log statements) matters far more than which protocol transports them. +**6. OTel is plumbing, not observability** — OpenTelemetry standardises +delivery. It does not decide what to capture. You can emit bad telemetry in a +standardised format. The mental model shift (emit events not log statements) +matters far more than which protocol transports them. --- ### Why Axiom over ELK / Datadog / Grafana Loki -| Concern | Axiom | ELK | Loki | -|---|---|---|---| -| High-cardinality queries | ✅ ClickHouse-backed | ❌ Elasticsearch chokes | ⚠️ Slow | -| Zero infra to run | ✅ SaaS | ❌ Run your own | ❌ Run your own | -| Structured event querying | ✅ APL (SQL-like) | ⚠️ Lucene syntax | ❌ Label-based only | -| Generous free tier | ✅ 500GB/month | ❌ Self-hosted cost | ⚠️ Hosted cost | -| pino transport available | ✅ `@axiomhq/pino` | ⚠️ filebeat/logstash | ⚠️ loki-logging-plugin | +| Concern | Axiom | ELK | Loki | +| ------------------------- | -------------------- | ----------------------- | ---------------------- | +| High-cardinality queries | ✅ ClickHouse-backed | ❌ Elasticsearch chokes | ⚠️ Slow | +| Zero infra to run | ✅ SaaS | ❌ Run your own | ❌ Run your own | +| Structured event querying | ✅ APL (SQL-like) | ⚠️ Lucene syntax | ❌ Label-based only | +| Generous free tier | ✅ 500GB/month | ❌ Self-hosted cost | ⚠️ Hosted cost | +| pino transport available | ✅ `@axiomhq/pino` | ⚠️ filebeat/logstash | ⚠️ loki-logging-plugin | -Axiom uses ClickHouse under the hood — a columnar database built for high-cardinality, high-dimensionality analytics. This is what Charity Majors' argument points at: the tooling has caught up. The bottleneck is now the mental model, not the storage engine. +Axiom uses ClickHouse under the hood — a columnar database built for +high-cardinality, high-dimensionality analytics. This is what Charity Majors' +argument points at: the tooling has caught up. The bottleneck is now the mental +model, not the storage engine. -When `AXIOM_TOKEN` is not set (local dev, CI), logs go to stdout only. When set, `pino` streams to both stdout and Axiom via a multistream. No sidecar, no agent, no extra infra. +When `AXIOM_TOKEN` is not set (local dev, CI), logs go to stdout only. When set, +`pino` streams to both stdout and Axiom via a multistream. No sidecar, no agent, +no extra infra. --- @@ -461,13 +590,18 @@ OTel is a protocol for exporting telemetry to a collector. Adding it means: - Configuring exporters to Axiom's OTLP endpoint - Writing spans instead of events -For a single-service VPS deployment, this complexity buys nothing. Our `trace_id` field is propagated from the `x-trace-id` request header if set by a gateway, which gives cross-service correlation without a full OTel setup. If we move to microservices, OTel would be the right next step — the `trace_id` field is already in place to hook into it. +For a single-service VPS deployment, this complexity buys nothing. Our +`trace_id` field is propagated from the `x-trace-id` request header if set by a +gateway, which gives cross-service correlation without a full OTel setup. If we +move to microservices, OTel would be the right next step — the `trace_id` field +is already in place to hook into it. --- ### What we built -The `WideEvent` type and `addToEvent` primitive implement the pattern directly from the article's "Implementing Wide Events" section, adapted to Hono: +The `WideEvent` type and `addToEvent` primitive implement the pattern directly +from the article's "Implementing Wide Events" section, adapted to Hono: ``` Request in @@ -479,29 +613,31 @@ Request in **The fields we instrumenting by default** (without any handler-level code): -| Field | Source | Why | -|---|---|---| -| `request_id` | UUID per request | High-cardinality, uniquely identifies this event | -| `trace_id` | `x-trace-id` header or new UUID | Multi-service correlation without OTel | -| `session_id` | better-auth session ID | Separate from user_id — one user, many sessions | -| `deployment_id` | `DEPLOYMENT_ID` env var | "Which deploy caused this regression?" | -| `service_version` | `SERVICE_VERSION` env var (git SHA) | Code version of the running process | -| `user.id` + `user.role` | auth middleware | Who made this request | -| `status_code`, `duration_ms` | response | Outcome and performance | -| `ip`, `user_agent` | request headers | Client context | +| Field | Source | Why | +| ---------------------------- | ----------------------------------- | ------------------------------------------------ | +| `request_id` | UUID per request | High-cardinality, uniquely identifies this event | +| `trace_id` | `x-trace-id` header or new UUID | Multi-service correlation without OTel | +| `session_id` | better-auth session ID | Separate from user_id — one user, many sessions | +| `deployment_id` | `DEPLOYMENT_ID` env var | "Which deploy caused this regression?" | +| `service_version` | `SERVICE_VERSION` env var (git SHA) | Code version of the running process | +| `user.id` + `user.role` | auth middleware | Who made this request | +| `status_code`, `duration_ms` | response | Outcome and performance | +| `ip`, `user_agent` | request headers | Client context | **Tail-based sampling rules** (applied after request completes): -| Condition | Rationale | -|---|---| -| `status_code >= 500` | Infrastructure or application error — never drop | -| `outcome === "error"` | Explicit error outcome — never drop | -| `duration_ms > 2000` | Latency outliers — never drop | -| `user.role === "admin"` | Low-volume, high-signal operational traffic | +| Condition | Rationale | +| ----------------------- | ------------------------------------------------------------- | +| `status_code >= 500` | Infrastructure or application error — never drop | +| `outcome === "error"` | Explicit error outcome — never drop | +| `duration_ms > 2000` | Latency outliers — never drop | +| `user.role === "admin"` | Low-volume, high-signal operational traffic | | `feature_flags` present | Request is part of a feature rollout — critical for debugging | -| Everything else | 5% random sample | +| Everything else | 5% random sample | -The feature_flags rule was added beyond the article's example. During a feature rollout, dropping 95% of flagged requests would make it impossible to analyse the new behaviour. Handlers annotate rollout requests with: +The feature_flags rule was added beyond the article's example. During a feature +rollout, dropping 95% of flagged requests would make it impossible to analyse +the new behaviour. Handlers annotate rollout requests with: ```typescript addToEvent(c, { feature_flags: { new_checkout_flow: true } }); @@ -513,8 +649,20 @@ and those events are always retained. ### What we explicitly left out -**`subscription_tier` / user plan on the `user` field** — the article's canonical example includes `user.subscription: "premium"`. We left it as a `[k: string]: unknown` extension point rather than baking it in: not every app built on this template will have subscription tiers. Add it in `authMiddleware.ts` by extending the `addToEvent` call with your user's plan field once it exists on the user record. - -**`error.code` and `error.retriable`** — typed on `WideEvent.error` as optional fields. The middleware catches unhandled errors and populates `type` and `message` automatically. Handlers should add `code` and `retriable` for business-logic errors where that context is meaningful (e.g. payment failures with Stripe decline codes). - -**Metrics** — no Prometheus, no StatsD, no counters. The same queries you'd run on a metrics dashboard (error rate by endpoint, p99 latency) can be run as APL aggregations on wide events in Axiom. One data store, one query language, no cardinality limit. +**`subscription_tier` / user plan on the `user` field** — the article's +canonical example includes `user.subscription: "premium"`. We left it as a +`[k: string]: unknown` extension point rather than baking it in: not every app +built on this template will have subscription tiers. Add it in +`authMiddleware.ts` by extending the `addToEvent` call with your user's plan +field once it exists on the user record. + +**`error.code` and `error.retriable`** — typed on `WideEvent.error` as optional +fields. The middleware catches unhandled errors and populates `type` and +`message` automatically. Handlers should add `code` and `retriable` for +business-logic errors where that context is meaningful (e.g. payment failures +with Stripe decline codes). + +**Metrics** — no Prometheus, no StatsD, no counters. The same queries you'd run +on a metrics dashboard (error rate by endpoint, p99 latency) can be run as APL +aggregations on wide events in Axiom. One data store, one query language, no +cardinality limit. diff --git a/apps/backend/docs/PATTERNS.md b/apps/backend/docs/PATTERNS.md index 998ae1c..d287246 100644 --- a/apps/backend/docs/PATTERNS.md +++ b/apps/backend/docs/PATTERNS.md @@ -21,9 +21,13 @@ export const posts = pgTable("posts", { id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()), title: text("title").notNull(), content: text("content").notNull(), - authorId: text("author_id").notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(), + authorId: text("author_id").notNull().references(() => users.id, { + onDelete: "cascade", + }), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow() + .notNull(), }); export const insertPostSchema = createInsertSchema(posts); @@ -47,7 +51,11 @@ export * from "./posts"; // Expected, business-rule failures — not bugs. // Each variant carries exactly the data a handler needs. export type PostNotFound = { type: "POST_NOT_FOUND"; lookup: string }; -export type NotPostAuthor = { type: "NOT_POST_AUTHOR"; userId: string; postId: string }; +export type NotPostAuthor = { + type: "NOT_POST_AUTHOR"; + userId: string; + postId: string; +}; export type PostRepoError = PostNotFound | NotPostAuthor; ``` @@ -60,18 +68,19 @@ export type PostRepoError = PostNotFound | NotPostAuthor; import { eq } from "drizzle-orm"; import { db } from "@/db"; import { posts } from "@repo/db/schema"; -import { ok, err } from "@repo/shared"; +import { err, ok } from "@repo/shared"; import type { Result } from "@repo/shared"; import { InfrastructureError } from "@/lib/error"; import { tryInfra } from "@/lib/infra"; -import type { Post, InsertPost } from "@repo/db/schema"; +import type { InsertPost, Post } from "@repo/db/schema"; import type { PostNotFound } from "./posts.errors"; export async function findPostById( id: string, ): Promise> { - const result = await tryInfra(`fetch post ${id}`, () => - db.query.posts.findFirst({ where: eq(posts.id, id) }), + const result = await tryInfra( + `fetch post ${id}`, + () => db.query.posts.findFirst({ where: eq(posts.id, id) }), ); if (!result.ok) return result; if (!result.value) return err({ type: "POST_NOT_FOUND", lookup: id }); @@ -81,11 +90,14 @@ export async function findPostById( export async function createPost( data: InsertPost, ): Promise> { - const result = await tryInfra("create post", () => - db.insert(posts).values(data).returning().then((rows) => rows[0]), + const result = await tryInfra( + "create post", + () => db.insert(posts).values(data).returning().then((rows) => rows[0]), ); if (!result.ok) return result; - if (!result.value) return err(new InfrastructureError("Insert returned no rows")); + if (!result.value) { + return err(new InfrastructureError("Insert returned no rows")); + } return ok(result.value); } @@ -93,8 +105,12 @@ export async function updatePost( id: string, data: Partial, ): Promise> { - const result = await tryInfra(`update post ${id}`, () => - db.update(posts).set(data).where(eq(posts.id, id)).returning().then((rows) => rows[0]), + const result = await tryInfra( + `update post ${id}`, + () => + db.update(posts).set(data).where(eq(posts.id, id)).returning().then(( + rows, + ) => rows[0]), ); if (!result.ok) return result; if (!result.value) return err({ type: "POST_NOT_FOUND", lookup: id }); @@ -104,8 +120,12 @@ export async function updatePost( export async function deletePost( id: string, ): Promise> { - const result = await tryInfra(`delete post ${id}`, () => - db.delete(posts).where(eq(posts.id, id)).returning().then((rows) => rows[0]), + const result = await tryInfra( + `delete post ${id}`, + () => + db.delete(posts).where(eq(posts.id, id)).returning().then((rows) => + rows[0] + ), ); if (!result.ok) return result; if (!result.value) return err({ type: "POST_NOT_FOUND", lookup: id }); @@ -120,15 +140,15 @@ export async function deletePost( ```typescript import { createRoute, z } from "@hono/zod-openapi"; import { selectPostSchema } from "@repo/db/schema"; -import { apiSuccessSchema, apiErrorSchema } from "@repo/shared"; +import { apiErrorSchema, apiSuccessSchema } from "@repo/shared"; import { - jsonRes, + CREATED, + INTERNAL_SERVER_ERROR, jsonBody, + jsonRes, + NOT_FOUND, OK, - CREATED, UNAUTHORIZED, - NOT_FOUND, - INTERNAL_SERVER_ERROR, } from "@/lib/types"; const tags = ["Posts"]; @@ -142,7 +162,9 @@ export const createPost = createRoute({ path: "/posts", tags, request: { - body: jsonBody(z.object({ title: z.string().min(3), content: z.string().min(1) })), + body: jsonBody( + z.object({ title: z.string().min(3), content: z.string().min(1) }), + ), }, responses: { [CREATED]: jsonRes(apiSuccessSchema(selectPostSchema), "Created"), @@ -168,26 +190,31 @@ export type CreatePostRoute = typeof createPost; export type GetPostRoute = typeof getPost; ``` -> **Every status code your handler returns must be declared in `responses`.** `AppRouteHandler` is type-safe against the route definition — returning an undeclared status (including 500) is a compile error. Always declare 500 if the handler calls a repository. +> **Every status code your handler returns must be declared in `responses`.** +> `AppRouteHandler` is type-safe against the route definition — returning an +> undeclared status (including 500) is a compile error. Always declare 500 if +> the handler calls a repository. ### 5. Write Use-Cases (when needed) -Add `posts.usecases.ts` only when business logic exists. These are **pure functions** — no DB imports, no async, no HTTP. +Add `posts.usecases.ts` only when business logic exists. These are **pure +functions** — no DB imports, no async, no HTTP. `apps/backend/src/modules/posts/posts.usecases.ts`: ```typescript -import type { User, Post } from "@repo/db/schema"; +import type { Post, User } from "@repo/db/schema"; import type { NotPostAuthor } from "./posts.errors"; -import { ok, err, type Result } from "@repo/shared"; +import { err, ok, type Result } from "@repo/shared"; // Pure rule: can this user modify this post? export function authorizePostUpdate( user: { id: string }, post: Post, ): Result { - if (post.authorId !== user.id) + if (post.authorId !== user.id) { return err({ type: "NOT_POST_AUTHOR", userId: user.id, postId: post.id }); + } return ok(post); } ``` @@ -202,19 +229,33 @@ For simple CRUD with no rules, skip this file entirely. ```typescript import type { AppRouteHandler } from "@/lib/types"; -import { success, failure, isInfraError, OK, CREATED, NOT_FOUND, INTERNAL_SERVER_ERROR } from "@/lib/types"; +import { + CREATED, + failure, + INTERNAL_SERVER_ERROR, + isInfraError, + NOT_FOUND, + OK, + success, +} from "@/lib/types"; import { match } from "@repo/shared"; import type { CreatePostRoute, GetPostRoute } from "./routes"; import { createPost, findPostById } from "./posts.repository"; -export const createPostHandler: AppRouteHandler = async (c) => { +export const createPostHandler: AppRouteHandler = async ( + c, +) => { const userId = c.get("user").id; const body = c.req.valid("json"); const result = await createPost({ authorId: userId, ...body }); - if (!result.ok) - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + if (!result.ok) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } return c.json(success(result.value), CREATED); }; @@ -227,11 +268,18 @@ export const getPostHandler: AppRouteHandler = async (c) => { return match(result, { ok: (post) => c.json(success(post), OK), err: (e) => { - if (isInfraError(e)) - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } switch (e.type) { case "POST_NOT_FOUND": - return c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND); + return c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ); } }, }); @@ -282,7 +330,7 @@ app.use("/api/*", authMiddleware); ```typescript export const handler: AppRouteHandler = async (c) => { - const user = c.get("user"); // { id, email, name, role } + const user = c.get("user"); // { id, email, name, role } const session = c.get("session"); // { id, userId, expiresAt } }; ``` @@ -303,7 +351,8 @@ app.use("/api/admin/*", requireRole("admin")); ### Ownership Checks -Put ownership rules in a use-case — pure function, no DB. The handler loads the data (imperative shell) and delegates the rule (functional core): +Put ownership rules in a use-case — pure function, no DB. The handler loads the +data (imperative shell) and delegates the rule (functional core): ```typescript // posts.usecases.ts @@ -311,36 +360,57 @@ export function authorizePostUpdate( user: { id: string }, post: Post, ): Result { - if (post.authorId !== user.id) + if (post.authorId !== user.id) { return err({ type: "NOT_POST_AUTHOR", userId: user.id, postId: post.id }); + } return ok(post); } // handlers.ts -export const updatePostHandler: AppRouteHandler = async (c) => { +export const updatePostHandler: AppRouteHandler = async ( + c, +) => { const user = c.get("user"); const { id } = c.req.valid("param"); const body = c.req.valid("json"); const found = await findPostById(id); - if (!found.ok) + if (!found.ok) { return isInfraError(found.error) - ? c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR) - : c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND); + ? c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ) + : c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ); + } const authorized = authorizePostUpdate(user, found.value); - if (!authorized.ok) - return c.json(failure({ code: "FORBIDDEN", message: "Not your post" }), FORBIDDEN); + if (!authorized.ok) { + return c.json( + failure({ code: "FORBIDDEN", message: "Not your post" }), + FORBIDDEN, + ); + } const result = await updatePost(id, body); return match(result, { ok: (post) => c.json(success(post), OK), err: (e) => { - if (isInfraError(e)) - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } switch (e.type) { case "POST_NOT_FOUND": - return c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND); + return c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ); } }, }); @@ -352,31 +422,43 @@ export const updatePostHandler: AppRouteHandler = async (c) => ## Result Helpers ```typescript -import { ok, err, map, andThen, andThenAsync, match } from "@repo/shared"; +import { andThen, andThenAsync, err, map, match, ok } from "@repo/shared"; ``` ### `match` — handle both branches in a handler -`match` handles the ok/err split. When the error union has multiple variants, use `switch` inside the `err` branch — TypeScript will tell you if you miss one: +`match` handles the ok/err split. When the error union has multiple variants, +use `switch` inside the `err` branch — TypeScript will tell you if you miss one: ```typescript const result = await findPostById(id); return match(result, { ok: (post) => c.json(success(post), OK), err: (e) => { - if (isInfraError(e)) - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } switch (e.type) { case "POST_NOT_FOUND": - return c.json(failure({ code: "NOT_FOUND", message: "Post not found" }), NOT_FOUND); + return c.json( + failure({ code: "NOT_FOUND", message: "Post not found" }), + NOT_FOUND, + ); case "NOT_POST_AUTHOR": - return c.json(failure({ code: "FORBIDDEN", message: "Not your post" }), FORBIDDEN); + return c.json( + failure({ code: "FORBIDDEN", message: "Not your post" }), + FORBIDDEN, + ); } }, }); ``` -The ternary shorthand is only appropriate when there is exactly one domain error variant. +The ternary shorthand is only appropriate when there is exactly one domain error +variant. ### `andThenAsync` — chain two async repository calls @@ -421,9 +503,10 @@ export async function listPosts(options: { offset: options.offset, orderBy: (p, { desc }) => desc(p.createdAt), }), - db.select({ count: sql`count(*)` }).from(posts).then((r) => Number(r[0].count)), - ]), - ); + db.select({ count: sql`count(*)` }).from(posts).then((r) => + Number(r[0].count) + ), + ])); if (!result.ok) return result; const [data, total] = result.value; return ok({ data, total }); @@ -438,8 +521,12 @@ export const listPostsHandler: AppRouteHandler = async (c) => { const offset = (page - 1) * limit; const result = await listPosts({ limit, offset }); - if (!result.ok) - return c.json(failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), INTERNAL_SERVER_ERROR); + if (!result.ok) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } const { data, total } = result.value; return c.json({ @@ -458,16 +545,25 @@ Three tiers. No mocks unless there is genuinely no alternative. ### 1. Unit Testing Use-Cases -Use-cases are pure functions — no DB, no HTTP, no mocks needed. Just call them with plain values. +Use-cases are pure functions — no DB, no HTTP, no mocks needed. Just call them +with plain values. File: `modules/posts/__tests__/posts.usecases.test.ts` ```typescript -import { describe, it, expect } from "vitest"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import { authorizePostUpdate } from "../posts.usecases"; describe("authorizePostUpdate", () => { - const post = { id: "p1", authorId: "u1", title: "hi", content: "...", createdAt: new Date(), updatedAt: new Date() }; + const post = { + id: "p1", + authorId: "u1", + title: "hi", + content: "...", + createdAt: new Date(), + updatedAt: new Date(), + }; it("returns ok when user owns the post", () => { const result = authorizePostUpdate({ id: "u1" }, post); @@ -476,7 +572,10 @@ describe("authorizePostUpdate", () => { it("returns NOT_POST_AUTHOR when user does not own the post", () => { const result = authorizePostUpdate({ id: "u2" }, post); - expect(result).toEqual({ ok: false, error: { type: "NOT_POST_AUTHOR", userId: "u2", postId: "p1" } }); + expect(result).toEqual({ + ok: false, + error: { type: "NOT_POST_AUTHOR", userId: "u2", postId: "p1" }, + }); }); }); ``` @@ -485,12 +584,14 @@ Every business rule lives in a use-case. Every use-case is testable this way. ### 2. Integration Testing Repositories -Repositories talk directly to the DB — test them against a real test database, not mocks. Run a local Postgres instance (or Docker) configured via `.env.test`. +Repositories talk directly to the DB — test them against a real test database, +not mocks. Run a local Postgres instance (or Docker) configured via `.env.test`. File: `modules/posts/__tests__/posts.repository.test.ts` ```typescript -import { describe, it, expect, afterEach } from "vitest"; +import { afterEach, describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import { createPost, findPostById } from "../posts.repository"; import { db } from "@/db"; import { schema } from "@/db"; @@ -504,11 +605,18 @@ afterEach(async () => { describe("findPostById", () => { it("returns POST_NOT_FOUND when no row exists", async () => { const result = await findPostById("nonexistent"); - expect(result).toEqual({ ok: false, error: { type: "POST_NOT_FOUND", lookup: "nonexistent" } }); + expect(result).toEqual({ + ok: false, + error: { type: "POST_NOT_FOUND", lookup: "nonexistent" }, + }); }); it("returns the post when it exists", async () => { - const created = await createPost({ authorId: "u1", title: "hello", content: "world" }); + const created = await createPost({ + authorId: "u1", + title: "hello", + content: "world", + }); expect(created.ok).toBe(true); if (!created.ok) return; @@ -518,16 +626,19 @@ describe("findPostById", () => { }); ``` -This tests the actual SQL queries, actual constraint errors, and actual `tryInfra` boundary behaviour. +This tests the actual SQL queries, actual constraint errors, and actual +`tryInfra` boundary behaviour. ### 3. Integration Testing Handlers -Test full HTTP flows against a real DB using `app.request()`. Auth is handled by signing up through the app — no mocks, no fake sessions. +Test full HTTP flows against a real DB using `app.request()`. Auth is handled by +signing up through the app — no mocks, no fake sessions. File: `modules/posts/__tests__/handlers.test.ts` ```typescript -import { describe, it, expect, afterEach } from "vitest"; +import { afterEach, describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import app from "@/app"; import { db } from "@/db"; import { schema } from "@/db"; @@ -544,7 +655,7 @@ async function signUp(email = "test@example.com", password = "password-123") { afterEach(async () => { await db.delete(schema.verifications); // no FK cascade - await db.delete(schema.users); // cascades sessions + accounts + await db.delete(schema.users); // cascades sessions + accounts }); describe("GET /api/posts/:id", () => { @@ -581,6 +692,7 @@ describe("GET /api/posts/:id", () => { ### When Mocks Are Acceptable Only when the real thing cannot run in a test environment: + - External email/SMS providers (mock the transport, not the business logic) - Third-party payment APIs (Stripe, etc.) - External webhooks or OAuth flows @@ -591,8 +703,9 @@ Do **not** mock the DB, Redis, or any infrastructure you can run locally. ## Observability — Enriching Wide Events -Every request emits **one** structured JSON event (a "wide event" / canonical log line). -`wideEventMiddleware` populates the base fields automatically; handlers add domain context. +Every request emits **one** structured JSON event (a "wide event" / canonical +log line). `wideEventMiddleware` populates the base fields automatically; +handlers add domain context. ### Adding fields from a handler @@ -641,16 +754,16 @@ The wide event emitted at the end of the request includes every field added via ### Sampling rules (applied before emit) -| Condition | Sampled? | -|---|---| -| `status_code >= 500` | Always | -| `outcome === "error"` | Always | -| `duration_ms > 2000` | Always (slow requests) | -| User role `admin` | Always | -| Everything else | 5% random | +| Condition | Sampled? | +| --------------------- | ---------------------- | +| `status_code >= 500` | Always | +| `outcome === "error"` | Always | +| `duration_ms > 2000` | Always (slow requests) | +| User role `admin` | Always | +| Everything else | 5% random | -Events that are dropped are never written to Axiom, so you stay within the free tier -for normal traffic while retaining 100% of interesting signals. +Events that are dropped are never written to Axiom, so you stay within the free +tier for normal traffic while retaining 100% of interesting signals. ### Axiom setup @@ -663,4 +776,5 @@ for normal traffic while retaining 100% of interesting signals. SERVICE_VERSION=$(git rev-parse --short HEAD) REGION=eu-central-1 ``` -4. No extra infrastructure — `@axiomhq/pino` streams directly from the process over HTTPS. +4. No extra infrastructure — `@axiomhq/pino` streams directly from the process + over HTTPS. diff --git a/apps/backend/docs/REFERENCES.md b/apps/backend/docs/REFERENCES.md index 6bec1b0..c8e48b8 100644 --- a/apps/backend/docs/REFERENCES.md +++ b/apps/backend/docs/REFERENCES.md @@ -1,83 +1,102 @@ # References & Further Reading -The talks, articles, and specifications that informed the decisions in this codebase. Each section maps to a decision in [DECISIONS.md](./DECISIONS.md). +The talks, articles, and specifications that informed the decisions in this +codebase. Each section maps to a decision in [DECISIONS.md](./DECISIONS.md). --- ## Functional Core / Imperative Shell -*Decisions: use-case layer, handlers as orchestrators, pure functions for business logic.* +_Decisions: use-case layer, handlers as orchestrators, pure functions for +business logic._ -**Gary Bernhardt — "Boundaries"** (Destroy All Software, 2012) -The original articulation of this pattern. A 30-minute screencast explaining why mixing pure logic with I/O produces code that is hard to test and hard to reason about. Every use-case in this codebase exists because of this talk. +**Gary Bernhardt — "Boundaries"** (Destroy All Software, 2012) The original +articulation of this pattern. A 30-minute screencast explaining why mixing pure +logic with I/O produces code that is hard to test and hard to reason about. +Every use-case in this codebase exists because of this talk. https://www.destroyallsoftware.com/talks/boundaries -**Mark Seemann — "Impureim Sandwich"** (2020) -A concise restatement of the same idea: order your code as impure → pure → impure. Handlers are the bread; use-cases are the filling. -https://blog.ploeh.dk/2020/03/02/impureim-sandwich/ +**Mark Seemann — "Impureim Sandwich"** (2020) A concise restatement of the same +idea: order your code as impure → pure → impure. Handlers are the bread; +use-cases are the filling. https://blog.ploeh.dk/2020/03/02/impureim-sandwich/ --- ## Result Types / Railway Oriented Programming -*Decisions: never throw, `Result`, `ok`/`err`/`match`/`andThen`/`map` combinators.* +_Decisions: never throw, `Result`, `ok`/`err`/`match`/`andThen`/`map` +combinators._ -**Scott Wlaschin — "Railway Oriented Programming"** (NDC Oslo, 2014) -The talk that popularised chaining functions that carry a success/failure track. Direct inspiration for the combinator vocabulary in `@repo/shared`. The article version is a good reference to keep open while reading this codebase. -https://fsharpforfun.com/posts/recipe-part2.html -Talk: https://www.youtube.com/watch?v=fYo3LN9Vf_M +**Scott Wlaschin — "Railway Oriented Programming"** (NDC Oslo, 2014) The talk +that popularised chaining functions that carry a success/failure track. Direct +inspiration for the combinator vocabulary in `@repo/shared`. The article version +is a good reference to keep open while reading this codebase. +https://fsharpforfun.com/posts/recipe-part2.html Talk: +https://www.youtube.com/watch?v=fYo3LN9Vf_M -**Rust `std::result::Result`** -Language-level proof that Result types work at scale. Much of the combinator naming (`map`, `and_then`, `unwrap_or`) comes from Rust's standard library. -https://doc.rust-lang.org/std/result/ +**Rust `std::result::Result`** Language-level proof that Result types work at +scale. Much of the combinator naming (`map`, `and_then`, `unwrap_or`) comes from +Rust's standard library. https://doc.rust-lang.org/std/result/ --- ## Parse, Don't Validate -*Decisions: Zod at the HTTP boundary, `c.req.valid("json")` returns a fully-typed value — no further runtime checks inside the application.* +_Decisions: Zod at the HTTP boundary, `c.req.valid("json")` returns a +fully-typed value — no further runtime checks inside the application._ -**Alexis King — "Parse, Don't Validate"** (2019) -Argues that validation produces a boolean and throws away information; parsing produces a richer type and cannot be bypassed by accident. The core reason we validate *once* at the route boundary and trust the types downstream. +**Alexis King — "Parse, Don't Validate"** (2019) Argues that validation produces +a boolean and throws away information; parsing produces a richer type and cannot +be bypassed by accident. The core reason we validate _once_ at the route +boundary and trust the types downstream. https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ --- ## Making Impossible States Impossible -*Decisions: domain errors are discriminated unions with typed payloads, not generic `Error` subclasses or string codes.* +_Decisions: domain errors are discriminated unions with typed payloads, not +generic `Error` subclasses or string codes._ **Richard Feldman — "Making Impossible States Impossible"** (Elm Europe, 2016) -Shows how to design types so that invalid program states literally cannot be constructed. The pattern behind `type UserError = UserNotFound | EmailTaken` — each variant typed, each distinct, each handling exactly the data its handler needs. -https://www.youtube.com/watch?v=IcgmSRJHu_8 +Shows how to design types so that invalid program states literally cannot be +constructed. The pattern behind `type UserError = UserNotFound | EmailTaken` — +each variant typed, each distinct, each handling exactly the data its handler +needs. https://www.youtube.com/watch?v=IcgmSRJHu_8 --- ## Integration Testing Over Mock-Heavy Unit Tests -*Decisions: repositories tested against a real database, no mocking infrastructure, mocks only for third-party services that cannot run locally.* +_Decisions: repositories tested against a real database, no mocking +infrastructure, mocks only for third-party services that cannot run locally._ -**J.B. Rainsberger — "Integrated Tests Are a Scam"** (continuously updated since 2010) -Not an argument against integration tests — an argument against the belief that mocking your infrastructure gives you real confidence. Required reading before reaching for `vi.mock` on a database call. +**J.B. Rainsberger — "Integrated Tests Are a Scam"** (continuously updated +since 2010) Not an argument against integration tests — an argument against the +belief that mocking your infrastructure gives you real confidence. Required +reading before reaching for `vi.mock` on a database call. https://blog.thecodewhisperer.com/permalink/integrated-tests-are-a-scam -**Martin Fowler — "Test Double"** -The canonical taxonomy: stubs, mocks, fakes, spies. Useful for understanding *what* to mock (external HTTP APIs, payment providers, email transports) versus *what not to* (Postgres, Redis, infrastructure you can run in Docker). +**Martin Fowler — "Test Double"** The canonical taxonomy: stubs, mocks, fakes, +spies. Useful for understanding _what_ to mock (external HTTP APIs, payment +providers, email transports) versus _what not to_ (Postgres, Redis, +infrastructure you can run in Docker). https://martinfowler.com/bliki/TestDouble.html --- ## OpenAPI-First Design -*Decisions: `createRoute()` as single source of truth for validation, types, and documentation.* +_Decisions: `createRoute()` as single source of truth for validation, types, and +documentation._ -**OpenAPI Specification 3.1** -The specification every `createRoute()` call maps to. Worth reading the Paths and Components sections if you're extending the API. +**OpenAPI Specification 3.1** The specification every `createRoute()` call maps +to. Worth reading the Paths and Components sections if you're extending the API. https://spec.openapis.org/oas/v3.1.0 -**Zod documentation** -Zod schemas serve double duty throughout this codebase: runtime validation at the boundary and static TypeScript type inference everywhere downstream. -https://zod.dev +**Zod documentation** Zod schemas serve double duty throughout this codebase: +runtime validation at the boundary and static TypeScript type inference +everywhere downstream. https://zod.dev --- @@ -89,47 +108,64 @@ https://hono.dev **Drizzle ORM documentation** — schema declaration, query builder, migrations https://orm.drizzle.team -**better-auth documentation** — session management, auth flows, email verification -https://www.better-auth.com +**better-auth documentation** — session management, auth flows, email +verification https://www.better-auth.com --- ## Observability -*Decisions: wide events over scattered logs + metrics, tail-based sampling, Axiom, `addToEvent` as the instrumentation primitive. See the full rationale in [DECISIONS.md → Observability](./DECISIONS.md).* - -**Boris Tane — "Logging Sucks"** (loggingsucks.com, 2024) -The most direct reference for our implementation. Walks through the core problem (17 log lines per request that tell you nothing), defines the vocabulary (cardinality, dimensionality, wide events, canonical log lines), shows the implementation pattern step by step, and covers tail-based sampling. The `wideEventMiddleware` and `addToEvent` pattern in this codebase follows this implementation guide directly. -https://loggingsucks.com - -**Charity Majors — Blog and talks (Honeycomb)** -The originator of the "high-cardinality observability" argument. Her central claim: traditional logging and APM are broken because they force you to pre-aggregate before storage, discarding the individual request data you need to debug production. You need to store raw events in a column-oriented store and query them at debug time — not grep them. -- "Observability — The 5-Year Retrospective": https://charity.wtf/2020/03/03/observability-is-a-many-splendored-thing/ +_Decisions: wide events over scattered logs + metrics, tail-based sampling, +Axiom, `addToEvent` as the instrumentation primitive. See the full rationale in +[DECISIONS.md → Observability](./DECISIONS.md)._ + +**Boris Tane — "Logging Sucks"** (loggingsucks.com, 2024) The most direct +reference for our implementation. Walks through the core problem (17 log lines +per request that tell you nothing), defines the vocabulary (cardinality, +dimensionality, wide events, canonical log lines), shows the implementation +pattern step by step, and covers tail-based sampling. The `wideEventMiddleware` +and `addToEvent` pattern in this codebase follows this implementation guide +directly. https://loggingsucks.com + +**Charity Majors — Blog and talks (Honeycomb)** The originator of the +"high-cardinality observability" argument. Her central claim: traditional +logging and APM are broken because they force you to pre-aggregate before +storage, discarding the individual request data you need to debug production. +You need to store raw events in a column-oriented store and query them at debug +time — not grep them. + +- "Observability — The 5-Year Retrospective": + https://charity.wtf/2020/03/03/observability-is-a-many-splendored-thing/ - "Is This Just Metrics?": https://charity.wtf/2022/08/12/is-this-just-metrics/ -- "High Cardinality is Not the Same As High Dimensionality": https://charity.wtf/2021/08/09/notes-on-the-art-of-measuring-things/ +- "High Cardinality is Not the Same As High Dimensionality": + https://charity.wtf/2021/08/09/notes-on-the-art-of-measuring-things/ -**Stripe Engineering — "Canonical Log Lines"** -The origin of the term. Stripe's approach: each service emits one structured log line per request containing all the context needed to understand what happened. This is the "canonical log line" that `wideEventMiddleware` implements. +**Stripe Engineering — "Canonical Log Lines"** The origin of the term. Stripe's +approach: each service emits one structured log line per request containing all +the context needed to understand what happened. This is the "canonical log line" +that `wideEventMiddleware` implements. https://stripe.com/blog/canonical-log-lines -**Honeycomb documentation — "Core Analysis Loop"** -The mental model for debugging with events: start with a wide query (error rate), narrow by cardinality (which user_id?), expand to see the full event context. This is the loop Axiom's APL enables on our wide events. +**Honeycomb documentation — "Core Analysis Loop"** The mental model for +debugging with events: start with a wide query (error rate), narrow by +cardinality (which user_id?), expand to see the full event context. This is the +loop Axiom's APL enables on our wide events. https://docs.honeycomb.io/investigate-incidents-faster/ --- ## Summary Table -| Resource | Author | Format | Why it matters here | -|---|---|---|---| -| "Boundaries" | Gary Bernhardt | Talk (30 min) | Functional core / imperative shell | -| "Impureim Sandwich" | Mark Seemann | Article | Practical ordering of pure and impure code | -| "Railway Oriented Programming" | Scott Wlaschin | Talk + Article | Result types, combinator design | -| "Parse, Don't Validate" | Alexis King | Article | Zod at the boundary, trust types downstream | -| "Making Impossible States Impossible" | Richard Feldman | Talk (25 min) | Discriminated union error design | -| "Integrated Tests Are a Scam" | J.B. Rainsberger | Article | Real DB over mocks | -| "Test Double" | Martin Fowler | Article | When mocks are actually appropriate | -| *A Philosophy of Software Design* | John Ousterhout | Book | Deep modules, reducing complexity | -| "Logging Sucks" | Boris Tane | Article | Wide events implementation pattern | -| "Observability Requires Rethinking Logging" | Charity Majors | Articles/Talks | High cardinality/dimensionality philosophy | -| "Is This Just Metrics?" | Charity Majors | Article | Why metrics are not a substitute for events | +| Resource | Author | Format | Why it matters here | +| ------------------------------------------- | ---------------- | -------------- | ------------------------------------------- | +| "Boundaries" | Gary Bernhardt | Talk (30 min) | Functional core / imperative shell | +| "Impureim Sandwich" | Mark Seemann | Article | Practical ordering of pure and impure code | +| "Railway Oriented Programming" | Scott Wlaschin | Talk + Article | Result types, combinator design | +| "Parse, Don't Validate" | Alexis King | Article | Zod at the boundary, trust types downstream | +| "Making Impossible States Impossible" | Richard Feldman | Talk (25 min) | Discriminated union error design | +| "Integrated Tests Are a Scam" | J.B. Rainsberger | Article | Real DB over mocks | +| "Test Double" | Martin Fowler | Article | When mocks are actually appropriate | +| _A Philosophy of Software Design_ | John Ousterhout | Book | Deep modules, reducing complexity | +| "Logging Sucks" | Boris Tane | Article | Wide events implementation pattern | +| "Observability Requires Rethinking Logging" | Charity Majors | Articles/Talks | High cardinality/dimensionality philosophy | +| "Is This Just Metrics?" | Charity Majors | Article | Why metrics are not a substitute for events | diff --git a/apps/backend/drizzle.config.ts b/apps/backend/drizzle.config.ts index 9f3e39c..f2b5095 100644 --- a/apps/backend/drizzle.config.ts +++ b/apps/backend/drizzle.config.ts @@ -1,14 +1,13 @@ -import "dotenv/config"; import { defineConfig } from "drizzle-kit"; -import env from "./src/env.js"; + +const DATABASE_URL = Deno.env.get("DATABASE_URL"); +if (!DATABASE_URL) throw new Error("DATABASE_URL is required"); export default defineConfig({ - schema: "../../packages/db/src/schema/*.ts", - out: "../../packages/db/migrations", - dialect: "postgresql", - dbCredentials: { - url: env.DATABASE_URL, - }, - verbose: true, - strict: true, + schema: "../../packages/db/src/schema/*.ts", + out: "../../packages/db/migrations", + dialect: "postgresql", + dbCredentials: { url: DATABASE_URL }, + verbose: true, + strict: true, }); diff --git a/apps/backend/package.json b/apps/backend/package.json index 41cddac..e3ed8aa 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -1,65 +1,5 @@ { - "name": "backend", - "version": "1.0.0", - "license": "MIT", - "type": "module", - "scripts": { - "dev": "tsx watch src/index.ts", - "start": "node ./dist/src/index.js", - "typecheck": "tsc --noEmit", - "lint": "biome check .", - "lint:fix": "biome check --fix .", - "test": "cross-env NODE_ENV=test vitest run", - "build": "swc src --out-dir dist --config-file .swcrc && tsc-alias && cp package.json dist/", - "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit push", - "db:studio": "drizzle-kit studio", - "jobs": "tsx src/jobs/worker.ts" - }, - "dependencies": { - "@asteasolutions/zod-to-openapi": "8.0.0", - "@aws-sdk/client-s3": "^3.750.0", - "@aws-sdk/s3-request-presigner": "^3.750.0", - "@axiomhq/pino": "^1.4.0", - "@hono/node-server": "^1.19.7", - "@hono/node-ws": "^1.2.0", - "@hono/swagger-ui": "^0.5.3", - "@hono/zod-openapi": "^1.2.0", - "@hono/zod-validator": "^0.7.6", - "@repo/db": "workspace:*", - "@repo/email-templates": "workspace:*", - "@repo/shared": "workspace:*", - "@scalar/hono-api-reference": "^0.9.30", - "better-auth": "^1.4.9", - "bullmq": "^5.34.0", - "dotenv": "^16.6.1", - "dotenv-expand": "^12.0.3", - "drizzle-orm": "^0.44.7", - "drizzle-zod": "0.8.3", - "hono": "^4.11.3", - "hono-pino": "^0.7.2", - "ioredis": "^5.4.1", - "neverthrow": "^8.2.0", - "pg": "^8.16.3", - "pino": "^9.14.0", - "pino-pretty": "^13.1.3", - "postgres": "^3.4.7", - "resend": "^6.6.0", - "stoker": "2.0.1", - "zod": "^4.2.1" - }, - "devDependencies": { - "@biomejs/biome": "2.3.7", - "@swc/cli": "^0.7.9", - "@swc/core": "^1.15.8", - "@types/node": "^22.19.3", - "@types/pg": "^8.16.0", - "@vitest/ui": "^4.0.16", - "cross-env": "^7.0.3", - "drizzle-kit": "^0.31.8", - "tsc-alias": "^1.8.16", - "tsx": "^4.21.0", - "typescript": "^5.9.3", - "vitest": "^4.0.16" - } + "name": "backend", + "type": "module", + "private": true } diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts index 723b83d..9657299 100644 --- a/apps/backend/src/app.ts +++ b/apps/backend/src/app.ts @@ -6,14 +6,14 @@ import configureOpenAPI from "@/lib/configure-open-api"; import createApp from "@/lib/create-app"; import { authMiddleware } from "@/middlewares/auth"; import { wideEventMiddleware } from "@/middlewares/wide-event"; -import { publicRoutes, routes } from "@/routes/index"; +import { publicRoutes, routes } from "@/routes/index.ts"; -const app = createApp(); +const app = await createApp(); const allowedOrigins = new Set([ - "http://localhost:5173", - "http://localhost:5174", - env.FRONTEND_URL, + "http://localhost:5173", + "http://localhost:5174", + env.FRONTEND_URL, ]); // Wide event middleware — must be first so it wraps the entire request lifecycle @@ -21,14 +21,14 @@ app.use("*", wideEventMiddleware); // CORS configuration app.use( - "*", - cors({ - origin: [...allowedOrigins], - allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"], - allowHeaders: ["Content-Type", "Authorization"], - exposeHeaders: ["set-auth-token"], - credentials: true, - }), + "*", + cors({ + origin: [...allowedOrigins], + allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"], + allowHeaders: ["Content-Type", "Authorization"], + exposeHeaders: ["set-auth-token"], + credentials: true, + }), ); // OpenAPI documentation @@ -39,7 +39,7 @@ app.on(["POST", "GET"], "/api/auth/*", (c) => auth.handler(c.req.raw)); // Public routes (no auth required) for (const route of publicRoutes) { - app.route("/api", route); + app.route("/api", route); } // Apply auth middleware to all /api/* routes @@ -47,12 +47,12 @@ app.use("/api/*", authMiddleware); // Protected routes for (const route of routes) { - app.route("/api", route); + app.route("/api", route); } // Show routes in development if (env.NODE_ENV === "development") { - showRoutes(app); + showRoutes(app); } export type AppType = (typeof routes)[number]; diff --git a/apps/backend/src/db/migrate.ts b/apps/backend/src/db/migrate.ts index 43e0415..62f6089 100644 --- a/apps/backend/src/db/migrate.ts +++ b/apps/backend/src/db/migrate.ts @@ -1,31 +1,30 @@ -import path from "node:path"; import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; -const DATABASE_URL = process.env.DATABASE_URL; +const DATABASE_URL = Deno.env.get("DATABASE_URL"); if (!DATABASE_URL) { - console.error("Error: DATABASE_URL environment variable is required."); - process.exit(1); + console.error("Error: DATABASE_URL environment variable is required."); + Deno.exit(1); } -// In the Docker image the runner WORKDIR is /app and migrations are copied to /app/migrations. -// Locally, process.cwd() is the workspace root where packages/db/migrations lives — but the -// recommended approach for local dev is `pnpm db:migrate` (drizzle-kit push). -const migrationsFolder = path.join(process.cwd(), "migrations"); +const migrationsFolder = new URL( + "../../../../packages/db/migrations", + import.meta.url, +).pathname; const client = postgres(DATABASE_URL, { max: 1 }); const db = drizzle(client); async function main() { - console.log(`Running migrations from: ${migrationsFolder}`); - await migrate(db, { migrationsFolder }); - console.log("Migrations complete."); + console.log(`Running migrations from: ${migrationsFolder}`); + await migrate(db, { migrationsFolder }); + console.log("Migrations complete."); } main() - .catch((err) => { - console.error("Migration failed:", err); - process.exit(1); - }) - .finally(() => client.end()); + .catch((err) => { + console.error("Migration failed:", err); + Deno.exit(1); + }) + .finally(() => client.end()); diff --git a/apps/backend/src/env.ts b/apps/backend/src/env.ts index 016bb3a..8031926 100644 --- a/apps/backend/src/env.ts +++ b/apps/backend/src/env.ts @@ -3,69 +3,69 @@ import { expand } from "dotenv-expand"; import { z } from "zod"; // Load environment-specific .env file first (e.g. .env.test), then fall back to .env. -const nodeEnv = process.env.NODE_ENV ?? "development"; +const nodeEnv = Deno.env.get("NODE_ENV") ?? "development"; expand(config({ path: `.env.${nodeEnv}`, override: false })); expand(config({ override: false })); const envSchema = z.object({ - NODE_ENV: z - .enum(["development", "staging", "production", "test"]) - .default("development"), - PORT: z.coerce.number().default(9999), - LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"), + NODE_ENV: z + .enum(["development", "staging", "production", "test"]) + .default("development"), + PORT: z.coerce.number().default(9999), + LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"), - // Database - DATABASE_URL: z.string().min(1, "DATABASE_URL is required"), + // Database + DATABASE_URL: z.string().min(1, "DATABASE_URL is required"), - // Auth - BETTER_AUTH_SECRET: z - .string() - .min(32, "BETTER_AUTH_SECRET must be at least 32 characters"), - BETTER_AUTH_URL: z.string().url().default("http://localhost:9999"), + // Auth + BETTER_AUTH_SECRET: z + .string() + .min(32, "BETTER_AUTH_SECRET must be at least 32 characters"), + BETTER_AUTH_URL: z.url().default("http://localhost:9999"), - // URLs - SERVER_URL: z.string().url().default("http://localhost:9999"), - FRONTEND_URL: z.string().url().default("http://localhost:5173"), + // URLs + SERVER_URL: z.url().default("http://localhost:9999"), + FRONTEND_URL: z.url().default("http://localhost:5173"), - // Redis (optional - for jobs, rate limiting, caching) - REDIS_URL: z.string().optional(), + // Redis (optional - for jobs, rate limiting, caching) + REDIS_URL: z.string().optional(), - // S3/R2 Storage (optional) - S3_ENDPOINT: z.string().optional(), - S3_REGION: z.string().optional(), - S3_BUCKET: z.string().optional(), - S3_ACCESS_KEY_ID: z.string().optional(), - S3_SECRET_ACCESS_KEY: z.string().optional(), + // S3/R2 Storage (optional) + S3_ENDPOINT: z.string().optional(), + S3_REGION: z.string().optional(), + S3_BUCKET: z.string().optional(), + S3_ACCESS_KEY_ID: z.string().optional(), + S3_SECRET_ACCESS_KEY: z.string().optional(), - // Social OAuth (optional — only activate providers whose vars are set) - GOOGLE_CLIENT_ID: z.string().optional(), - GOOGLE_CLIENT_SECRET: z.string().optional(), - GITHUB_CLIENT_ID: z.string().optional(), - GITHUB_CLIENT_SECRET: z.string().optional(), + // Social OAuth (optional — only activate providers whose vars are set) + GOOGLE_CLIENT_ID: z.string().optional(), + GOOGLE_CLIENT_SECRET: z.string().optional(), + GITHUB_CLIENT_ID: z.string().optional(), + GITHUB_CLIENT_SECRET: z.string().optional(), - // Email - RESEND_API_KEY: z.string().optional(), + // Email + RESEND_API_KEY: z.string().optional(), - // Observability (Axiom) - AXIOM_TOKEN: z.string().optional(), - AXIOM_DATASET: z.string().optional(), + // Observability (Axiom) + AXIOM_TOKEN: z.string().optional(), + AXIOM_DATASET: z.string().optional(), - // Deployment metadata (injected by CI as git SHA / docker image tag / region) - SERVICE_VERSION: z.string().default("local"), - DEPLOYMENT_ID: z.string().default("local"), - REGION: z.string().default("local"), + // Deployment metadata (injected by CI as git SHA / docker image tag / region) + SERVICE_VERSION: z.string().default("local"), + DEPLOYMENT_ID: z.string().default("local"), + REGION: z.string().default("local"), }); export type Env = z.infer; function parseEnv(): Env { - const parsed = envSchema.safeParse(process.env); - if (!parsed.success) { - console.error("❌ Invalid environment variables:"); - console.error(parsed.error.flatten().fieldErrors); - process.exit(1); - } - return parsed.data; + const parsed = envSchema.safeParse(Deno.env.toObject()); + if (!parsed.success) { + console.error("Invalid environment variables:"); + console.error(parsed.error.flatten().fieldErrors); + Deno.exit(1); + } + return parsed.data; } const env = parseEnv(); diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts index c3e7577..9f09020 100644 --- a/apps/backend/src/index.ts +++ b/apps/backend/src/index.ts @@ -1,23 +1,23 @@ -import { serve } from "@hono/node-server"; import app from "@/app"; import env from "@/env"; -const server = serve({ - fetch: app.fetch, - port: env.PORT, -}); - console.log(`Server running at http://localhost:${env.PORT}`); -console.log(`📚 API docs at http://localhost:${env.PORT}/docs`); +console.log(`API docs at http://localhost:${env.PORT}/docs`); + +const abortController = new AbortController(); + +Deno.serve( + { port: env.PORT, signal: abortController.signal }, + (req) => app.fetch(req), +); // Graceful shutdown const shutdown = () => { - console.log("\n Shutting down gracefully..."); - server.close(() => { - console.log("✅ Server closed"); - process.exit(0); - }); + console.log("\n Shutting down gracefully..."); + abortController.abort(); + console.log("✅ Server closed"); + Deno.exit(0); }; -process.on("SIGTERM", shutdown); -process.on("SIGINT", shutdown); +Deno.addSignalListener("SIGTERM", shutdown); +Deno.addSignalListener("SIGINT", shutdown); diff --git a/apps/backend/src/jobs/index.ts b/apps/backend/src/jobs/index.ts index 113cde4..84a1cea 100644 --- a/apps/backend/src/jobs/index.ts +++ b/apps/backend/src/jobs/index.ts @@ -1,62 +1,64 @@ import { Queue } from "bullmq"; +import { passwordResetEmail, welcomeEmail } from "@repo/email-templates"; import { getRedis } from "@/lib/redis"; -// Define your job types -export type JobName = "email" | "cleanup" | "sync"; +export type EmailTemplateName = "welcome" | "passwordReset"; + +export type JobName = "email"; export interface JobData { - email: { to: string; template: string; data: Record }; - cleanup: { olderThanDays: number }; - sync: { userId: string }; + email: { + to: string; + template: EmailTemplateName; + props: { name: string; actionUrl?: string }; + }; } +// Shared template lookup — used by the worker to process queued jobs and by +// queueEmail's no-Redis fallback to send inline, so both paths build the same email. +export const emailTemplates: Record< + EmailTemplateName, + ( + props: { name: string; actionUrl?: string }, + ) => ReturnType +> = { + welcome: welcomeEmail, + passwordReset: passwordResetEmail, +}; + // Create queues function createQueue(name: T) { - // biome-ignore lint/suspicious/noExplicitAny: BullMQ accepts ioredis instances but types diverge - return new Queue(name, { connection: getRedis() as any }); + // biome-ignore lint/suspicious/noExplicitAny: BullMQ accepts ioredis instances but types diverge + return new Queue(name, { connection: getRedis() as any }); } // Export queues (lazy initialization) let emailQueue: Queue | null = null; -let cleanupQueue: Queue | null = null; -let syncQueue: Queue | null = null; export function getEmailQueue(): Queue { - if (!emailQueue) emailQueue = createQueue("email"); - return emailQueue; -} - -export function getCleanupQueue(): Queue { - if (!cleanupQueue) cleanupQueue = createQueue("cleanup"); - return cleanupQueue; -} - -export function getSyncQueue(): Queue { - if (!syncQueue) syncQueue = createQueue("sync"); - return syncQueue; + if (!emailQueue) emailQueue = createQueue("email"); + return emailQueue; } // Helper to add jobs -export async function addJob( - name: T, - data: JobData[T], - options?: { delay?: number; priority?: number }, +export function addJob( + name: T, + data: JobData[T], + options?: { delay?: number; priority?: number }, ) { - const queueMap = { - email: getEmailQueue(), - cleanup: getCleanupQueue(), - sync: getSyncQueue(), - }; - - const queue = queueMap[name]; - - // Queue collapses all generics so .add() accepts (string, data) without ExtractNameType inference errors - // biome-ignore lint/suspicious/noExplicitAny: BullMQ ExtractNameType bug (github.com/taskforcesh/bullmq/issues/3369) - const q = queue as unknown as Queue; - return q.add(name, data, { - delay: options?.delay, - priority: options?.priority, - removeOnComplete: 100, - removeOnFail: 1000, - }); + const queueMap = { + email: getEmailQueue(), + }; + + const queue = queueMap[name]; + + // Queue collapses all generics so .add() accepts (string, data) without ExtractNameType inference errors + // biome-ignore lint/suspicious/noExplicitAny: BullMQ ExtractNameType bug (github.com/taskforcesh/bullmq/issues/3369) + const q = queue as unknown as Queue; + return q.add(name, data, { + delay: options?.delay, + priority: options?.priority, + removeOnComplete: 100, + removeOnFail: 1000, + }); } diff --git a/apps/backend/src/jobs/worker.ts b/apps/backend/src/jobs/worker.ts index 4ece22f..d1ffacb 100644 --- a/apps/backend/src/jobs/worker.ts +++ b/apps/backend/src/jobs/worker.ts @@ -1,57 +1,49 @@ import { type Job, Worker } from "bullmq"; import pino from "pino"; -import { getRedis } from "@/lib/redis"; -import type { JobData, JobName } from "./index"; +import { getRedis } from "@/lib/redis.ts"; +import { sendEmail } from "@/lib/email.ts"; +import { emailTemplates, type JobData, type JobName } from "./index.ts"; const logger = pino({ name: "worker" }); const processors: { [K in JobName]: (job: Job) => Promise } = - { - async email(job) { - logger.info( - { to: job.data.to, template: job.data.template }, - "Processing email job", - ); - // TODO: Implement email sending - }, - async cleanup(job) { - logger.info( - { olderThanDays: job.data.olderThanDays }, - "Processing cleanup job", - ); - // TODO: Implement cleanup logic - }, - async sync(job) { - logger.info({ userId: job.data.userId }, "Processing sync job"); - // TODO: Implement sync logic - }, - }; + { + async email(job) { + const { to, template, props } = job.data; + logger.info({ to, template }, "Processing email job"); + const { subject, html, text } = emailTemplates[template](props); + await sendEmail({ to, subject, html, text }); + }, + }; function startWorker(name: T) { - const worker = new Worker( - name, - (job) => processors[name](job as Job), - // biome-ignore lint/suspicious/noExplicitAny: BullMQ accepts ioredis instances but types diverge - { connection: getRedis() as any, concurrency: 5 }, - ); - worker.on("completed", (job) => - logger.info({ jobId: job.id, name }, "Job completed"), - ); - worker.on("failed", (job, err) => - logger.error({ jobId: job?.id, name, error: err.message }, "Job failed"), - ); - return worker; + const worker = new Worker( + name, + (job) => processors[name](job as Job), + // biome-ignore lint/suspicious/noExplicitAny: BullMQ accepts ioredis instances but types diverge + { connection: getRedis() as any, concurrency: 5 }, + ); + worker.on( + "completed", + (job) => logger.info({ jobId: job.id, name }, "Job completed"), + ); + worker.on( + "failed", + (job, err) => + logger.error({ jobId: job?.id, name, error: err.message }, "Job failed"), + ); + return worker; } -const workers = (["email", "cleanup", "sync"] as JobName[]).map(startWorker); +const workers = (["email"] as JobName[]).map(startWorker); async function shutdown() { - logger.info("Shutting down workers..."); - await Promise.all(workers.map((w) => w.close())); - process.exit(0); + logger.info("Shutting down workers..."); + await Promise.all(workers.map((w) => w.close())); + Deno.exit(0); } -process.on("SIGTERM", shutdown); -process.on("SIGINT", shutdown); +Deno.addSignalListener("SIGTERM", shutdown); +Deno.addSignalListener("SIGINT", shutdown); logger.info("Workers started"); diff --git a/apps/backend/src/lib/__tests__/result.test.ts b/apps/backend/src/lib/__tests__/result.test.ts index 7cd63d6..e4430ff 100644 --- a/apps/backend/src/lib/__tests__/result.test.ts +++ b/apps/backend/src/lib/__tests__/result.test.ts @@ -2,178 +2,185 @@ // Pure functions — no DB, no network, no mocks. import { - andThen, - andThenAsync, - err, - isErr, - isOk, - map, - match, - ok, - unwrap, + andThen, + andThenAsync, + err, + isErr, + isOk, + map, + match, + ok, + unwrap, } from "@repo/shared"; -import { describe, expect, it } from "vitest"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; // ─── Constructors ──────────────────────────────────────────────────────────── describe("ok", () => { - it("creates an Ok result", () => { - expect(ok(42)).toEqual({ ok: true, value: 42 }); - }); - - it("wraps any value including null and objects", () => { - expect(ok(null)).toEqual({ ok: true, value: null }); - expect(ok({ id: "1" })).toEqual({ ok: true, value: { id: "1" } }); - }); + it("creates an Ok result", () => { + expect(ok(42)).toEqual({ ok: true, value: 42 }); + }); + + it("wraps any value including null and objects", () => { + expect(ok(null)).toEqual({ ok: true, value: null }); + expect(ok({ id: "1" })).toEqual({ ok: true, value: { id: "1" } }); + }); }); describe("err", () => { - it("creates an Err result", () => { - expect(err("oops")).toEqual({ ok: false, error: "oops" }); - }); - - it("wraps typed error objects", () => { - const e = { type: "NOT_FOUND" as const, lookup: "abc" }; - expect(err(e)).toEqual({ ok: false, error: e }); - }); + it("creates an Err result", () => { + expect(err("oops")).toEqual({ ok: false, error: "oops" }); + }); + + it("wraps typed error objects", () => { + const e = { type: "NOT_FOUND" as const, lookup: "abc" }; + expect(err(e)).toEqual({ ok: false, error: e }); + }); }); // ─── Guards ────────────────────────────────────────────────────────────────── describe("isOk / isErr", () => { - it("isOk returns true for Ok, false for Err", () => { - expect(isOk(ok(1))).toBe(true); - expect(isOk(err("x"))).toBe(false); - }); - - it("isErr returns true for Err, false for Ok", () => { - expect(isErr(err("x"))).toBe(true); - expect(isErr(ok(1))).toBe(false); - }); + it("isOk returns true for Ok, false for Err", () => { + expect(isOk(ok(1))).toBe(true); + expect(isOk(err("x"))).toBe(false); + }); + + it("isErr returns true for Err, false for Ok", () => { + expect(isErr(err("x"))).toBe(true); + expect(isErr(ok(1))).toBe(false); + }); }); // ─── Unwrap ─────────────────────────────────────────────────────────────────── describe("unwrap", () => { - it("returns the value for an Ok result", () => { - expect(unwrap(ok("hello"))).toBe("hello"); - }); + it("returns the value for an Ok result", () => { + expect(unwrap(ok("hello"))).toBe("hello"); + }); - it("throws when called on an Err result", () => { - expect(() => unwrap(err("bad"))).toThrow("Called unwrap on an Err result"); - }); + it("throws when called on an Err result", () => { + expect(() => unwrap(err("bad"))).toThrow("Called unwrap on an Err result"); + }); }); // ─── map ───────────────────────────────────────────────────────────────────── describe("map", () => { - it("transforms the Ok value", () => { - const result = map(ok(2), (n) => n * 3); - expect(result).toEqual(ok(6)); - }); - - it("passes Err through unchanged", () => { - const original = err({ type: "NOT_FOUND" as const, lookup: "x" }); - const result = map(original, (n: number) => n * 3); - expect(result).toEqual(original); - }); - - it("does not call fn on Err", () => { - let called = false; - map(err("e"), () => { - called = true; - return 0; - }); - expect(called).toBe(false); - }); + it("transforms the Ok value", () => { + const result = map(ok(2), (n) => n * 3); + expect(result).toEqual(ok(6)); + }); + + it("passes Err through unchanged", () => { + const original = err({ type: "NOT_FOUND" as const, lookup: "x" }); + const result = map(original, (n: number) => n * 3); + expect(result).toEqual(original); + }); + + it("does not call fn on Err", () => { + let called = false; + map(err("e"), () => { + called = true; + return 0; + }); + expect(called).toBe(false); + }); }); // ─── andThen ───────────────────────────────────────────────────────────────── describe("andThen", () => { - it("chains the function when Ok", () => { - const result = andThen(ok(5), (n) => ok(n + 1)); - expect(result).toEqual(ok(6)); - }); - - it("short-circuits when the input is Err", () => { - const original = err("input failed"); - const result = andThen(original, (n: number) => ok(n + 1)); - expect(result).toEqual(original); - }); - - it("propagates Err returned by the chained function", () => { - const result = andThen(ok(5), () => err("chained failed")); - expect(result).toEqual(err("chained failed")); - }); - - it("does not call fn on Err input", () => { - let called = false; - andThen(err("e"), () => { - called = true; - return ok(0); - }); - expect(called).toBe(false); - }); + it("chains the function when Ok", () => { + const result = andThen(ok(5), (n) => ok(n + 1)); + expect(result).toEqual(ok(6)); + }); + + it("short-circuits when the input is Err", () => { + const original = err("input failed"); + const result = andThen(original, (n: number) => ok(n + 1)); + expect(result).toEqual(original); + }); + + it("propagates Err returned by the chained function", () => { + const result = andThen(ok(5), () => err("chained failed")); + expect(result).toEqual(err("chained failed")); + }); + + it("does not call fn on Err input", () => { + let called = false; + andThen(err("e"), () => { + called = true; + return ok(0); + }); + expect(called).toBe(false); + }); }); // ─── andThenAsync ───────────────────────────────────────────────────────────── describe("andThenAsync", () => { - it("chains the async function when Ok", async () => { - const result = await andThenAsync(ok(5), async (n) => ok(n * 2)); - expect(result).toEqual(ok(10)); - }); - - it("short-circuits when the input is Err", async () => { - const original = err("input failed"); - const result = await andThenAsync(original, async (n: number) => ok(n)); - expect(result).toEqual(original); - }); - - it("propagates Err returned by the async function", async () => { - const result = await andThenAsync(ok("x"), async () => err("async failed")); - expect(result).toEqual(err("async failed")); - }); - - it("does not call fn on Err input", async () => { - let called = false; - await andThenAsync(err("e"), async () => { - called = true; - return ok(0); - }); - expect(called).toBe(false); - }); + it("chains the async function when Ok", async () => { + const result = await andThenAsync(ok(5), (n) => Promise.resolve(ok(n * 2))); + expect(result).toEqual(ok(10)); + }); + + it("short-circuits when the input is Err", async () => { + const original = err("input failed"); + const result = await andThenAsync( + original, + (n: number) => Promise.resolve(ok(n)), + ); + expect(result).toEqual(original); + }); + + it("propagates Err returned by the async function", async () => { + const result = await andThenAsync( + ok("x"), + () => Promise.resolve(err("async failed")), + ); + expect(result).toEqual(err("async failed")); + }); + + it("does not call fn on Err input", async () => { + let called = false; + await andThenAsync(err("e"), () => { + called = true; + return Promise.resolve(ok(0)); + }); + expect(called).toBe(false); + }); }); // ─── match ─────────────────────────────────────────────────────────────────── describe("match", () => { - it("calls the ok handler for Ok results", () => { - const result = match(ok(42), { - ok: (n) => `value is ${n}`, - err: () => "error", - }); - expect(result).toBe("value is 42"); - }); - - it("calls the err handler for Err results", () => { - const result = match(err("bad"), { - ok: () => "ok", - err: (e) => `error: ${e}`, - }); - expect(result).toBe("error: bad"); - }); - - it("ok and err branches can return different types", () => { - // This tests the R1 | R2 generic — both branches compile with distinct return types. - const result = match( - ok(1) as ReturnType> | ReturnType>, - { - ok: (n) => n * 2, // number - err: (e) => e.length, // also number, but from a different source - }, - ); - expect(typeof result).toBe("number"); - }); + it("calls the ok handler for Ok results", () => { + const result = match(ok(42), { + ok: (n) => `value is ${n}`, + err: () => "error", + }); + expect(result).toBe("value is 42"); + }); + + it("calls the err handler for Err results", () => { + const result = match(err("bad"), { + ok: () => "ok", + err: (e) => `error: ${e}`, + }); + expect(result).toBe("error: bad"); + }); + + it("ok and err branches can return different types", () => { + // This tests the R1 | R2 generic — both branches compile with distinct return types. + const result = match( + ok(1) as ReturnType> | ReturnType>, + { + ok: (n) => n * 2, // number + err: (e) => e.length, // also number, but from a different source + }, + ); + expect(typeof result).toBe("number"); + }); }); diff --git a/apps/backend/src/lib/auth.ts b/apps/backend/src/lib/auth.ts index 83dfd3f..e82892f 100644 --- a/apps/backend/src/lib/auth.ts +++ b/apps/backend/src/lib/auth.ts @@ -1,67 +1,157 @@ +import { hash, verify } from "@node-rs/argon2"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { openAPI } from "better-auth/plugins"; +import { twoFactor } from "better-auth/plugins/two-factor"; import { db, schema } from "@/db"; -import env from "@/env"; +import { queueEmail } from "@/lib/email"; +import { redis } from "@/lib/redis"; +import type { User as DbUser } from "@repo/db/schema"; +import env from "@/env.ts"; + +const argon2Options = { + memoryCost: 65536, + timeCost: 3, + parallelism: 4, + outputLen: 32, + algorithm: 2, +}; -// Build social providers object — only include a provider when both its -// client ID and secret are present. Adding empty strings would cause silent -// OAuth failures, so we omit the provider entirely when vars are missing. const socialProviders: Parameters[0]["socialProviders"] = { - ...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET - ? { - google: { - clientId: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, - }, - } - : {}), - ...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET - ? { - github: { - clientId: env.GITHUB_CLIENT_ID, - clientSecret: env.GITHUB_CLIENT_SECRET, - }, - } - : {}), + ...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET + ? { + google: { + clientId: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET, + }, + } + : {}), + ...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET + ? { + github: { + clientId: env.GITHUB_CLIENT_ID, + clientSecret: env.GITHUB_CLIENT_SECRET, + }, + } + : {}), }; +const secondaryStorage = redis + ? { + get: (key: string) => redis!.get(key), + set: async (key: string, value: string, ttl?: number) => { + if (ttl) await redis!.setex(key, ttl, value); + else await redis!.set(key, value); + }, + delete: async (key: string) => { + await redis!.del(key); + }, + } + : undefined; + export const auth = betterAuth({ - plugins: [openAPI()], - socialProviders, - database: drizzleAdapter(db, { - provider: "pg", - // Map better-auth model names to the project's Drizzle table objects. - // better-auth uses singular names (user, session, account, verification) - // while the schema uses plural names (users, sessions, accounts, verifications). - schema: { - user: schema.users, - session: schema.sessions, - account: schema.accounts, - verification: schema.verifications, - }, - }), - secret: env.BETTER_AUTH_SECRET, - baseURL: env.BETTER_AUTH_URL, - trustedOrigins: [env.FRONTEND_URL], - emailAndPassword: { - enabled: true, - autoSignIn: true, - }, - session: { - expiresIn: 60 * 60 * 24 * 7, // 7 days - updateAge: 60 * 60 * 24, // 1 day - }, - user: { - additionalFields: { - role: { - type: "string", - required: false, - defaultValue: "user", - }, - }, - }, + database: drizzleAdapter(db, { + provider: "pg", + schema: { + user: schema.users, + session: schema.sessions, + account: schema.accounts, + verification: schema.verifications, + }, + }), + plugins: [ + openAPI(), + twoFactor({ + issuer: env.BETTER_AUTH_URL + ? new URL(env.BETTER_AUTH_URL).hostname + : "Orcta", + totpOptions: { + digits: 6, + period: 30, + }, + backupCodeOptions: { + amount: 10, + length: 10, + storeBackupCodes: "encrypted", + }, + trustDeviceMaxAge: 30 * 24 * 60 * 60, + twoFactorCookieMaxAge: 600, + }), + ], + socialProviders, + secret: env.BETTER_AUTH_SECRET, + baseURL: env.BETTER_AUTH_URL, + trustedOrigins: [env.FRONTEND_URL], + emailAndPassword: { + enabled: true, + autoSignIn: true, + minPasswordLength: 8, + maxPasswordLength: 256, + revokeSessionsOnPasswordReset: true, + sendResetPassword: async ({ user, url }) => { + await queueEmail({ + to: user.email, + template: "passwordReset", + props: { name: user.name, actionUrl: url }, + }); + }, + password: { + hash: (password) => hash(password, argon2Options), + verify: ({ password, hash: storedHash }) => + verify(storedHash, password, argon2Options), + }, + }, + emailVerification: { + sendVerificationEmail: async ({ user, url }) => { + await queueEmail({ + to: user.email, + template: "welcome", + props: { name: user.name, actionUrl: url }, + }); + }, + sendOnSignUp: true, + }, + session: { + expiresIn: 60 * 60 * 24 * 7, + updateAge: 60 * 60 * 24, + cookieCache: { + enabled: true, + maxAge: 60 * 15, + }, + }, + secondaryStorage, + rateLimit: { + // better-auth applies a built-in 3-requests-per-10s rule to + // /sign-in, /sign-up, /change-password, /change-email regardless of the + // max below. With storage: "secondary-storage" and no Redis configured, + // that storage layer silently no-ops (optional chaining on an undefined + // secondaryStorage), so the limit was never actually enforced outside + // of environments with Redis — including the integration test suite, + // which legitimately signs up/in many users in quick succession. + enabled: env.NODE_ENV !== "test", + window: 10, + max: 100, + storage: "secondary-storage", + }, + user: { + additionalFields: { + role: { + type: "string", + required: false, + defaultValue: "buyer", + }, + }, + }, + advanced: { + backgroundTasks: { + handler: (promise) => { + promise.catch(console.error); + }, + }, + }, }); -export type Session = typeof auth.$Infer.Session; -export type User = Session["user"]; +type BetterAuthSession = typeof auth.$Infer.Session; +type BetterUser = BetterAuthSession["user"]; +export type Session = BetterAuthSession; +export type User = BetterUser & Pick; diff --git a/apps/backend/src/lib/cache.ts b/apps/backend/src/lib/cache.ts index fcf6ad2..c8fa1da 100644 --- a/apps/backend/src/lib/cache.ts +++ b/apps/backend/src/lib/cache.ts @@ -23,28 +23,28 @@ import { redis } from "@/lib/redis"; * ); */ export async function withCache( - key: string, - ttlSeconds: number, - fn: () => Promise, + key: string, + ttlSeconds: number, + fn: () => Promise, ): Promise { - if (!redis) return fn(); + if (!redis) return fn(); - try { - const cached = await redis.get(key); - if (cached !== null) return JSON.parse(cached) as T; - } catch { - // Redis unavailable — fall through to source of truth - } + try { + const cached = await redis.get(key); + if (cached !== null) return JSON.parse(cached) as T; + } catch { + // Redis unavailable — fall through to source of truth + } - const value = await fn(); + const value = await fn(); - try { - await redis.set(key, JSON.stringify(value), "EX", ttlSeconds); - } catch { - // Best-effort write — never fail the request because the cache is down - } + try { + await redis.set(key, JSON.stringify(value), "EX", ttlSeconds); + } catch { + // Best-effort write — never fail the request because the cache is down + } - return value; + return value; } /** @@ -56,8 +56,8 @@ export async function withCache( * await invalidateCache(cacheKey("post", postId), cacheKey("posts", "list")); */ export async function invalidateCache(...keys: string[]): Promise { - if (!redis || keys.length === 0) return; - await redis.del(...keys); + if (!redis || keys.length === 0) return; + await redis.del(...keys); } /** @@ -69,5 +69,5 @@ export async function invalidateCache(...keys: string[]): Promise { * cacheKey("posts", page, limit) // "posts:1:20" */ export function cacheKey(...parts: (string | number)[]): string { - return parts.join(":"); + return parts.join(":"); } diff --git a/apps/backend/src/lib/configure-open-api.ts b/apps/backend/src/lib/configure-open-api.ts index 3fae757..15a410b 100644 --- a/apps/backend/src/lib/configure-open-api.ts +++ b/apps/backend/src/lib/configure-open-api.ts @@ -1,52 +1,52 @@ import { Scalar } from "@scalar/hono-api-reference"; -import type { AppType } from "./create-app"; +import type { AppType } from "./create-app.ts"; -export default function configureOpenAPI(app: AppType) { - app.doc("/openapi.json", { - openapi: "3.1.0", - info: { - title: "Orcta Stack API", - version: "1.0.0", - description: "API documentation for Orcta Stack", - }, - servers: [ - { - url: "http://localhost:9999", - description: "Local development", - }, - ], - }); +export default function configureOpenAPI(app: Awaited) { + app.doc("/openapi.json", { + openapi: "3.1.0", + info: { + title: "Orcta Stack API", + version: "1.0.0", + description: "API documentation for Orcta Stack", + }, + servers: [ + { + url: "http://localhost:9999", + description: "Local development", + }, + ], + }); - app.get( - "/docs", - Scalar({ - theme: "kepler", - layout: "modern", - defaultHttpClient: { - targetKey: "js", - clientKey: "fetch", - }, - url: "/openapi.json", - sources: [ - { - title: "Backend API", - url: "/openapi.json", - }, - { url: "/api/auth/open-api/generate-schema", title: "Auth" }, - ], - title: "Orcta Stack API Reference", - metaData: { - title: "Orcta Stack", - description: "Interactive API documentation for Orcta Stack", - ogDescription: "Explore and test the Orcta Stack API endpoints", - ogTitle: "Orcta Stack API Documentation", - twitterCard: "summary_large_image", - }, - searchHotKey: "k", - showSidebar: true, - hideModels: false, - hideDownloadButton: false, - hideDarkModeToggle: false, - }), - ); + app.get( + "/docs", + Scalar({ + theme: "kepler", + layout: "modern", + defaultHttpClient: { + targetKey: "js", + clientKey: "fetch", + }, + url: "/openapi.json", + sources: [ + { + title: "Backend API", + url: "/openapi.json", + }, + { url: "/api/auth/open-api/generate-schema", title: "Auth" }, + ], + title: "Orcta Stack API Reference", + metaData: { + title: "Orcta Stack", + description: "Interactive API documentation for Orcta Stack", + ogDescription: "Explore and test the Orcta Stack API endpoints", + ogTitle: "Orcta Stack API Documentation", + twitterCard: "summary_large_image", + }, + searchHotKey: "k", + showSidebar: true, + hideModels: false, + hideDownloadButton: false, + hideDarkModeToggle: false, + }), + ); } diff --git a/apps/backend/src/lib/create-app.ts b/apps/backend/src/lib/create-app.ts index c79b6e7..abce5a5 100644 --- a/apps/backend/src/lib/create-app.ts +++ b/apps/backend/src/lib/create-app.ts @@ -3,70 +3,72 @@ import { pinoLogger } from "hono-pino"; import pino, { multistream } from "pino"; import { notFound, onError } from "stoker/middlewares"; import { defaultHook } from "stoker/openapi"; -import env from "@/env"; -import type { Session } from "./auth"; -import type { WideEvent } from "./types"; +import env from "@/env.ts"; +import type { Session, User } from "./auth.ts"; +import type { WideEvent } from "./types.ts"; export type AppEnv = { - Variables: { - user: Session["user"]; - session: Session["session"]; - wideEvent: WideEvent; - }; + Variables: { + user: User; + session: Session["session"]; + wideEvent: WideEvent; + }; }; export function createRouter() { - return new OpenAPIHono({ - defaultHook, - }); + return new OpenAPIHono({ + defaultHook, + }); } -function buildPinoInstance() { - if (env.NODE_ENV === "development") { - return pino({ - level: env.LOG_LEVEL, - transport: { target: "pino-pretty", options: { colorize: true } }, - }); - } +async function buildPinoInstance() { + if (env.NODE_ENV === "development") { + return pino({ + level: env.LOG_LEVEL, + transport: { target: "pino-pretty", options: { colorize: true } }, + }); + } - const streams: pino.StreamEntry[] = [ - { level: env.LOG_LEVEL, stream: process.stdout }, - ]; + const stdoutStream = { + write: (msg: string) => Deno.stdout.write(new TextEncoder().encode(msg)), + }; - if (env.AXIOM_TOKEN && env.AXIOM_DATASET) { - // Dynamic import keeps the Axiom transport out of dev/test bundles - // eslint-disable-next-line @typescript-eslint/no-require-imports - const { createWriteStream } = require("@axiomhq/pino"); - streams.push({ - level: "info", - stream: createWriteStream({ - dataset: env.AXIOM_DATASET, - token: env.AXIOM_TOKEN, - }), - }); - } + const streams: pino.StreamEntry[] = [ + { level: env.LOG_LEVEL, stream: stdoutStream }, + ]; - return pino({ level: env.LOG_LEVEL }, multistream(streams)); + if (env.AXIOM_TOKEN && env.AXIOM_DATASET) { + const { default: createAxiomStream } = await import("@axiomhq/pino"); + streams.push({ + level: "info", + stream: await createAxiomStream({ + dataset: env.AXIOM_DATASET, + token: env.AXIOM_TOKEN, + }), + }); + } + + return pino({ level: env.LOG_LEVEL }, multistream(streams)); } -export default function createApp() { - const app = createRouter(); +export default async function createApp() { + const app = createRouter(); - // Logging — hono-pino binds a per-request logger to context; wide-event - // middleware uses this logger to emit the single canonical event. - app.use( - pinoLogger({ - pino: buildPinoInstance(), - // Suppress hono-pino's own per-request log — we emit the wide event instead - http: false, - }), - ); + // Logging — hono-pino binds a per-request logger to context; wide-event + // middleware uses this logger to emit the single canonical event. + app.use( + pinoLogger({ + pino: await buildPinoInstance(), + // Suppress hono-pino's own per-request log — we emit the wide event instead + http: false, + }), + ); - // Error handling - app.onError(onError); - app.notFound(notFound); + // Error handling + app.onError(onError); + app.notFound(notFound); - return app; + return app; } -export type AppType = ReturnType; +export type AppType = Awaited>; diff --git a/apps/backend/src/lib/email.ts b/apps/backend/src/lib/email.ts new file mode 100644 index 0000000..bf1c714 --- /dev/null +++ b/apps/backend/src/lib/email.ts @@ -0,0 +1,47 @@ +import { Resend } from "resend"; +import env from "@/env.ts"; +import { redis } from "@/lib/redis"; +import { addJob, emailTemplates, type JobData } from "@/jobs/index.ts"; + +const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null; + +export async function sendEmail(options: { + to: string; + subject: string; + html: string; + text: string; +}) { + if (!resend) { + console.log("[email] no RESEND_API_KEY configured, logging instead"); + console.log("[email] to:", options.to); + console.log("[email] subject:", options.subject); + return; + } + + const { error } = await resend.emails.send({ + from: `Orcta `, + replyTo: "noreply@orctatech.com", + ...options, + }); + + if (error) { + console.error("[email] failed to send:", error); + } +} + +// Email and background jobs are independently optional batteries (RESEND_API_KEY +// vs REDIS_URL). Queue through BullMQ when Redis is configured; otherwise send +// inline so email keeps working without Redis, same as `sendEmail`'s own +// RESEND_API_KEY fallback. +export async function queueEmail(payload: JobData["email"]) { + if (redis) { + await addJob("email", payload); + return; + } + const { subject, html, text } = emailTemplates[payload.template]( + payload.props, + ); + await sendEmail({ to: payload.to, subject, html, text }); +} diff --git a/apps/backend/src/lib/error.ts b/apps/backend/src/lib/error.ts index ac36abc..7b5750e 100644 --- a/apps/backend/src/lib/error.ts +++ b/apps/backend/src/lib/error.ts @@ -1,42 +1,42 @@ // AppError — for known, expected failures that map to HTTP responses. // Use in handlers when you need to communicate a specific error to the client. export class AppError extends Error { - constructor( - public readonly code: string, - message: string, - public readonly statusCode: number = 500, - public readonly details?: Record, - ) { - super(message); - this.name = "AppError"; - } + constructor( + public readonly code: string, + message: string, + public readonly statusCode: number = 500, + public readonly details?: Record, + ) { + super(message); + this.name = "AppError"; + } - static badRequest(message: string, details?: Record) { - return new AppError("BAD_REQUEST", message, 400, details); - } - static unauthorized(message = "Unauthorized") { - return new AppError("UNAUTHORIZED", message, 401); - } - static forbidden(message = "Forbidden") { - return new AppError("FORBIDDEN", message, 403); - } - static notFound(message = "Not found") { - return new AppError("NOT_FOUND", message, 404); - } - static conflict(message: string) { - return new AppError("CONFLICT", message, 409); - } - static internal(message = "Internal server error") { - return new AppError("INTERNAL_ERROR", message, 500); - } + static badRequest(message: string, details?: Record) { + return new AppError("BAD_REQUEST", message, 400, details); + } + static unauthorized(message = "Unauthorized") { + return new AppError("UNAUTHORIZED", message, 401); + } + static forbidden(message = "Forbidden") { + return new AppError("FORBIDDEN", message, 403); + } + static notFound(message = "Not found") { + return new AppError("NOT_FOUND", message, 404); + } + static conflict(message: string) { + return new AppError("CONFLICT", message, 409); + } + static internal(message = "Internal server error") { + return new AppError("INTERNAL_ERROR", message, 500); + } - toJSON() { - return { - code: this.code, - message: this.message, - ...(this.details && { details: this.details }), - }; - } + toJSON() { + return { + code: this.code, + message: this.message, + ...(this.details && { details: this.details }), + }; + } } // InfrastructureError — wraps throws from DB, Redis, storage, external APIs. @@ -44,11 +44,11 @@ export class AppError extends Error { // Handlers that receive one as a result return 500; they don't re-throw. // The global onError only sees these if they escape (a bug). export class InfrastructureError extends Error { - constructor( - message: string, - public readonly cause?: unknown, - ) { - super(message); - this.name = "InfrastructureError"; - } + constructor( + message: string, + public override readonly cause?: unknown, + ) { + super(message); + this.name = "InfrastructureError"; + } } diff --git a/apps/backend/src/lib/http-status-phrases.ts b/apps/backend/src/lib/http-status-phrases.ts index 1d3a655..8266701 100644 --- a/apps/backend/src/lib/http-status-phrases.ts +++ b/apps/backend/src/lib/http-status-phrases.ts @@ -151,7 +151,7 @@ export const MULTIPLE_CHOICES = "Multiple Choices"; * The 511 status code indicates that the client needs to authenticate to gain network access. */ export const NETWORK_AUTHENTICATION_REQUIRED = - "Network Authentication Required"; + "Network Authentication Required"; /** * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.3.5 * @@ -258,7 +258,7 @@ export const PROXY_AUTHENTICATION_REQUIRED = "Proxy Authentication Required"; * The server is unwilling to process the request because its header fields are too large. The request MAY be resubmitted after reducing the size of the request header fields. */ export const REQUEST_HEADER_FIELDS_TOO_LARGE = - "Request Header Fields Too Large"; + "Request Header Fields Too Large"; /** * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.5.7 * @@ -283,7 +283,7 @@ export const REQUEST_URI_TOO_LONG = "Request-URI Too Long"; * The range specified by the Range header field in the request can't be fulfilled; it's possible that the range is outside the size of the target URI's data. */ export const REQUESTED_RANGE_NOT_SATISFIABLE = - "Requested Range Not Satisfiable"; + "Requested Range Not Satisfiable"; /** * Official Documentation @ https://tools.ietf.org/html/rfc7231#section-6.3.6 * diff --git a/apps/backend/src/lib/infra.ts b/apps/backend/src/lib/infra.ts index 4cb08da..896ae05 100644 --- a/apps/backend/src/lib/infra.ts +++ b/apps/backend/src/lib/infra.ts @@ -1,16 +1,16 @@ import type { Result } from "@repo/shared"; import { err, ok } from "@repo/shared"; -import { InfrastructureError } from "./error"; +import { InfrastructureError } from "./error.ts"; // The single catch boundary for all repository operations. // Wrap every DB/Redis/storage call in this — never catch anywhere else in a repository. export async function tryInfra( - message: string, - fn: () => Promise, + message: string, + fn: () => Promise, ): Promise> { - try { - return ok(await fn()); - } catch (cause) { - return err(new InfrastructureError(message, cause)); - } + try { + return ok(await fn()); + } catch (cause) { + return err(new InfrastructureError(message, cause)); + } } diff --git a/apps/backend/src/lib/rate-limit.ts b/apps/backend/src/lib/rate-limit.ts index 0e2271e..507d0d3 100644 --- a/apps/backend/src/lib/rate-limit.ts +++ b/apps/backend/src/lib/rate-limit.ts @@ -1,67 +1,67 @@ import type { Context, Next } from "hono"; interface RateLimitOptions { - windowMs?: number; // Time window in ms (default: 60000 = 1 min) - max?: number; // Max requests per window (default: 100) - keyGenerator?: (c: Context) => string; - handler?: (c: Context) => Response; + windowMs?: number; // Time window in ms (default: 60000 = 1 min) + max?: number; // Max requests per window (default: 100) + keyGenerator?: (c: Context) => string; + handler?: (c: Context) => Response; } interface RateLimitEntry { - count: number; - resetAt: number; + count: number; + resetAt: number; } const store = new Map(); // Cleanup old entries periodically setInterval(() => { - const now = Date.now(); - for (const [key, entry] of store) { - if (entry.resetAt < now) store.delete(key); - } + const now = Date.now(); + for (const [key, entry] of store) { + if (entry.resetAt < now) store.delete(key); + } }, 60000); export function rateLimit(options: RateLimitOptions = {}) { - const { - windowMs = 60000, - max = 100, - keyGenerator = (c) => - c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || "unknown", - handler = (c) => - c.json( - { - success: false, - error: { code: "RATE_LIMITED", message: "Too many requests" }, - }, - 429, - ), - } = options; + const { + windowMs = 60000, + max = 100, + keyGenerator = (c) => + c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || "unknown", + handler = (c) => + c.json( + { + success: false, + error: { code: "RATE_LIMITED", message: "Too many requests" }, + }, + 429, + ), + } = options; - return async (c: Context, next: Next) => { - const key = keyGenerator(c); - const now = Date.now(); - const entry = store.get(key); + return (c: Context, next: Next) => { + const key = keyGenerator(c); + const now = Date.now(); + const entry = store.get(key); - if (!entry || entry.resetAt < now) { - store.set(key, { count: 1, resetAt: now + windowMs }); - c.header("X-RateLimit-Limit", String(max)); - c.header("X-RateLimit-Remaining", String(max - 1)); - return next(); - } + if (!entry || entry.resetAt < now) { + store.set(key, { count: 1, resetAt: now + windowMs }); + c.header("X-RateLimit-Limit", String(max)); + c.header("X-RateLimit-Remaining", String(max - 1)); + return next(); + } - if (entry.count >= max) { - c.header("X-RateLimit-Limit", String(max)); - c.header("X-RateLimit-Remaining", "0"); - c.header("Retry-After", String(Math.ceil((entry.resetAt - now) / 1000))); - return handler(c); - } + if (entry.count >= max) { + c.header("X-RateLimit-Limit", String(max)); + c.header("X-RateLimit-Remaining", "0"); + c.header("Retry-After", String(Math.ceil((entry.resetAt - now) / 1000))); + return handler(c); + } - entry.count++; - c.header("X-RateLimit-Limit", String(max)); - c.header("X-RateLimit-Remaining", String(max - entry.count)); - return next(); - }; + entry.count++; + c.header("X-RateLimit-Limit", String(max)); + c.header("X-RateLimit-Remaining", String(max - entry.count)); + return next(); + }; } // Presets diff --git a/apps/backend/src/lib/redis.ts b/apps/backend/src/lib/redis.ts index 2f66e37..89a55b4 100644 --- a/apps/backend/src/lib/redis.ts +++ b/apps/backend/src/lib/redis.ts @@ -1,13 +1,13 @@ -import Redis from "ioredis"; +import { Redis } from "ioredis"; import env from "@/env"; export const redis = env.REDIS_URL - ? new Redis(env.REDIS_URL, { maxRetriesPerRequest: null }) - : null; + ? new Redis(env.REDIS_URL, { maxRetriesPerRequest: null }) + : null; export function getRedis(): Redis { - if (!redis) { - throw new Error("REDIS_URL not configured"); - } - return redis; + if (!redis) { + throw new Error("REDIS_URL not configured"); + } + return redis; } diff --git a/apps/backend/src/lib/storage.ts b/apps/backend/src/lib/storage.ts index 48eada9..0d74abb 100644 --- a/apps/backend/src/lib/storage.ts +++ b/apps/backend/src/lib/storage.ts @@ -1,83 +1,83 @@ import { - DeleteObjectCommand, - GetObjectCommand, - PutObjectCommand, - S3Client, + DeleteObjectCommand, + GetObjectCommand, + PutObjectCommand, + S3Client, } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import env from "@/env"; // Works with AWS S3, Cloudflare R2, MinIO, etc. const s3 = new S3Client({ - region: env.S3_REGION || "auto", - endpoint: env.S3_ENDPOINT, - credentials: env.S3_ACCESS_KEY_ID - ? { - accessKeyId: env.S3_ACCESS_KEY_ID, - secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "", - } - : undefined, + region: env.S3_REGION || "auto", + endpoint: env.S3_ENDPOINT, + credentials: env.S3_ACCESS_KEY_ID + ? { + accessKeyId: env.S3_ACCESS_KEY_ID, + secretAccessKey: env.S3_SECRET_ACCESS_KEY ?? "", + } + : undefined, }); const BUCKET = env.S3_BUCKET || "uploads"; interface UploadOptions { - key: string; - contentType?: string; - expiresIn?: number; // seconds, default 3600 + key: string; + contentType?: string; + expiresIn?: number; // seconds, default 3600 } interface DownloadOptions { - key: string; - expiresIn?: number; + key: string; + expiresIn?: number; } /** * Generate presigned URL for uploading */ -export async function getUploadUrl({ - key, - contentType, - expiresIn = 3600, +export function getUploadUrl({ + key, + contentType, + expiresIn = 3600, }: UploadOptions): Promise { - const command = new PutObjectCommand({ - Bucket: BUCKET, - Key: key, - ContentType: contentType, - }); - return getSignedUrl(s3, command, { expiresIn }); + const command = new PutObjectCommand({ + Bucket: BUCKET, + Key: key, + ContentType: contentType, + }); + return getSignedUrl(s3, command, { expiresIn }); } /** * Generate presigned URL for downloading */ -export async function getDownloadUrl({ - key, - expiresIn = 3600, +export function getDownloadUrl({ + key, + expiresIn = 3600, }: DownloadOptions): Promise { - const command = new GetObjectCommand({ - Bucket: BUCKET, - Key: key, - }); - return getSignedUrl(s3, command, { expiresIn }); + const command = new GetObjectCommand({ + Bucket: BUCKET, + Key: key, + }); + return getSignedUrl(s3, command, { expiresIn }); } /** * Delete a file */ export async function deleteFile(key: string): Promise { - const command = new DeleteObjectCommand({ - Bucket: BUCKET, - Key: key, - }); - await s3.send(command); + const command = new DeleteObjectCommand({ + Bucket: BUCKET, + Key: key, + }); + await s3.send(command); } /** * Generate a unique file key */ export function generateKey(filename: string, prefix = "uploads"): string { - const ext = filename.split(".").pop() || ""; - const id = crypto.randomUUID(); - return `${prefix}/${id}${ext ? `.${ext}` : ""}`; + const ext = filename.split(".").pop() || ""; + const id = crypto.randomUUID(); + return `${prefix}/${id}${ext ? `.${ext}` : ""}`; } diff --git a/apps/backend/src/lib/types.ts b/apps/backend/src/lib/types.ts index f44058f..60d63a5 100644 --- a/apps/backend/src/lib/types.ts +++ b/apps/backend/src/lib/types.ts @@ -2,19 +2,19 @@ import type { RouteConfig, RouteHandler } from "@hono/zod-openapi"; import type { ApiError, ApiSuccess } from "@repo/shared"; import type { Context } from "hono"; import type { ZodType } from "zod"; -import type { AppEnv } from "./create-app"; -import { InfrastructureError } from "./error"; +import type { AppEnv } from "./create-app.ts"; +import { InfrastructureError } from "./error.ts"; /** * Shared API response types re-exported for handler convenience. */ export type { - ApiError, - ApiResponse, - ApiSuccess, - Err, - Ok, - Result, + ApiError, + ApiResponse, + ApiSuccess, + Err, + Ok, + Result, } from "@repo/shared"; /** @@ -29,19 +29,19 @@ export { err, isErr, isOk, ok } from "@repo/shared"; * from a single module. */ export { - BAD_REQUEST, - CONFLICT, - CREATED, - FORBIDDEN, - INTERNAL_SERVER_ERROR, - NO_CONTENT, - NOT_FOUND, - OK, - SERVICE_UNAVAILABLE, - TOO_MANY_REQUESTS, - UNAUTHORIZED, - UNPROCESSABLE_ENTITY, -} from "./http-status-codes"; + BAD_REQUEST, + CONFLICT, + CREATED, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + NO_CONTENT, + NOT_FOUND, + OK, + SERVICE_UNAVAILABLE, + TOO_MANY_REQUESTS, + UNAUTHORIZED, + UNPROCESSABLE_ENTITY, +} from "./http-status-codes.ts"; /** * Strongly typed route handler bound to the application environment. @@ -60,10 +60,10 @@ export type AppRouteHandler = RouteHandler; * 200: jsonRes(userSchema, "User retrieved") */ export function jsonRes(schema: S, description: string) { - return { - content: { "application/json": { schema } }, - description, - } as const; + return { + content: { "application/json": { schema } }, + description, + } as const; } /** @@ -75,9 +75,9 @@ export function jsonRes(schema: S, description: string) { * body: jsonBody(createUserSchema) */ export function jsonBody(schema: S) { - return { - content: { "application/json": { schema } }, - } as const; + return { + content: { "application/json": { schema } }, + } as const; } /** @@ -86,7 +86,7 @@ export function jsonBody(schema: S) { * @param data - Response payload */ export function success(data: T): ApiSuccess { - return { success: true, data }; + return { success: true, data }; } /** @@ -95,11 +95,11 @@ export function success(data: T): ApiSuccess { * @param error - Machine-readable error payload */ export function failure(error: { - code: string; - message: string; - details?: Record; + code: string; + message: string; + details?: Record; }): ApiError { - return { success: false, error }; + return { success: false, error }; } /** @@ -109,7 +109,7 @@ export function failure(error: { * from domain/business rule errors. */ export const isInfraError = (e: unknown): e is InfrastructureError => - e instanceof InfrastructureError; + e instanceof InfrastructureError; /** * WideEvent @@ -121,82 +121,82 @@ export const isInfraError = (e: unknown): e is InfrastructureError => * wide-event middleware. */ export type WideEvent = { - /** Unique request identifier */ - request_id?: string; - - /** - * Trace identifier for cross-service correlation. - * - * Forwarded from `x-trace-id` when present, otherwise defaults - * to `request_id`. - */ - trace_id?: string; - - /** ISO timestamp */ - timestamp?: string; - - /** HTTP method */ - method?: string; - - /** Request path */ - path?: string; - - /** Final HTTP status code */ - status_code?: number; - - /** Request duration in milliseconds */ - duration_ms?: number; - - /** Request outcome classification */ - outcome?: "success" | "error"; - - /** Logical service name */ - service?: string; - - /** Code/service version */ - service_version?: string; - - /** - * Deployment identifier. - * - * Typically a Docker image tag or Git SHA. Distinct from - * `service_version`. - */ - deployment_id?: string; - - /** Execution region */ - region?: string; - - /** Client IP address */ - ip?: string; - - /** Client user agent */ - user_agent?: string; - - /** Session identifier */ - session_id?: string; - - /** Authenticated user context */ - user?: { - id: string; - role: string; - [k: string]: unknown; - }; - - /** Error metadata */ - error?: { - type?: string; - message?: string; - code?: string; - retriable?: boolean; - [k: string]: unknown; - }; - - /** Feature flag snapshot */ - feature_flags?: Record; - - /** Arbitrary handler-defined fields */ - [key: string]: unknown; + /** Unique request identifier */ + request_id?: string; + + /** + * Trace identifier for cross-service correlation. + * + * Forwarded from `x-trace-id` when present, otherwise defaults + * to `request_id`. + */ + trace_id?: string; + + /** ISO timestamp */ + timestamp?: string; + + /** HTTP method */ + method?: string; + + /** Request path */ + path?: string; + + /** Final HTTP status code */ + status_code?: number; + + /** Request duration in milliseconds */ + duration_ms?: number; + + /** Request outcome classification */ + outcome?: "success" | "error"; + + /** Logical service name */ + service?: string; + + /** Code/service version */ + service_version?: string; + + /** + * Deployment identifier. + * + * Typically a Docker image tag or Git SHA. Distinct from + * `service_version`. + */ + deployment_id?: string; + + /** Execution region */ + region?: string; + + /** Client IP address */ + ip?: string; + + /** Client user agent */ + user_agent?: string; + + /** Session identifier */ + session_id?: string; + + /** Authenticated user context */ + user?: { + id: string; + role: string; + [k: string]: unknown; + }; + + /** Error metadata */ + error?: { + type?: string; + message?: string; + code?: string; + retriable?: boolean; + [k: string]: unknown; + }; + + /** Feature flag snapshot */ + feature_flags?: Record; + + /** Arbitrary handler-defined fields */ + [key: string]: unknown; }; /** @@ -209,9 +209,9 @@ export type WideEvent = { * @param fields - Partial event fields to merge */ export function addToEvent( - c: Context, - fields: Partial, + c: Context, + fields: Partial, ): void { - const event = c.get("wideEvent"); - if (event) Object.assign(event, fields); + const event = c.get("wideEvent"); + if (event) Object.assign(event, fields); } diff --git a/apps/backend/src/lib/ws.ts b/apps/backend/src/lib/ws.ts index 18c06d8..51d9ed9 100644 --- a/apps/backend/src/lib/ws.ts +++ b/apps/backend/src/lib/ws.ts @@ -1,78 +1,78 @@ import type { WSContext } from "hono/ws"; type Connection = { - ws: WSContext; - userId?: string; - rooms: Set; + ws: WSContext; + userId?: string; + rooms: Set; }; class WebSocketManager { - private connections = new Map(); + private connections = new Map(); - add(id: string, ws: WSContext, userId?: string): void { - this.connections.set(id, { ws, userId, rooms: new Set() }); - } + add(id: string, ws: WSContext, userId?: string): void { + this.connections.set(id, { ws, userId, rooms: new Set() }); + } - remove(id: string): void { - this.connections.delete(id); - } + remove(id: string): void { + this.connections.delete(id); + } - get(id: string): Connection | undefined { - return this.connections.get(id); - } + get(id: string): Connection | undefined { + return this.connections.get(id); + } - // Join a room - join(id: string, room: string): void { - const conn = this.connections.get(id); - if (conn) conn.rooms.add(room); - } + // Join a room + join(id: string, room: string): void { + const conn = this.connections.get(id); + if (conn) conn.rooms.add(room); + } - // Leave a room - leave(id: string, room: string): void { - const conn = this.connections.get(id); - if (conn) conn.rooms.delete(room); - } + // Leave a room + leave(id: string, room: string): void { + const conn = this.connections.get(id); + if (conn) conn.rooms.delete(room); + } - // Send to specific connection - send(id: string, data: unknown): void { - const conn = this.connections.get(id); - if (conn) conn.ws.send(JSON.stringify(data)); - } + // Send to specific connection + send(id: string, data: unknown): void { + const conn = this.connections.get(id); + if (conn) conn.ws.send(JSON.stringify(data)); + } - // Send to all connections in a room - broadcast(room: string, data: unknown, excludeId?: string): void { - const message = JSON.stringify(data); - for (const [id, conn] of this.connections) { - if (conn.rooms.has(room) && id !== excludeId) { - conn.ws.send(message); - } - } - } + // Send to all connections in a room + broadcast(room: string, data: unknown, excludeId?: string): void { + const message = JSON.stringify(data); + for (const [id, conn] of this.connections) { + if (conn.rooms.has(room) && id !== excludeId) { + conn.ws.send(message); + } + } + } - // Send to a specific user (all their connections) - sendToUser(userId: string, data: unknown): void { - const message = JSON.stringify(data); - for (const conn of this.connections.values()) { - if (conn.userId === userId) { - conn.ws.send(message); - } - } - } + // Send to a specific user (all their connections) + sendToUser(userId: string, data: unknown): void { + const message = JSON.stringify(data); + for (const conn of this.connections.values()) { + if (conn.userId === userId) { + conn.ws.send(message); + } + } + } - // Broadcast to all connections - broadcastAll(data: unknown, excludeId?: string): void { - const message = JSON.stringify(data); - for (const [id, conn] of this.connections) { - if (id !== excludeId) { - conn.ws.send(message); - } - } - } + // Broadcast to all connections + broadcastAll(data: unknown, excludeId?: string): void { + const message = JSON.stringify(data); + for (const [id, conn] of this.connections) { + if (id !== excludeId) { + conn.ws.send(message); + } + } + } - // Get connection count - get size(): number { - return this.connections.size; - } + // Get connection count + get size(): number { + return this.connections.size; + } } export const wsManager = new WebSocketManager(); diff --git a/apps/backend/src/middlewares/auth.ts b/apps/backend/src/middlewares/auth.ts index 1ad7529..c98b0e9 100644 --- a/apps/backend/src/middlewares/auth.ts +++ b/apps/backend/src/middlewares/auth.ts @@ -1,62 +1,62 @@ import type { Context, Next } from "hono"; -import { auth } from "@/lib/auth"; +import { auth, type User } from "@/lib/auth"; import type { AppEnv } from "@/lib/create-app"; import { addToEvent, UNAUTHORIZED } from "@/lib/types"; export async function authMiddleware(c: Context, next: Next) { - const session = await auth.api.getSession({ - headers: c.req.raw.headers, - }); - - if (!session) { - return c.json( - { - success: false, - error: { code: "UNAUTHORIZED", message: "Authentication required" }, - }, - UNAUTHORIZED, - ); - } - - c.set("user", session.user); - c.set("session", session.session); - - // Enrich the wide event with auth context so every authenticated request - // carries user identity and session ID without handlers doing it manually. - // session_id is distinct from user_id: one user can have many concurrent - // sessions across devices, making it a separate high-cardinality dimension. - addToEvent(c, { - session_id: session.session.id, - user: { id: session.user.id, role: session.user.role ?? "user" }, - }); - - await next(); + const session = await auth.api.getSession({ + headers: c.req.raw.headers, + }); + + if (!session) { + return c.json( + { + success: false, + error: { code: "UNAUTHORIZED", message: "Authentication required" }, + }, + UNAUTHORIZED, + ); + } + + c.set("user", session.user as User); + c.set("session", session.session); + + // Enrich the wide event with auth context so every authenticated request + // carries user identity and session ID without handlers doing it manually. + // session_id is distinct from user_id: one user can have many concurrent + // sessions across devices, making it a separate high-cardinality dimension. + addToEvent(c, { + session_id: session.session.id, + user: { id: session.user.id, role: session.user.role ?? "buyer" }, + }); + + await next(); } export function requireRole(...roles: string[]) { - return async (c: Context, next: Next) => { - const user = c.get("user"); - - if (!user) { - return c.json( - { - success: false, - error: { code: "UNAUTHORIZED", message: "Authentication required" }, - }, - UNAUTHORIZED, - ); - } - - if (!roles.includes(user.role ?? "user")) { - return c.json( - { - success: false, - error: { code: "FORBIDDEN", message: "Insufficient permissions" }, - }, - 403, - ); - } - - await next(); - }; + return async (c: Context, next: Next) => { + const user = c.get("user"); + + if (!user) { + return c.json( + { + success: false, + error: { code: "UNAUTHORIZED", message: "Authentication required" }, + }, + UNAUTHORIZED, + ); + } + + if (!roles.includes(user.role ?? "buyer")) { + return c.json( + { + success: false, + error: { code: "FORBIDDEN", message: "Insufficient permissions" }, + }, + 403, + ); + } + + await next(); + }; } diff --git a/apps/backend/src/middlewares/wide-event.ts b/apps/backend/src/middlewares/wide-event.ts index 41043d0..2658dda 100644 --- a/apps/backend/src/middlewares/wide-event.ts +++ b/apps/backend/src/middlewares/wide-event.ts @@ -12,18 +12,19 @@ import type { WideEvent } from "@/lib/types"; // - Always keep: 5xx responses, any error field, requests > 2s, admin users // - Randomly sample 5% of everything else function shouldSample(event: WideEvent): boolean { - // Never drop errors or slow requests — these are the events that matter most. - if ((event.status_code ?? 0) >= 500) return true; - if (event.outcome === "error") return true; - if ((event.duration_ms ?? 0) > 2000) return true; - // Never drop admin users — low volume, high signal. - if (event.user?.role === "admin") return true; - // Never drop requests annotated with feature_flags — critical during rollouts. - // Handlers set this via: addToEvent(c, { feature_flags: { flag_name: true } }) - if (event.feature_flags && Object.keys(event.feature_flags).length > 0) - return true; - // Random sample 5% of happy, fast, normal requests. - return Math.random() < 0.05; + // Never drop errors or slow requests — these are the events that matter most. + if ((event.status_code ?? 0) >= 500) return true; + if (event.outcome === "error") return true; + if ((event.duration_ms ?? 0) > 2000) return true; + // Never drop admin users — low volume, high signal. + if (event.user?.role === "admin") return true; + // Never drop requests annotated with feature_flags — critical during rollouts. + // Handlers set this via: addToEvent(c, { feature_flags: { flag_name: true } }) + if (event.feature_flags && Object.keys(event.feature_flags).length > 0) { + return true; + } + // Random sample 5% of happy, fast, normal requests. + return Math.random() < 0.05; } // ─── Wide Event Middleware ──────────────────────────────────────────────────── @@ -39,45 +40,45 @@ function shouldSample(event: WideEvent): boolean { // hono-pino's own per-request log is suppressed in create-app.ts so this is // the only log emitted per request. export async function wideEventMiddleware(c: Context, next: Next) { - const startTime = Date.now(); + const startTime = Date.now(); - const event: WideEvent = { - timestamp: new Date().toISOString(), - request_id: crypto.randomUUID(), - // Propagate trace_id from upstream (gateway/client) if present so events - // across services can be correlated by the same ID without a full OTel setup. - // Falls back to a new UUID so every event always has a trace_id. - trace_id: c.req.header("x-trace-id") ?? crypto.randomUUID(), - method: c.req.method, - path: c.req.path, - ip: - c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "unknown", - user_agent: c.req.header("user-agent"), - service: "backend", - service_version: env.SERVICE_VERSION, - deployment_id: env.DEPLOYMENT_ID, - region: env.REGION, - }; + const event: WideEvent = { + timestamp: new Date().toISOString(), + request_id: crypto.randomUUID(), + // Propagate trace_id from upstream (gateway/client) if present so events + // across services can be correlated by the same ID without a full OTel setup. + // Falls back to a new UUID so every event always has a trace_id. + trace_id: c.req.header("x-trace-id") ?? crypto.randomUUID(), + method: c.req.method, + path: c.req.path, + ip: c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? + "unknown", + user_agent: c.req.header("user-agent"), + service: "backend", + service_version: env.SERVICE_VERSION, + deployment_id: env.DEPLOYMENT_ID, + region: env.REGION, + }; - c.set("wideEvent", event); + c.set("wideEvent", event); - try { - await next(); - event.status_code = c.res.status; - event.outcome = c.res.status < 500 ? "success" : "error"; - } catch (err) { - event.status_code = 500; - event.outcome = "error"; - event.error = { - type: err instanceof Error ? err.constructor.name : "UnknownError", - message: err instanceof Error ? err.message : String(err), - }; - throw err; - } finally { - event.duration_ms = Date.now() - startTime; + try { + await next(); + event.status_code = c.res.status; + event.outcome = c.res.status < 500 ? "success" : "error"; + } catch (err) { + event.status_code = 500; + event.outcome = "error"; + event.error = { + type: err instanceof Error ? err.constructor.name : "UnknownError", + message: err instanceof Error ? err.message : String(err), + }; + throw err; + } finally { + event.duration_ms = Date.now() - startTime; - if (shouldSample(event)) { - getLogger(c).info(event); - } - } + if (shouldSample(event)) { + getLogger(c).info(event); + } + } } diff --git a/apps/backend/src/modules/health/handlers.ts b/apps/backend/src/modules/health/handlers.ts index 9eb74eb..59f6403 100644 --- a/apps/backend/src/modules/health/handlers.ts +++ b/apps/backend/src/modules/health/handlers.ts @@ -2,32 +2,32 @@ import { sql } from "drizzle-orm"; import { db } from "@/db"; import type { AppRouteHandler } from "@/lib/types"; import { OK, SERVICE_UNAVAILABLE, success } from "@/lib/types"; -import type { HealthCheckRoute, PingRoute } from "./routes"; +import type { HealthCheckRoute, PingRoute } from "./routes.ts"; const startTime = Date.now(); export const healthCheckHandler: AppRouteHandler = async ( - c, + c, ) => { - let databaseUp = false; - try { - await db.execute(sql`SELECT 1`); - databaseUp = true; - } catch { - databaseUp = false; - } + let databaseUp = false; + try { + await db.execute(sql`SELECT 1`); + databaseUp = true; + } catch { + databaseUp = false; + } - const data = { - status: (databaseUp ? "healthy" : "unhealthy") as "healthy" | "unhealthy", - timestamp: new Date().toISOString(), - version: "1.0.0", - uptime: Math.floor((Date.now() - startTime) / 1000), - services: { database: (databaseUp ? "up" : "down") as "up" | "down" }, - }; + const data = { + status: (databaseUp ? "healthy" : "unhealthy") as "healthy" | "unhealthy", + timestamp: new Date().toISOString(), + version: "1.0.0", + uptime: Math.floor((Date.now() - startTime) / 1000), + services: { database: (databaseUp ? "up" : "down") as "up" | "down" }, + }; - return c.json(success(data), databaseUp ? OK : SERVICE_UNAVAILABLE); + return c.json(success(data), databaseUp ? OK : SERVICE_UNAVAILABLE); }; -export const pingHandler: AppRouteHandler = async (c) => { - return c.json(success({ message: "pong" as const }), OK); +export const pingHandler: AppRouteHandler = (c) => { + return c.json(success({ message: "pong" as const }), OK); }; diff --git a/apps/backend/src/modules/health/index.ts b/apps/backend/src/modules/health/index.ts index f8b93ba..5818253 100644 --- a/apps/backend/src/modules/health/index.ts +++ b/apps/backend/src/modules/health/index.ts @@ -1,9 +1,9 @@ import { createRouter } from "@/lib/create-app"; -import * as handlers from "./handlers"; -import * as routes from "./routes"; +import * as handlers from "./handlers.ts"; +import * as routes from "./routes.ts"; const router = createRouter() - .openapi(routes.healthCheck, handlers.healthCheckHandler) - .openapi(routes.ping, handlers.pingHandler); + .openapi(routes.healthCheck, handlers.healthCheckHandler) + .openapi(routes.ping, handlers.pingHandler); export default router; diff --git a/apps/backend/src/modules/health/routes.ts b/apps/backend/src/modules/health/routes.ts index 132cebf..3d7b10a 100644 --- a/apps/backend/src/modules/health/routes.ts +++ b/apps/backend/src/modules/health/routes.ts @@ -1,51 +1,51 @@ import { createRoute, z } from "@hono/zod-openapi"; import { apiErrorSchema, apiSuccessSchema } from "@repo/shared"; import { - INTERNAL_SERVER_ERROR, - jsonRes, - OK, - SERVICE_UNAVAILABLE, + INTERNAL_SERVER_ERROR, + jsonRes, + OK, + SERVICE_UNAVAILABLE, } from "@/lib/types"; const tags = ["Health"]; const healthDataSchema = z.object({ - status: z.enum(["healthy", "degraded", "unhealthy"]), - timestamp: z.string(), - version: z.string(), - uptime: z.number(), - services: z.object({ - database: z.enum(["up", "down"]), - }), + status: z.enum(["healthy", "degraded", "unhealthy"]), + timestamp: z.string(), + version: z.string(), + uptime: z.number(), + services: z.object({ + database: z.enum(["up", "down"]), + }), }); const healthResponseSchema = apiSuccessSchema(healthDataSchema); export const healthCheck = createRoute({ - method: "get", - path: "/health", - tags, - summary: "Health check", - description: "Check the health status of the API and its dependencies", - responses: { - [OK]: jsonRes(healthResponseSchema, "Service is healthy"), - [INTERNAL_SERVER_ERROR]: jsonRes(apiErrorSchema, "Service is unhealthy"), - [SERVICE_UNAVAILABLE]: jsonRes(healthResponseSchema, "Service is degraded"), - }, + method: "get", + path: "/health", + tags, + summary: "Health check", + description: "Check the health status of the API and its dependencies", + responses: { + [OK]: jsonRes(healthResponseSchema, "Service is healthy"), + [INTERNAL_SERVER_ERROR]: jsonRes(apiErrorSchema, "Service is unhealthy"), + [SERVICE_UNAVAILABLE]: jsonRes(healthResponseSchema, "Service is degraded"), + }, }); export const ping = createRoute({ - method: "get", - path: "/ping", - tags, - summary: "Ping", - description: "Simple ping endpoint for basic connectivity check", - responses: { - [OK]: jsonRes( - apiSuccessSchema(z.object({ message: z.literal("pong") })), - "Pong response", - ), - }, + method: "get", + path: "/ping", + tags, + summary: "Ping", + description: "Simple ping endpoint for basic connectivity check", + responses: { + [OK]: jsonRes( + apiSuccessSchema(z.object({ message: z.literal("pong") })), + "Pong response", + ), + }, }); export type HealthCheckRoute = typeof healthCheck; diff --git a/apps/backend/src/modules/health/usecases/check-health.usecase.ts b/apps/backend/src/modules/health/usecases/check-health.usecase.ts index d29ad3a..870d280 100644 --- a/apps/backend/src/modules/health/usecases/check-health.usecase.ts +++ b/apps/backend/src/modules/health/usecases/check-health.usecase.ts @@ -2,59 +2,59 @@ // Dependencies required by this use-case export interface CheckHealthDeps { - checkDatabase: () => Promise; + checkDatabase: () => Promise; } // Input for the use-case interface CheckHealthInput { - startTime: number; - version: string; + startTime: number; + version: string; } // Discriminated union result type type CheckHealthResult = - | { - type: "HEALTHY"; - data: HealthData; - } - | { - type: "UNHEALTHY"; - data: HealthData; - }; + | { + type: "HEALTHY"; + data: HealthData; + } + | { + type: "UNHEALTHY"; + data: HealthData; + }; interface HealthData { - status: "healthy" | "degraded" | "unhealthy"; - timestamp: string; - version: string; - uptime: number; - services: { - database: "up" | "down"; - }; + status: "healthy" | "degraded" | "unhealthy"; + timestamp: string; + version: string; + uptime: number; + services: { + database: "up" | "down"; + }; } export async function checkHealthUseCase( - deps: CheckHealthDeps, - input: CheckHealthInput, + deps: CheckHealthDeps, + input: CheckHealthInput, ): Promise { - const { checkDatabase } = deps; - const { startTime, version } = input; - - // Check database connectivity - const databaseUp = await checkDatabase(); - - const data: HealthData = { - status: databaseUp ? "healthy" : "unhealthy", - timestamp: new Date().toISOString(), - version, - uptime: Math.floor((Date.now() - startTime) / 1000), - services: { - database: databaseUp ? "up" : "down", - }, - }; - - if (databaseUp) { - return { type: "HEALTHY", data }; - } - - return { type: "UNHEALTHY", data }; + const { checkDatabase } = deps; + const { startTime, version } = input; + + // Check database connectivity + const databaseUp = await checkDatabase(); + + const data: HealthData = { + status: databaseUp ? "healthy" : "unhealthy", + timestamp: new Date().toISOString(), + version, + uptime: Math.floor((Date.now() - startTime) / 1000), + services: { + database: databaseUp ? "up" : "down", + }, + }; + + if (databaseUp) { + return { type: "HEALTHY", data }; + } + + return { type: "UNHEALTHY", data }; } diff --git a/apps/backend/src/modules/users/__tests__/handlers.test.ts b/apps/backend/src/modules/users/__tests__/handlers.test.ts index 8ef8455..6918c62 100644 --- a/apps/backend/src/modules/users/__tests__/handlers.test.ts +++ b/apps/backend/src/modules/users/__tests__/handlers.test.ts @@ -6,18 +6,27 @@ // Auth is handled by actually signing up through the app — no mocks. // Each test suite gets its own user; afterEach cleans all auth-related tables. -import { eq } from "drizzle-orm"; -import { afterEach, describe, expect, it } from "vitest"; +import { eq, inArray } from "drizzle-orm"; +import { afterAll, afterEach, describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import app from "@/app"; import { db, schema } from "@/db"; // ─── Cleanup ───────────────────────────────────────────────────────────────── +const createdEmails: string[] = []; +function uniqueEmail(prefix = "test"): string { + return `${prefix}-${crypto.randomUUID()}@example.com`; +} + afterEach(async () => { - // verifications has no cascade; delete first. - await db.delete(schema.verifications); - // users cascades sessions + accounts. - await db.delete(schema.users); + const emails = createdEmails.splice(0); + if (emails.length === 0) return; + await db.delete(schema.users).where(inArray(schema.users.email, emails)); +}); + +afterAll(async () => { + await (db as any).$client.end(); }); // ─── Helpers ───────────────────────────────────────────────────────────────── @@ -25,203 +34,208 @@ afterEach(async () => { // Registers a user through the app and returns the session cookie. // autoSignIn: true in auth config means sign-up also creates the session. async function signUp(overrides?: { - email?: string; - password?: string; - name?: string; + email?: string; + password?: string; + name?: string; }) { - const email = overrides?.email ?? "test@example.com"; - const password = overrides?.password ?? "password-secret-123"; - const name = overrides?.name ?? "Test User"; - - const res = await app.request("/api/auth/sign-up/email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email, password, name }), - }); - - const cookie = res.headers.get("set-cookie") ?? ""; - return { cookie, email, password, name }; + const email = overrides?.email ?? uniqueEmail(); + const password = overrides?.password ?? "password-secret-123"; + const name = overrides?.name ?? "Test User"; + + const res = await app.request("/api/auth/sign-up/email", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password, name }), + }); + + createdEmails.push(email); + + const cookie = res.headers.get("set-cookie") ?? ""; + return { cookie, email, password, name }; } // Signs in and returns the session cookie. async function signIn(email: string, password: string) { - const res = await app.request("/api/auth/sign-in/email", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email, password }), - }); - return res.headers.get("set-cookie") ?? ""; + const res = await app.request("/api/auth/sign-in/email", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }), + }); + return res.headers.get("set-cookie") ?? ""; } // Elevates a user to admin directly in the DB. async function makeAdmin(email: string) { - await db - .update(schema.users) - .set({ role: "admin" }) - .where(eq(schema.users.email, email)); + await db + .update(schema.users) + .set({ role: "admin" }) + .where(eq(schema.users.email, email)); } // ─── GET /api/users/me ──────────────────────────────────────────────────────── describe("GET /api/users/me", () => { - it("returns 401 when not authenticated", async () => { - const res = await app.request("/api/users/me"); - expect(res.status).toBe(401); - }); - - it("returns the current user's profile when authenticated", async () => { - const { cookie, email, name } = await signUp(); - const res = await app.request("/api/users/me", { - headers: { Cookie: cookie }, - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as any; - expect(body.success).toBe(true); - expect(body.data.email).toBe(email); - expect(body.data.name).toBe(name); - }); + it("returns 401 when not authenticated", async () => { + const res = await app.request("/api/users/me"); + expect(res.status).toBe(401); + }); + + it("returns the current user's profile when authenticated", async () => { + const { cookie, email, name } = await signUp(); + const res = await app.request("/api/users/me", { + headers: { Cookie: cookie }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as any; + expect(body.success).toBe(true); + expect(body.data.email).toBe(email); + expect(body.data.name).toBe(name); + }); }); // ─── PATCH /api/users/me ───────────────────────────────────────────────────── describe("PATCH /api/users/me", () => { - it("returns 401 when not authenticated", async () => { - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ name: "New Name" }), - }); - expect(res.status).toBe(401); - }); - - it("updates the user's name", async () => { - const { cookie } = await signUp(); - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { Cookie: cookie, "Content-Type": "application/json" }, - body: JSON.stringify({ name: "Updated Name" }), - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as any; - expect(body.data.name).toBe("Updated Name"); - }); - - it("returns 409 when the new email is already taken", async () => { - await signUp({ email: "taken@example.com", name: "Other" }); - const { cookie } = await signUp({ email: "me@example.com", name: "Me" }); - - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { Cookie: cookie, "Content-Type": "application/json" }, - body: JSON.stringify({ email: "taken@example.com" }), - }); - - expect(res.status).toBe(409); - const body = (await res.json()) as any; - expect(body.success).toBe(false); - expect(body.error.code).toBe("EMAIL_TAKEN"); - }); - - it("returns 400 when the email is the same as the current one", async () => { - const { cookie, email } = await signUp(); - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { Cookie: cookie, "Content-Type": "application/json" }, - body: JSON.stringify({ email }), - }); - - expect(res.status).toBe(400); - const body = (await res.json()) as any; - expect(body.error.code).toBe("EMAIL_UNCHANGED"); - }); - - it("updates the email, sets emailVerified to false, and returns the updated user", async () => { - const { cookie } = await signUp({ email: "old@example.com" }); - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { Cookie: cookie, "Content-Type": "application/json" }, - body: JSON.stringify({ email: "new@example.com" }), - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as any; - expect(body.data.email).toBe("new@example.com"); - expect(body.data.emailVerified).toBe(false); - }); - - it("returns 200 with the current user when no fields are provided", async () => { - const { cookie, email } = await signUp(); - const res = await app.request("/api/users/me", { - method: "PATCH", - headers: { Cookie: cookie, "Content-Type": "application/json" }, - body: JSON.stringify({}), - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as any; - expect(body.data.email).toBe(email); - }); + it("returns 401 when not authenticated", async () => { + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: "New Name" }), + }); + expect(res.status).toBe(401); + }); + + it("updates the user's name", async () => { + const { cookie } = await signUp(); + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { Cookie: cookie, "Content-Type": "application/json" }, + body: JSON.stringify({ name: "Updated Name" }), + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as any; + expect(body.data.name).toBe("Updated Name"); + }); + + it("returns 409 when the new email is already taken", async () => { + const takenEmail = uniqueEmail("taken"); + await signUp({ email: takenEmail, name: "Other" }); + const { cookie } = await signUp({ email: uniqueEmail("me"), name: "Me" }); + + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { Cookie: cookie, "Content-Type": "application/json" }, + body: JSON.stringify({ email: takenEmail }), + }); + + expect(res.status).toBe(409); + const body = (await res.json()) as any; + expect(body.success).toBe(false); + expect(body.error.code).toBe("EMAIL_TAKEN"); + }); + + it("returns 400 when the email is the same as the current one", async () => { + const { cookie, email } = await signUp(); + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { Cookie: cookie, "Content-Type": "application/json" }, + body: JSON.stringify({ email }), + }); + + expect(res.status).toBe(400); + const body = (await res.json()) as any; + expect(body.error.code).toBe("EMAIL_UNCHANGED"); + }); + + it("updates the email, sets emailVerified to false, and returns the updated user", async () => { + const oldEmail = uniqueEmail("old"); + const newEmail = uniqueEmail("new"); + const { cookie } = await signUp({ email: oldEmail }); + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { Cookie: cookie, "Content-Type": "application/json" }, + body: JSON.stringify({ email: newEmail }), + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as any; + expect(body.data.email).toBe(newEmail); + expect(body.data.emailVerified).toBe(false); + }); + + it("returns 200 with the current user when no fields are provided", async () => { + const { cookie, email } = await signUp(); + const res = await app.request("/api/users/me", { + method: "PATCH", + headers: { Cookie: cookie, "Content-Type": "application/json" }, + body: JSON.stringify({}), + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as any; + expect(body.data.email).toBe(email); + }); }); // ─── GET /api/users/:id ─────────────────────────────────────────────────────── describe("GET /api/users/:id", () => { - it("returns 401 when not authenticated", async () => { - const res = await app.request("/api/users/some-id"); - expect(res.status).toBe(401); - }); - - it("returns 403 when authenticated as a regular user", async () => { - const { cookie } = await signUp(); - const res = await app.request("/api/users/some-id", { - headers: { Cookie: cookie }, - }); - - expect(res.status).toBe(403); - const body = (await res.json()) as any; - expect(body.error.code).toBe("FORBIDDEN"); - }); - - it("returns 404 when the user does not exist (admin)", async () => { - const { email } = await signUp(); - await makeAdmin(email); - // Re-sign in to get a fresh session with the updated role - const freshCookie = await signIn(email, "password-secret-123"); - - const res = await app.request("/api/users/nonexistent", { - headers: { Cookie: freshCookie }, - }); - - expect(res.status).toBe(404); - }); - - it("returns the target user when authenticated as admin", async () => { - // Create the target user - const { email: targetEmail } = await signUp({ - email: "target@example.com", - name: "Target", - }); - // Fetch the target's id from DB - const target = await db.query.users.findFirst({ - where: eq(schema.users.email, targetEmail), - }); - - // Create and elevate the admin - const { email: adminEmail } = await signUp({ - email: "admin@example.com", - name: "Admin", - }); - await makeAdmin(adminEmail); - const adminCookie = await signIn(adminEmail, "password-secret-123"); - - const res = await app.request(`/api/users/${target?.id}`, { - headers: { Cookie: adminCookie }, - }); - - expect(res.status).toBe(200); - const body = (await res.json()) as any; - expect(body.data.email).toBe(targetEmail); - }); + it("returns 401 when not authenticated", async () => { + const res = await app.request("/api/users/some-id"); + expect(res.status).toBe(401); + }); + + it("returns 403 when authenticated as a regular user", async () => { + const { cookie } = await signUp(); + const res = await app.request("/api/users/some-id", { + headers: { Cookie: cookie }, + }); + + expect(res.status).toBe(403); + const body = (await res.json()) as any; + expect(body.error.code).toBe("FORBIDDEN"); + }); + + it("returns 404 when the user does not exist (admin)", async () => { + const { email } = await signUp(); + await makeAdmin(email); + // Re-sign in to get a fresh session with the updated role + const freshCookie = await signIn(email, "password-secret-123"); + + const res = await app.request("/api/users/nonexistent", { + headers: { Cookie: freshCookie }, + }); + + expect(res.status).toBe(404); + }); + + it("returns the target user when authenticated as admin", async () => { + // Create the target user + const { email: targetEmail } = await signUp({ + email: uniqueEmail("target"), + name: "Target", + }); + // Fetch the target's id from DB + const target = await db.query.users.findFirst({ + where: eq(schema.users.email, targetEmail), + }); + + // Create and elevate the admin + const { email: adminEmail } = await signUp({ + email: uniqueEmail("admin"), + name: "Admin", + }); + await makeAdmin(adminEmail); + const adminCookie = await signIn(adminEmail, "password-secret-123"); + + const res = await app.request(`/api/users/${target?.id}`, { + headers: { Cookie: adminCookie }, + }); + + expect(res.status).toBe(200); + const body = (await res.json()) as any; + expect(body.data.email).toBe(targetEmail); + }); }); diff --git a/apps/backend/src/modules/users/__tests__/users.repository.test.ts b/apps/backend/src/modules/users/__tests__/users.repository.test.ts index 7469db6..4e23c17 100644 --- a/apps/backend/src/modules/users/__tests__/users.repository.test.ts +++ b/apps/backend/src/modules/users/__tests__/users.repository.test.ts @@ -6,126 +6,137 @@ // Each test is isolated: afterEach cleans out the users table. // Sessions and accounts cascade on user delete, so no manual cleanup needed. +import { inArray } from "drizzle-orm"; import { users } from "@repo/db/schema"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterAll, afterEach, describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import { db } from "@/db"; import { - createUser, - findUserByEmail, - findUserById, - updateUser, -} from "../users.repository"; + createUser, + findUserByEmail, + findUserById, + updateUser, +} from "../users.repository.ts"; + +const FIXTURE_IDS = ["test-user-alice", "test-user-bob"]; afterEach(async () => { - await db.delete(users); + await db.delete(users).where(inArray(users.id, FIXTURE_IDS)); +}); + +afterAll(async () => { + await (db as any).$client.end(); }); // ─── Fixtures ───────────────────────────────────────────────────────────────── const alice = { - id: "test-user-alice", - email: "alice@example.com", - name: "Alice", - emailVerified: false as const, + id: "test-user-alice", + email: "alice@example.com", + name: "Alice", + emailVerified: false as const, }; const bob = { - id: "test-user-bob", - email: "bob@example.com", - name: "Bob", - emailVerified: false as const, + id: "test-user-bob", + email: "bob@example.com", + name: "Bob", + emailVerified: false as const, }; // ─── findUserById ───────────────────────────────────────────────────────────── describe("findUserById", () => { - it("returns USER_NOT_FOUND when no row exists", async () => { - const result = await findUserById("nonexistent"); - expect(result).toEqual({ - ok: false, - error: { type: "USER_NOT_FOUND", lookup: "nonexistent" }, - }); - }); - - it("returns the user when the row exists", async () => { - await db.insert(users).values(alice); - const result = await findUserById(alice.id); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.value.id).toBe(alice.id); - expect(result.value.email).toBe(alice.email); - } - }); + it("returns USER_NOT_FOUND when no row exists", async () => { + const result = await findUserById("nonexistent"); + expect(result).toEqual({ + ok: false, + error: { type: "USER_NOT_FOUND", lookup: "nonexistent" }, + }); + }); + + it("returns the user when the row exists", async () => { + await db.insert(users).values(alice); + const result = await findUserById(alice.id); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.id).toBe(alice.id); + expect(result.value.email).toBe(alice.email); + } + }); }); // ─── findUserByEmail ────────────────────────────────────────────────────────── describe("findUserByEmail", () => { - it("returns USER_NOT_FOUND when no row exists", async () => { - const result = await findUserByEmail("nobody@example.com"); - expect(result).toEqual({ - ok: false, - error: { type: "USER_NOT_FOUND", lookup: "nobody@example.com" }, - }); - }); - - it("returns the user when the row exists", async () => { - await db.insert(users).values(alice); - const result = await findUserByEmail(alice.email); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.value.email).toBe(alice.email); - } - }); + it("returns USER_NOT_FOUND when no row exists", async () => { + const result = await findUserByEmail("nobody@example.com"); + expect(result).toEqual({ + ok: false, + error: { type: "USER_NOT_FOUND", lookup: "nobody@example.com" }, + }); + }); + + it("returns the user when the row exists", async () => { + await db.insert(users).values(alice); + const result = await findUserByEmail(alice.email); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.email).toBe(alice.email); + } + }); }); // ─── createUser ─────────────────────────────────────────────────────────────── describe("createUser", () => { - it("inserts and returns the new user", async () => { - const result = await createUser(alice); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.value.email).toBe(alice.email); - expect(result.value.name).toBe(alice.name); - } - }); - - it("returns EMAIL_TAKEN when the email is already registered", async () => { - await createUser(alice); - const result = await createUser({ ...bob, email: alice.email }); - expect(result).toEqual({ - ok: false, - error: { type: "EMAIL_TAKEN", email: alice.email }, - }); - }); - - it("does not insert a duplicate when EMAIL_TAKEN is returned", async () => { - await createUser(alice); - await createUser({ ...bob, email: alice.email }); - const rows = await db.select().from(users); - expect(rows).toHaveLength(1); - }); + it("inserts and returns the new user", async () => { + const result = await createUser(alice); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.email).toBe(alice.email); + expect(result.value.name).toBe(alice.name); + } + }); + + it("returns EMAIL_TAKEN when the email is already registered", async () => { + await createUser(alice); + const result = await createUser({ ...bob, email: alice.email }); + expect(result).toEqual({ + ok: false, + error: { type: "EMAIL_TAKEN", email: alice.email }, + }); + }); + + it("does not insert a duplicate when EMAIL_TAKEN is returned", async () => { + await createUser(alice); + await createUser({ ...bob, email: alice.email }); + const rows = await db + .select() + .from(users) + .where(inArray(users.id, FIXTURE_IDS)); + expect(rows).toHaveLength(1); + }); }); // ─── updateUser ─────────────────────────────────────────────────────────────── describe("updateUser", () => { - it("updates and returns the user", async () => { - await db.insert(users).values(alice); - const result = await updateUser(alice.id, { name: "Alice Updated" }); - expect(result.ok).toBe(true); - if (result.ok) { - expect(result.value.name).toBe("Alice Updated"); - expect(result.value.id).toBe(alice.id); - } - }); - - it("returns USER_NOT_FOUND when no row matches the id", async () => { - const result = await updateUser("nonexistent", { name: "Ghost" }); - expect(result).toEqual({ - ok: false, - error: { type: "USER_NOT_FOUND", lookup: "nonexistent" }, - }); - }); + it("updates and returns the user", async () => { + await db.insert(users).values(alice); + const result = await updateUser(alice.id, { name: "Alice Updated" }); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.name).toBe("Alice Updated"); + expect(result.value.id).toBe(alice.id); + } + }); + + it("returns USER_NOT_FOUND when no row matches the id", async () => { + const result = await updateUser("nonexistent", { name: "Ghost" }); + expect(result).toEqual({ + ok: false, + error: { type: "USER_NOT_FOUND", lookup: "nonexistent" }, + }); + }); }); diff --git a/apps/backend/src/modules/users/__tests__/users.usecases.test.ts b/apps/backend/src/modules/users/__tests__/users.usecases.test.ts index 77a25b1..c3ac7e1 100644 --- a/apps/backend/src/modules/users/__tests__/users.usecases.test.ts +++ b/apps/backend/src/modules/users/__tests__/users.usecases.test.ts @@ -2,55 +2,59 @@ // Pure functions — no DB, no network, no mocks. Call with plain values. import type { User } from "@repo/db/schema"; -import { describe, expect, it } from "vitest"; -import { prepareEmailChange } from "../users.usecases"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; +import { prepareEmailChange } from "../users.usecases.ts"; // ─── Fixture ───────────────────────────────────────────────────────────────── const baseUser: User = { - id: "user-1", - email: "alice@example.com", - name: "Alice", - image: null, - role: "user", - emailVerified: true, - createdAt: new Date("2024-01-01"), - updatedAt: new Date("2024-01-01"), + id: "user-1", + email: "alice@example.com", + name: "Alice", + image: null, + role: "buyer", + emailVerified: true, + twoFactorEnabled: false, + twoFactorSecret: null, + backupCodes: null, + createdAt: new Date("2024-01-01"), + updatedAt: new Date("2024-01-01"), }; // ─── prepareEmailChange ─────────────────────────────────────────────────────── describe("prepareEmailChange", () => { - it("returns the new email and resets emailVerified when the email changes", () => { - const result = prepareEmailChange(baseUser, "bob@example.com"); - expect(result).toEqual({ - ok: true, - value: { email: "bob@example.com", emailVerified: false }, - }); - }); - - it("returns EMAIL_UNCHANGED when the new email matches the current one", () => { - const result = prepareEmailChange(baseUser, "alice@example.com"); - expect(result).toEqual({ - ok: false, - error: { type: "EMAIL_UNCHANGED", email: "alice@example.com" }, - }); - }); - - it("always sets emailVerified to false on a successful change", () => { - // Even if the user already had emailVerified: false, it must stay false. - const unverified = { ...baseUser, emailVerified: false }; - const result = prepareEmailChange(unverified, "new@example.com"); - expect(result.ok).toBe(true); - if (result.ok) expect(result.value.emailVerified).toBe(false); - }); - - it("does not carry forward any other user fields", () => { - // The return value is only { email, emailVerified } — no id, name, role leakage. - const result = prepareEmailChange(baseUser, "new@example.com"); - expect(result.ok).toBe(true); - if (result.ok) { - expect(Object.keys(result.value)).toEqual(["email", "emailVerified"]); - } - }); + it("returns the new email and resets emailVerified when the email changes", () => { + const result = prepareEmailChange(baseUser, "bob@example.com"); + expect(result).toEqual({ + ok: true, + value: { email: "bob@example.com", emailVerified: false }, + }); + }); + + it("returns EMAIL_UNCHANGED when the new email matches the current one", () => { + const result = prepareEmailChange(baseUser, "alice@example.com"); + expect(result).toEqual({ + ok: false, + error: { type: "EMAIL_UNCHANGED", email: "alice@example.com" }, + }); + }); + + it("always sets emailVerified to false on a successful change", () => { + // Even if the user already had emailVerified: false, it must stay false. + const unverified = { ...baseUser, emailVerified: false }; + const result = prepareEmailChange(unverified, "new@example.com"); + expect(result.ok).toBe(true); + if (result.ok) expect(result.value.emailVerified).toBe(false); + }); + + it("does not carry forward any other user fields", () => { + // The return value is only { email, emailVerified } — no id, name, role leakage. + const result = prepareEmailChange(baseUser, "new@example.com"); + expect(result.ok).toBe(true); + if (result.ok) { + expect(Object.keys(result.value)).toEqual(["email", "emailVerified"]); + } + }); }); diff --git a/apps/backend/src/modules/users/handlers.ts b/apps/backend/src/modules/users/handlers.ts index 9b38814..f716ed7 100644 --- a/apps/backend/src/modules/users/handlers.ts +++ b/apps/backend/src/modules/users/handlers.ts @@ -15,45 +15,50 @@ import type { InsertUser } from "@repo/db/schema"; import { match } from "@repo/shared"; import type { AppRouteHandler } from "@/lib/types"; import { - BAD_REQUEST, - CONFLICT, - FORBIDDEN, - failure, - INTERNAL_SERVER_ERROR, - isInfraError, - NOT_FOUND, - OK, - success, + BAD_REQUEST, + CONFLICT, + failure, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + isInfraError, + NOT_FOUND, + OK, + success, } from "@/lib/types"; -import type { GetMeRoute, GetUserByIdRoute, UpdateMeRoute } from "./routes"; -import { findUserByEmail, findUserById, updateUser } from "./users.repository"; -import { prepareEmailChange } from "./users.usecases"; +import type { GetMeRoute, GetUserByIdRoute, UpdateMeRoute } from "./routes.ts"; +import { + findUserByEmail, + findUserById, + updateUser, +} from "./users.repository.ts"; +import { prepareEmailChange } from "./users.usecases.ts"; // GET /users/me // // Simple: load the current user from the DB and return it. // Shows the standard match + switch pattern for a single-error repository call. export const getMeHandler: AppRouteHandler = async (c) => { - const userId = c.get("session").userId; - const result = await findUserById(userId); + const userId = c.get("session").userId; + const result = await findUserById(userId); - return match(result, { - ok: (user) => c.json(success(user), OK), - err: (e) => { - if (isInfraError(e)) - return c.json( - failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), - INTERNAL_SERVER_ERROR, - ); - switch (e.type) { - case "USER_NOT_FOUND": - return c.json( - failure({ code: "NOT_FOUND", message: "User not found" }), - NOT_FOUND, - ); - } - }, - }); + return match(result, { + ok: (user) => c.json(success(user), OK), + err: (e) => { + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } + switch (e.type) { + case "USER_NOT_FOUND": + return c.json( + failure({ code: "NOT_FOUND", message: "User not found" }), + NOT_FOUND, + ); + } + }, + }); }; // PATCH /users/me @@ -70,90 +75,92 @@ export const getMeHandler: AppRouteHandler = async (c) => { // - Ensures the email verification flag is reset. // - Pure function: receives data, returns Result — no DB calls. export const updateMeHandler: AppRouteHandler = async (c) => { - const userId = c.get("user").id; - const body = c.req.valid("json"); + const userId = c.get("user").id; + const body = c.req.valid("json"); - // Load current user — the use-case needs it to apply business rules. - const current = await findUserById(userId); - if (!current.ok) { - if (isInfraError(current.error)) - return c.json( - failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), - INTERNAL_SERVER_ERROR, - ); - return c.json( - failure({ code: "NOT_FOUND", message: "User not found" }), - NOT_FOUND, - ); - } + // Load current user — the use-case needs it to apply business rules. + const current = await findUserById(userId); + if (!current.ok) { + if (isInfraError(current.error)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } + return c.json( + failure({ code: "NOT_FOUND", message: "User not found" }), + NOT_FOUND, + ); + } - // If an email change is requested, run the use-case and check availability. - let emailFields: { email: string; emailVerified: false } | undefined; - if (body.email !== undefined) { - // Pure rule: reject no-ops and derive the fields to persist. - const prepared = prepareEmailChange(current.value, body.email); - if (!prepared.ok) { - return c.json( - failure({ - code: "EMAIL_UNCHANGED", - message: "Provided email is the same as current", - }), - BAD_REQUEST, - ); - } + // If an email change is requested, run the use-case and check availability. + let emailFields: { email: string; emailVerified: false } | undefined; + if (body.email !== undefined) { + // Pure rule: reject no-ops and derive the fields to persist. + const prepared = prepareEmailChange(current.value, body.email); + if (!prepared.ok) { + return c.json( + failure({ + code: "EMAIL_UNCHANGED", + message: "Provided email is the same as current", + }), + BAD_REQUEST, + ); + } - // Imperative check: is the new email available? - // findUserByEmail returns ok if email IS taken, err(USER_NOT_FOUND) if it's free. - const emailCheck = await findUserByEmail(body.email); - if (emailCheck.ok) { - return c.json( - failure({ - code: "EMAIL_TAKEN", - message: "Email address is already in use", - }), - CONFLICT, - ); - } - if (isInfraError(emailCheck.error)) { - return c.json( - failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), - INTERNAL_SERVER_ERROR, - ); - } - // emailCheck.error.type === "USER_NOT_FOUND" — email is available. - emailFields = prepared.value; - } + // Imperative check: is the new email available? + // findUserByEmail returns ok if email IS taken, err(USER_NOT_FOUND) if it's free. + const emailCheck = await findUserByEmail(body.email); + if (emailCheck.ok) { + return c.json( + failure({ + code: "EMAIL_TAKEN", + message: "Email address is already in use", + }), + CONFLICT, + ); + } + if (isInfraError(emailCheck.error)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } + // emailCheck.error.type === "USER_NOT_FOUND" — email is available. + emailFields = prepared.value; + } - // Build the update payload. Spreading undefined = {} so emailFields is safe. - const updatePayload: Partial = { - ...(body.name !== undefined && { name: body.name }), - ...(body.image !== undefined && { image: body.image }), - ...emailFields, - }; + // Build the update payload. Spreading undefined = {} so emailFields is safe. + const updatePayload: Partial = { + ...(body.name !== undefined && { name: body.name }), + ...(body.image !== undefined && { image: body.image }), + ...emailFields, + }; - // Nothing to update — return current user without a DB roundtrip. - if (Object.keys(updatePayload).length === 0) { - return c.json(success(current.value), OK); - } + // Nothing to update — return current user without a DB roundtrip. + if (Object.keys(updatePayload).length === 0) { + return c.json(success(current.value), OK); + } - const result = await updateUser(userId, updatePayload); - return match(result, { - ok: (user) => c.json(success(user), OK), - err: (e) => { - if (isInfraError(e)) - return c.json( - failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), - INTERNAL_SERVER_ERROR, - ); - switch (e.type) { - case "USER_NOT_FOUND": - return c.json( - failure({ code: "NOT_FOUND", message: "User not found" }), - NOT_FOUND, - ); - } - }, - }); + const result = await updateUser(userId, updatePayload); + return match(result, { + ok: (user) => c.json(success(user), OK), + err: (e) => { + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } + switch (e.type) { + case "USER_NOT_FOUND": + return c.json( + failure({ code: "NOT_FOUND", message: "User not found" }), + NOT_FOUND, + ); + } + }, + }); }; // GET /users/:id (admin only) @@ -162,34 +169,35 @@ export const updateMeHandler: AppRouteHandler = async (c) => { // rather than via a path-level middleware, avoiding ambiguous path matching // with /users/me. All other logic is the same single-error match pattern. export const getUserByIdHandler: AppRouteHandler = async ( - c, + c, ) => { - const currentUser = c.get("user"); - if (currentUser.role !== "admin") { - return c.json( - failure({ code: "FORBIDDEN", message: "Admin access required" }), - FORBIDDEN, - ); - } + const currentUser = c.get("user"); + if (currentUser.role !== "admin") { + return c.json( + failure({ code: "FORBIDDEN", message: "Admin access required" }), + FORBIDDEN, + ); + } - const { id } = c.req.valid("param"); - const result = await findUserById(id); + const { id } = c.req.valid("param"); + const result = await findUserById(id); - return match(result, { - ok: (user) => c.json(success(user), OK), - err: (e) => { - if (isInfraError(e)) - return c.json( - failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), - INTERNAL_SERVER_ERROR, - ); - switch (e.type) { - case "USER_NOT_FOUND": - return c.json( - failure({ code: "NOT_FOUND", message: "User not found" }), - NOT_FOUND, - ); - } - }, - }); + return match(result, { + ok: (user) => c.json(success(user), OK), + err: (e) => { + if (isInfraError(e)) { + return c.json( + failure({ code: "INTERNAL_ERROR", message: "Service unavailable" }), + INTERNAL_SERVER_ERROR, + ); + } + switch (e.type) { + case "USER_NOT_FOUND": + return c.json( + failure({ code: "NOT_FOUND", message: "User not found" }), + NOT_FOUND, + ); + } + }, + }); }; diff --git a/apps/backend/src/modules/users/index.ts b/apps/backend/src/modules/users/index.ts index e916aca..040b421 100644 --- a/apps/backend/src/modules/users/index.ts +++ b/apps/backend/src/modules/users/index.ts @@ -1,10 +1,10 @@ import { createRouter } from "@/lib/create-app"; -import * as handlers from "./handlers"; -import * as routes from "./routes"; +import * as handlers from "./handlers.ts"; +import * as routes from "./routes.ts"; const router = createRouter() - .openapi(routes.getMe, handlers.getMeHandler) - .openapi(routes.updateMe, handlers.updateMeHandler) - .openapi(routes.getUserById, handlers.getUserByIdHandler); + .openapi(routes.getMe, handlers.getMeHandler) + .openapi(routes.updateMe, handlers.updateMeHandler) + .openapi(routes.getUserById, handlers.getUserByIdHandler); export default router; diff --git a/apps/backend/src/modules/users/routes.ts b/apps/backend/src/modules/users/routes.ts index dcced3d..57a31d7 100644 --- a/apps/backend/src/modules/users/routes.ts +++ b/apps/backend/src/modules/users/routes.ts @@ -2,15 +2,15 @@ import { createRoute, z } from "@hono/zod-openapi"; import { selectUserSchema } from "@repo/db/schema"; import { apiErrorSchema, apiSuccessSchema, idParamSchema } from "@repo/shared"; import { - BAD_REQUEST, - CONFLICT, - FORBIDDEN, - INTERNAL_SERVER_ERROR, - jsonBody, - jsonRes, - NOT_FOUND, - OK, - UNAUTHORIZED, + BAD_REQUEST, + CONFLICT, + FORBIDDEN, + INTERNAL_SERVER_ERROR, + jsonBody, + jsonRes, + NOT_FOUND, + OK, + UNAUTHORIZED, } from "@/lib/types"; const tags = ["Users"]; @@ -19,9 +19,9 @@ const userResponseSchema = apiSuccessSchema(selectUserSchema); // PATCH /users/me body const updateMeBodySchema = z.object({ - name: z.string().min(1).max(255).optional(), - image: z.url().optional(), - email: z.email().optional(), + name: z.string().min(1).max(255).optional(), + image: z.url().optional(), + email: z.email().optional(), }); // Shared error responses used across all three routes @@ -32,53 +32,53 @@ const e500 = jsonRes(apiErrorSchema, "Internal server error"); // ─── Routes ───────────────────────────────────────────────────────────────── export const getMe = createRoute({ - method: "get", - path: "/users/me", - tags, - summary: "Get current user", - description: "Returns the authenticated user's profile.", - responses: { - [OK]: jsonRes(userResponseSchema, "Current user profile"), - [UNAUTHORIZED]: e401, - [NOT_FOUND]: e404, - [INTERNAL_SERVER_ERROR]: e500, - }, + method: "get", + path: "/users/me", + tags, + summary: "Get current user", + description: "Returns the authenticated user's profile.", + responses: { + [OK]: jsonRes(userResponseSchema, "Current user profile"), + [UNAUTHORIZED]: e401, + [NOT_FOUND]: e404, + [INTERNAL_SERVER_ERROR]: e500, + }, }); export const updateMe = createRoute({ - method: "patch", - path: "/users/me", - tags, - summary: "Update current user", - description: - "Update name, image, or email. Changing email resets email verification and requires the address to be available.", - request: { - body: jsonBody(updateMeBodySchema), - }, - responses: { - [OK]: jsonRes(userResponseSchema, "Updated profile"), - [BAD_REQUEST]: jsonRes(apiErrorSchema, "Email is unchanged"), - [UNAUTHORIZED]: e401, - [NOT_FOUND]: e404, - [CONFLICT]: jsonRes(apiErrorSchema, "Email already taken"), - [INTERNAL_SERVER_ERROR]: e500, - }, + method: "patch", + path: "/users/me", + tags, + summary: "Update current user", + description: + "Update name, image, or email. Changing email resets email verification and requires the address to be available.", + request: { + body: jsonBody(updateMeBodySchema), + }, + responses: { + [OK]: jsonRes(userResponseSchema, "Updated profile"), + [BAD_REQUEST]: jsonRes(apiErrorSchema, "Email is unchanged"), + [UNAUTHORIZED]: e401, + [NOT_FOUND]: e404, + [CONFLICT]: jsonRes(apiErrorSchema, "Email already taken"), + [INTERNAL_SERVER_ERROR]: e500, + }, }); export const getUserById = createRoute({ - method: "get", - path: "/users/:id", - tags, - summary: "Get user by ID", - description: "Admin only. Fetch any user's profile by their ID.", - request: { params: idParamSchema }, - responses: { - [OK]: jsonRes(userResponseSchema, "User found"), - [UNAUTHORIZED]: e401, - [FORBIDDEN]: jsonRes(apiErrorSchema, "Forbidden — admin access required"), - [NOT_FOUND]: e404, - [INTERNAL_SERVER_ERROR]: e500, - }, + method: "get", + path: "/users/:id", + tags, + summary: "Get user by ID", + description: "Admin only. Fetch any user's profile by their ID.", + request: { params: idParamSchema }, + responses: { + [OK]: jsonRes(userResponseSchema, "User found"), + [UNAUTHORIZED]: e401, + [FORBIDDEN]: jsonRes(apiErrorSchema, "Forbidden — admin access required"), + [NOT_FOUND]: e404, + [INTERNAL_SERVER_ERROR]: e500, + }, }); // ─── Types ─────────────────────────────────────────────────────────────────── diff --git a/apps/backend/src/modules/users/users.repository.ts b/apps/backend/src/modules/users/users.repository.ts index 2190397..e1e4600 100644 --- a/apps/backend/src/modules/users/users.repository.ts +++ b/apps/backend/src/modules/users/users.repository.ts @@ -12,66 +12,68 @@ import { eq } from "drizzle-orm"; import { db } from "@/db"; import { InfrastructureError } from "@/lib/error"; import { tryInfra } from "@/lib/infra"; -import type { EmailTaken, UserNotFound } from "./users.errors"; +import type { EmailTaken, UserNotFound } from "./users.errors.ts"; export async function findUserById( - id: string, + id: string, ): Promise> { - const result = await tryInfra(`fetch user ${id}`, () => - db.query.users.findFirst({ where: eq(users.id, id) }), - ); - if (!result.ok) return result; - if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: id }); - return ok(result.value); + const result = await tryInfra( + `fetch user ${id}`, + () => db.query.users.findFirst({ where: eq(users.id, id) }), + ); + if (!result.ok) return result; + if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: id }); + return ok(result.value); } export async function findUserByEmail( - email: string, + email: string, ): Promise> { - const result = await tryInfra(`fetch user by email`, () => - db.query.users.findFirst({ where: eq(users.email, email) }), - ); - if (!result.ok) return result; - if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: email }); - return ok(result.value); + const result = await tryInfra( + `fetch user by email`, + () => db.query.users.findFirst({ where: eq(users.email, email) }), + ); + if (!result.ok) return result; + if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: email }); + return ok(result.value); } export async function createUser( - data: InsertUser, + data: InsertUser, ): Promise> { - // Check for existing email first — turns a DB constraint error into a typed domain error. - const existing = await tryInfra(`check email ${data.email}`, () => - db.query.users.findFirst({ where: eq(users.email, data.email) }), - ); - if (!existing.ok) return existing; - if (existing.value) return err({ type: "EMAIL_TAKEN", email: data.email }); + // Check for existing email first — turns a DB constraint error into a typed domain error. + const existing = await tryInfra( + `check email ${data.email}`, + () => db.query.users.findFirst({ where: eq(users.email, data.email) }), + ); + if (!existing.ok) return existing; + if (existing.value) return err({ type: "EMAIL_TAKEN", email: data.email }); - const result = await tryInfra("create user", () => - db - .insert(users) - .values(data) - .returning() - .then((rows) => rows[0]), - ); - if (!result.ok) return result; - if (!result.value) - return err(new InfrastructureError("Insert returned no rows")); - return ok(result.value); + const result = await tryInfra("create user", () => + db + .insert(users) + .values(data) + .returning() + .then((rows) => rows[0])); + if (!result.ok) return result; + if (!result.value) { + return err(new InfrastructureError("Insert returned no rows")); + } + return ok(result.value); } export async function updateUser( - id: string, - data: Partial, + id: string, + data: Partial, ): Promise> { - const result = await tryInfra(`update user ${id}`, () => - db - .update(users) - .set(data) - .where(eq(users.id, id)) - .returning() - .then((rows) => rows[0]), - ); - if (!result.ok) return result; - if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: id }); - return ok(result.value); + const result = await tryInfra(`update user ${id}`, () => + db + .update(users) + .set(data) + .where(eq(users.id, id)) + .returning() + .then((rows) => rows[0])); + if (!result.ok) return result; + if (!result.value) return err({ type: "USER_NOT_FOUND", lookup: id }); + return ok(result.value); } diff --git a/apps/backend/src/modules/users/users.usecases.ts b/apps/backend/src/modules/users/users.usecases.ts index edf16cf..704d650 100644 --- a/apps/backend/src/modules/users/users.usecases.ts +++ b/apps/backend/src/modules/users/users.usecases.ts @@ -5,7 +5,7 @@ // Testable by calling with plain values — no mocks, no DB setup. import type { User } from "@repo/db/schema"; import { err, ok, type Result } from "@repo/shared"; -import type { EmailUnchanged } from "./users.errors"; +import type { EmailUnchanged } from "./users.errors.ts"; // Prepare an email change for a user. // @@ -16,11 +16,12 @@ import type { EmailUnchanged } from "./users.errors"; // The handler is responsible for checking whether the new email is available // (findUserByEmail) before applying this result to updateUser. export function prepareEmailChange( - user: User, - newEmail: string, + user: User, + newEmail: string, ): Result<{ email: string; emailVerified: false }, EmailUnchanged> { - if (user.email === newEmail) - return err({ type: "EMAIL_UNCHANGED", email: newEmail }); + if (user.email === newEmail) { + return err({ type: "EMAIL_UNCHANGED", email: newEmail }); + } - return ok({ email: newEmail, emailVerified: false as const }); + return ok({ email: newEmail, emailVerified: false as const }); } diff --git a/apps/backend/src/routes/index.ts b/apps/backend/src/routes/index.ts index 5f9110a..236a22b 100644 --- a/apps/backend/src/routes/index.ts +++ b/apps/backend/src/routes/index.ts @@ -6,6 +6,6 @@ export const publicRoutes = [health]; // Protected routes (auth required — authMiddleware applied in app.ts) export const routes = [ - users, - // posts, + users, + // posts, ]; diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json deleted file mode 100644 index 1dc5371..0000000 --- a/apps/backend/tsconfig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "target": "ESNext", - "jsx": "react-jsx", - "jsxImportSource": "react", - "module": "ESNext", - "moduleResolution": "Bundler", - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "typeRoots": ["./node_modules/@types"], - "types": ["node"], - "strict": true, - "outDir": "./dist", - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - }, - "tsc-alias": { - "resolveFullPaths": true, - "baseUrl": "./", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["src", "globals.d.ts", "../../packages/db/seedCore.ts"], - "references": [ - { "path": "../../packages/db" }, - { "path": "../../packages/shared" } - ] -} diff --git a/apps/backend/vitest.config.ts b/apps/backend/vitest.config.ts deleted file mode 100644 index 950472c..0000000 --- a/apps/backend/vitest.config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import path from "node:path"; -import { defineConfig } from "vitest/config"; - -const repo = (pkg: string) => path.resolve(__dirname, "../../packages", pkg); - -export default defineConfig({ - test: { - globals: true, - environment: "node", - include: ["src/**/*.test.ts"], - coverage: { - reporter: ["text", "json", "html"], - }, - }, - resolve: { - alias: { - // Internal path alias - "@": path.resolve(__dirname, "./src"), - // Workspace packages — point straight to source so no build step is needed - "@repo/shared": repo("shared/src/index.ts"), - "@repo/db/schema": repo("db/src/schema/index.ts"), - "@repo/db/types": repo("db/src/types.ts"), - "@repo/db": repo("db/src/index.ts"), - }, - }, -}); diff --git a/apps/frontend/Caddyfile b/apps/frontend/Caddyfile new file mode 100644 index 0000000..64bd2c5 --- /dev/null +++ b/apps/frontend/Caddyfile @@ -0,0 +1,6 @@ +:80 { + root * /srv + file_server + + reverse_proxy /api/* backend:9999 +} diff --git a/apps/frontend/Dockerfile b/apps/frontend/Dockerfile new file mode 100644 index 0000000..74a3e40 --- /dev/null +++ b/apps/frontend/Dockerfile @@ -0,0 +1,30 @@ +# ─── Build ───────────────────────────────────────────────────────────── +FROM denoland/deno:debian-2.7.14 AS builder + +WORKDIR /app + +# Layer 1: Workspace skeleton (changes infrequently — preserves dep cache). +# Deno requires every deno.json under the workspace root to be a declared +# member (see docs/DENO_WORKSPACE_SCOPE.md), and `deno install` needs the +# full workspace + lockfile present to resolve pinned versions instead of +# silently re-resolving them — copying just apps/frontend's own files let +# npm deps drift on every build. +COPY deno.json deno.lock package.json ./ +COPY apps/backend/deno.json ./apps/backend/ +COPY apps/frontend/deno.json apps/frontend/package.json ./apps/frontend/ +COPY packages/shared/deno.json ./packages/shared/ +COPY packages/db/deno.json ./packages/db/ +COPY packages/email-templates/deno.json ./packages/email-templates/ +RUN cd apps/frontend && deno install + +# Layer 2: Frontend source +COPY apps/frontend/ ./apps/frontend/ +RUN cd apps/frontend && deno task build + +# ─── Run (Caddy serves SPA + proxies /api/* to backend) ────────────── +FROM caddy:alpine + +COPY --from=builder /app/apps/frontend/dist/ /srv/ +COPY apps/frontend/Caddyfile /etc/caddy/Caddyfile + +EXPOSE 80 diff --git a/apps/frontend/deno.json b/apps/frontend/deno.json new file mode 100644 index 0000000..a8889c8 --- /dev/null +++ b/apps/frontend/deno.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "react", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "types": ["vite/client"] + }, + "imports": { + "@/": "./src/" + }, + "exclude": ["node_modules", "dist", ".tanstack"], + "tasks": { + "dev": "deno run -A npm:vite dev --port 3000", + "build": "deno run -A npm:vite build", + "preview": "deno run -A npm:vite preview", + "typecheck": "tsc --noEmit", + "test": "deno test -A" + }, + "test": { + "include": ["src/**/*.test.ts", "src/**/*.test.tsx"] + }, + "lint": { + "rules": { + "exclude": ["no-explicit-any", "no-non-null-assertion"] + } + } +} diff --git a/apps/frontend/deno.lock b/apps/frontend/deno.lock new file mode 100644 index 0000000..5a7e5fa --- /dev/null +++ b/apps/frontend/deno.lock @@ -0,0 +1,3546 @@ +{ + "version": "5", + "specifiers": { + "npm:@base-ui/react@^1.2.0": "1.4.1_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@better-fetch/fetch@^1.1.18": "1.1.21", + "npm:@tailwindcss/vite@^4.1.17": "4.3.0_vite@7.3.3", + "npm:@tanstack/react-form@1": "1.31.0_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@tanstack/react-query@^5.90.7": "5.100.9_react@19.2.6", + "npm:@tanstack/react-router-devtools@^1.134.13": "1.166.13_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_@tanstack+router-core@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6_csstype@3.2.3", + "npm:@tanstack/react-router@^1.134.13": "1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@tanstack/router-plugin@^1.134.14": "1.167.35_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_vite@7.3.3_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@types/react-dom@^19.1.9": "19.2.3_@types+react@19.2.14", + "npm:@types/react@^19.1.16": "19.2.14", + "npm:@vitejs/plugin-react@^5.0.4": "5.2.0_vite@7.3.3", + "npm:class-variance-authority@~0.7.1": "0.7.1", + "npm:clsx@^2.1.1": "2.1.1", + "npm:lucide-react@0.553": "0.553.0_react@19.2.6", + "npm:react-dom@^19.1.1": "19.2.6_react@19.2.6", + "npm:react@^19.1.1": "19.2.6", + "npm:sonner@^2.0.7": "2.0.7_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:tailwind-merge@^3.3.1": "3.5.0", + "npm:tailwindcss@^4.1.17": "4.3.0", + "npm:typescript@~5.9.3": "5.9.3", + "npm:vite@^7.1.7": "7.3.3", + "npm:zustand@^5.0.8": "5.0.13_@types+react@19.2.14_react@19.2.6" + }, + "npm": { + "@asteasolutions/zod-to-openapi@8.5.0_zod@4.4.3": { + "integrity": "sha512-SABbKiObg5dLRiTFnqiW1WWwGcg1BJfmHtT2asIBnBHg6Smy/Ms2KHc650+JI4Hw7lSkdiNebEGXpwoxfben8Q==", + "dependencies": [ + "openapi3-ts", + "zod@4.4.3" + ] + }, + "@aws-crypto/crc32@5.2.0": { + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/crc32c@5.2.0": { + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/sha1-browser@5.2.0": { + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dependencies": [ + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/types", + "@aws-sdk/util-locate-window", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-crypto/sha256-browser@5.2.0": { + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": [ + "@aws-crypto/sha256-js", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/types", + "@aws-sdk/util-locate-window", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-crypto/sha256-js@5.2.0": { + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/supports-web-crypto@5.2.0": { + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": [ + "tslib" + ] + }, + "@aws-crypto/util@5.2.0": { + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-sdk/client-s3@3.1045.0": { + "integrity": "sha512-fsuO3Y6t+3Ro9Bsg41DKj4Sfy53CGSrhnMldNplWmG8Tx0UbYk+YDa4RD1hVlJpERw4JBmPkl0+J9qlxMh1pcA==", + "dependencies": [ + "@aws-crypto/sha1-browser", + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/core", + "@aws-sdk/credential-provider-node", + "@aws-sdk/middleware-bucket-endpoint", + "@aws-sdk/middleware-expect-continue", + "@aws-sdk/middleware-flexible-checksums", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-location-constraint", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-sdk-s3", + "@aws-sdk/middleware-ssec", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/fetch-http-handler", + "@smithy/hash-blob-browser", + "@smithy/hash-node", + "@smithy/hash-stream-node", + "@smithy/invalid-dependency", + "@smithy/md5-js", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "@smithy/util-waiter", + "tslib" + ] + }, + "@aws-sdk/core@3.974.8": { + "integrity": "sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==", + "dependencies": [ + "@aws-sdk/types", + "@aws-sdk/xml-builder", + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-base64", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/crc64-nvme@3.972.7": { + "integrity": "sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-env@3.972.34": { + "integrity": "sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-http@3.972.36": { + "integrity": "sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-stream", + "tslib" + ] + }, + "@aws-sdk/credential-provider-ini@3.972.38": { + "integrity": "sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-login", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-login@3.972.38": { + "integrity": "sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-node@3.972.39": { + "integrity": "sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==", + "dependencies": [ + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/types", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-process@3.972.34": { + "integrity": "sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-sso@3.972.38": { + "integrity": "sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/token-providers", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-web-identity@3.972.38": { + "integrity": "sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-bucket-endpoint@3.972.10": { + "integrity": "sha512-Vbc2frZH7wXlMNd+ZZSXUEs/l1Sv8Jj4zUnIfwrYF5lwaLdXHZ9xx4U3rjUcaye3HRhFVc+E5DbBxpRAbB16BA==", + "dependencies": [ + "@aws-sdk/types", + "@aws-sdk/util-arn-parser", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-config-provider", + "tslib" + ] + }, + "@aws-sdk/middleware-expect-continue@3.972.10": { + "integrity": "sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-flexible-checksums@3.974.16": { + "integrity": "sha512-6ru8doI0/XzszqLIPXf0E/V7HhAw1Pu94010XCKYtBUfD0LxF0BuOzrUf8OQGR6j2o6wgKTHUniOmndQycHwCA==", + "dependencies": [ + "@aws-crypto/crc32", + "@aws-crypto/crc32c", + "@aws-crypto/util", + "@aws-sdk/core", + "@aws-sdk/crc64-nvme", + "@aws-sdk/types", + "@smithy/is-array-buffer@4.2.2", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/middleware-host-header@3.972.10": { + "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-location-constraint@3.972.10": { + "integrity": "sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-logger@3.972.10": { + "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-recursion-detection@3.972.11": { + "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==", + "dependencies": [ + "@aws-sdk/types", + "@aws/lambda-invoke-store", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-sdk-s3@3.972.37": { + "integrity": "sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@aws-sdk/util-arn-parser", + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-config-provider", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/middleware-ssec@3.972.10": { + "integrity": "sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-user-agent@3.972.38": { + "integrity": "sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@smithy/core", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-retry", + "tslib" + ] + }, + "@aws-sdk/nested-clients@3.997.6": { + "integrity": "sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==", + "dependencies": [ + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/core", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/hash-node", + "@smithy/invalid-dependency", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/region-config-resolver@3.972.13": { + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/config-resolver", + "@smithy/node-config-provider", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/s3-request-presigner@3.1045.0": { + "integrity": "sha512-VDRF8GIuUPX+K4DUYrvcODj/h54LOmdJ7DhpLQ0wrYrdxzIiJEpi0n9jZ1bbjT2UxhwTbOorse5EGo+gnOK2aA==", + "dependencies": [ + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-format-url", + "@smithy/middleware-endpoint", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/signature-v4-multi-region@3.996.25": { + "integrity": "sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==", + "dependencies": [ + "@aws-sdk/middleware-sdk-s3", + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/token-providers@3.1041.0": { + "integrity": "sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/types@3.973.8": { + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/util-arn-parser@3.972.3": { + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "dependencies": [ + "tslib" + ] + }, + "@aws-sdk/util-endpoints@3.996.8": { + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-endpoints", + "tslib" + ] + }, + "@aws-sdk/util-format-url@3.972.10": { + "integrity": "sha512-DEKiHNJVtNxdyTeQspzY+15Po/kHm6sF0Cs4HV9Q2+lplB63+DrvdeiSoOSdWEWAoO2RcY1veoXVDz2tWxWCgQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/querystring-builder", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/util-locate-window@3.965.5": { + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dependencies": [ + "tslib" + ] + }, + "@aws-sdk/util-user-agent-browser@3.972.10": { + "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "bowser", + "tslib" + ] + }, + "@aws-sdk/util-user-agent-node@3.973.24": { + "integrity": "sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==", + "dependencies": [ + "@aws-sdk/middleware-user-agent", + "@aws-sdk/types", + "@smithy/node-config-provider", + "@smithy/types", + "@smithy/util-config-provider", + "tslib" + ] + }, + "@aws-sdk/xml-builder@3.972.22": { + "integrity": "sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==", + "dependencies": [ + "@nodable/entities", + "@smithy/types", + "fast-xml-parser", + "tslib" + ] + }, + "@aws/lambda-invoke-store@0.2.4": { + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==" + }, + "@axiomhq/js@1.6.1": { + "integrity": "sha512-iNWOnGvP+R2lIWYk9jkfvRI/rfhOR9hlWJOBJhMKwZ9mfpE0KdnFnG2XPLoMcrbyrOBKj6Jw0hpIH5GhPucLog==", + "dependencies": [ + "fetch-retry" + ] + }, + "@axiomhq/pino@1.6.1": { + "integrity": "sha512-T5mdwsrbPOkPu7dkBU/HnQvO8EoiVEO4HpqvGliDEgYv2bTVwE/ELiHep02UMVze/Gv3DEgQ/GQSIjbOqTAzYw==", + "dependencies": [ + "@axiomhq/js", + "pino-abstract-transport@1.2.0" + ] + }, + "@babel/code-frame@7.29.0": { + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dependencies": [ + "@babel/helper-validator-identifier", + "js-tokens", + "picocolors" + ] + }, + "@babel/compat-data@7.29.3": { + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==" + }, + "@babel/core@7.29.0": { + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dependencies": [ + "@babel/code-frame", + "@babel/generator", + "@babel/helper-compilation-targets", + "@babel/helper-module-transforms", + "@babel/helpers", + "@babel/parser", + "@babel/template", + "@babel/traverse", + "@babel/types", + "@jridgewell/remapping", + "convert-source-map", + "debug", + "gensync", + "json5", + "semver@6.3.1" + ] + }, + "@babel/generator@7.29.1": { + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping", + "jsesc" + ] + }, + "@babel/helper-compilation-targets@7.28.6": { + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dependencies": [ + "@babel/compat-data", + "@babel/helper-validator-option", + "browserslist", + "lru-cache", + "semver@6.3.1" + ] + }, + "@babel/helper-globals@7.28.0": { + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" + }, + "@babel/helper-module-imports@7.28.6": { + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dependencies": [ + "@babel/traverse", + "@babel/types" + ] + }, + "@babel/helper-module-transforms@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dependencies": [ + "@babel/core", + "@babel/helper-module-imports", + "@babel/helper-validator-identifier", + "@babel/traverse" + ] + }, + "@babel/helper-plugin-utils@7.28.6": { + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==" + }, + "@babel/helper-string-parser@7.27.1": { + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" + }, + "@babel/helper-validator-identifier@7.28.5": { + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" + }, + "@babel/helper-validator-option@7.27.1": { + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" + }, + "@babel/helpers@7.29.2": { + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dependencies": [ + "@babel/template", + "@babel/types" + ] + }, + "@babel/parser@7.29.3": { + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dependencies": [ + "@babel/types" + ], + "bin": true + }, + "@babel/plugin-syntax-jsx@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-syntax-typescript@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-self@7.27.1_@babel+core@7.29.0": { + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-source@7.27.1_@babel+core@7.29.0": { + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/runtime@7.29.2": { + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==" + }, + "@babel/template@7.28.6": { + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dependencies": [ + "@babel/code-frame", + "@babel/parser", + "@babel/types" + ] + }, + "@babel/traverse@7.29.0": { + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dependencies": [ + "@babel/code-frame", + "@babel/generator", + "@babel/helper-globals", + "@babel/parser", + "@babel/template", + "@babel/types", + "debug" + ] + }, + "@babel/types@7.29.0": { + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dependencies": [ + "@babel/helper-string-parser", + "@babel/helper-validator-identifier" + ] + }, + "@base-ui/react@1.4.1_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-Ab5/LIhcmL8BQcsBUYiOfkSDRdLpvgUBzMK30cu684JPcLclYlztharvCZyNNgzJtbAiREzI9q0pI5erHCMgCw==", + "dependencies": [ + "@babel/runtime", + "@base-ui/utils", + "@floating-ui/react-dom", + "@floating-ui/utils", + "@types/react", + "react", + "react-dom", + "use-sync-external-store" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@base-ui/utils@0.2.8_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ==", + "dependencies": [ + "@babel/runtime", + "@floating-ui/utils", + "@types/react", + "react", + "react-dom", + "reselect", + "use-sync-external-store" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@better-auth/core@1.6.10_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-13h/rfSGMLl7zwyOb1BSlxAQZs2nQqn/xFI/bxB7zQuS95hVgTmNbKqhHtJYkNDtuJCcjEf1sNtLBHkvaPT/vw==", + "dependencies": [ + "@better-auth/utils", + "@better-fetch/fetch", + "@opentelemetry/semantic-conventions", + "@standard-schema/spec", + "better-call", + "jose", + "kysely", + "nanostores", + "zod@4.4.3" + ] + }, + "@better-auth/drizzle-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0_postgres@3.4.9": { + "integrity": "sha512-Ax0Jlpvuu35P3U6FtUGfkLAUmBwYIF+JwwtHt+jBlOEQNIiBhbLHh8ArQxrKJFRTDYv/XoSsrvJ5OluPsUFuuQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "drizzle-orm" + ], + "optionalPeers": [ + "drizzle-orm" + ] + }, + "@better-auth/kysely-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_kysely@0.28.17_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_nanostores@1.3.0": { + "integrity": "sha512-Mp27qHgnvNCkkVEMRwhtMVpiiVFBnww0V+bunRWNU8fkxsm6H+GIBihUQOnkSCnIjV7f2HNjrf5DeYI2IhDePQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "kysely" + ], + "optionalPeers": [ + "kysely" + ] + }, + "@better-auth/memory-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-zXk7GXnOpafAjCJ3+boh8hTEmUozGCLQpCl4plH9sAix6UlMdpYmdDTEGd+I8zungiEK+jzw4oevy7IirzKrrw==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/mongo-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-EkbK3j8qwE9STteWoUh7vkve7n7/jSlkI0e9onAwr3YuE+an8scG7BgTHoDXku2qPlVa+KFPmcWc1FX6K/N6xA==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/prisma-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-p/eJXl/RtHLt/A75chX/P55gjMzo5tWSJyfAyICts1miGHFUsu6D2TmKSzF7KNtjucjjzRKmtFl6BwMOxXXf2Q==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/telemetry@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-7lcx4btKGe4tP7Y1Nk6MWQuaiKI+qOk08B4vZFJeNKxRXyCNjVmdck78NyOTEj3iaVxN69MiXDoBZ4fEdvVbBw==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "@better-fetch/fetch" + ] + }, + "@better-auth/utils@0.4.0": { + "integrity": "sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==", + "dependencies": [ + "@noble/hashes" + ] + }, + "@better-fetch/fetch@1.1.21": { + "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==" + }, + "@biomejs/biome@2.3.7": { + "integrity": "sha512-CTbAS/jNAiUc6rcq94BrTB8z83O9+BsgWj2sBCQg9rD6Wkh2gjfR87usjx0Ncx0zGXP1NKgT7JNglay5Zfs9jw==", + "optionalDependencies": [ + "@biomejs/cli-darwin-arm64", + "@biomejs/cli-darwin-x64", + "@biomejs/cli-linux-arm64", + "@biomejs/cli-linux-arm64-musl", + "@biomejs/cli-linux-x64", + "@biomejs/cli-linux-x64-musl", + "@biomejs/cli-win32-arm64", + "@biomejs/cli-win32-x64" + ], + "bin": true + }, + "@biomejs/cli-darwin-arm64@2.3.7": { + "integrity": "sha512-LirkamEwzIUULhXcf2D5b+NatXKeqhOwilM+5eRkbrnr6daKz9rsBL0kNZ16Hcy4b8RFq22SG4tcLwM+yx/wFA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@biomejs/cli-darwin-x64@2.3.7": { + "integrity": "sha512-Q4TO633kvrMQkKIV7wmf8HXwF0dhdTD9S458LGE24TYgBjSRbuhvio4D5eOQzirEYg6eqxfs53ga/rbdd8nBKg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-arm64-musl@2.3.7": { + "integrity": "sha512-/afy8lto4CB8scWfMdt+NoCZtatBUF62Tk3ilWH2w8ENd5spLhM77zKlFZEvsKJv9AFNHknMl03zO67CiklL2Q==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-arm64@2.3.7": { + "integrity": "sha512-inHOTdlstUBzgjDcx0ge71U4SVTbwAljmkfi3MC5WzsYCRhancqfeL+sa4Ke6v2ND53WIwCFD5hGsYExoI3EZQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-x64-musl@2.3.7": { + "integrity": "sha512-CQUtgH1tIN6e5wiYSJqzSwJumHYolNtaj1dwZGCnZXm2PZU1jOJof9TsyiP3bXNDb+VOR7oo7ZvY01If0W3iFQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-x64@2.3.7": { + "integrity": "sha512-fJMc3ZEuo/NaMYo5rvoWjdSS5/uVSW+HPRQujucpZqm2ZCq71b8MKJ9U4th9yrv2L5+5NjPF0nqqILCl8HY/fg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-win32-arm64@2.3.7": { + "integrity": "sha512-aJAE8eCNyRpcfx2JJAtsPtISnELJ0H4xVVSwnxm13bzI8RwbXMyVtxy2r5DV1xT3WiSP+7LxORcApWw0LM8HiA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@biomejs/cli-win32-x64@2.3.7": { + "integrity": "sha512-pulzUshqv9Ed//MiE8MOUeeEkbkSHVDVY5Cz5wVAnH1DUqliCQG3j6s1POaITTFqFfo7AVIx2sWdKpx/GS+Nqw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@drizzle-team/brocli@0.10.2": { + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==" + }, + "@emnapi/core@1.10.0": { + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dependencies": [ + "@emnapi/wasi-threads", + "tslib" + ] + }, + "@emnapi/runtime@1.10.0": { + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dependencies": [ + "tslib" + ] + }, + "@emnapi/wasi-threads@1.2.1": { + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dependencies": [ + "tslib" + ] + }, + "@esbuild-kit/core-utils@3.3.2": { + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "dependencies": [ + "esbuild@0.18.20", + "source-map-support" + ], + "deprecated": true + }, + "@esbuild-kit/esm-loader@2.6.5": { + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "dependencies": [ + "@esbuild-kit/core-utils", + "get-tsconfig" + ], + "deprecated": true + }, + "@esbuild/aix-ppc64@0.25.12": { + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/aix-ppc64@0.27.7": { + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/android-arm64@0.18.20": { + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm64@0.25.12": { + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm64@0.27.7": { + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm@0.18.20": { + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-arm@0.25.12": { + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-arm@0.27.7": { + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-x64@0.18.20": { + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/android-x64@0.25.12": { + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/android-x64@0.27.7": { + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/darwin-arm64@0.18.20": { + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-arm64@0.25.12": { + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-arm64@0.27.7": { + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-x64@0.18.20": { + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/darwin-x64@0.25.12": { + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/darwin-x64@0.27.7": { + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-arm64@0.18.20": { + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-arm64@0.25.12": { + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-arm64@0.27.7": { + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-x64@0.18.20": { + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-x64@0.25.12": { + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-x64@0.27.7": { + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/linux-arm64@0.18.20": { + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm64@0.25.12": { + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm64@0.27.7": { + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm@0.18.20": { + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-arm@0.25.12": { + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-arm@0.27.7": { + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-ia32@0.18.20": { + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-ia32@0.25.12": { + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-ia32@0.27.7": { + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-loong64@0.18.20": { + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-loong64@0.25.12": { + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-loong64@0.27.7": { + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-mips64el@0.18.20": { + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-mips64el@0.25.12": { + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-mips64el@0.27.7": { + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-ppc64@0.18.20": { + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-ppc64@0.25.12": { + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-ppc64@0.27.7": { + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-riscv64@0.18.20": { + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-riscv64@0.25.12": { + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-riscv64@0.27.7": { + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-s390x@0.18.20": { + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-s390x@0.25.12": { + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-s390x@0.27.7": { + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-x64@0.18.20": { + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/linux-x64@0.25.12": { + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/linux-x64@0.27.7": { + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-arm64@0.25.12": { + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-arm64@0.27.7": { + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-x64@0.18.20": { + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-x64@0.25.12": { + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-x64@0.27.7": { + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-arm64@0.25.12": { + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-arm64@0.27.7": { + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-x64@0.18.20": { + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-x64@0.25.12": { + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-x64@0.27.7": { + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openharmony-arm64@0.25.12": { + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/openharmony-arm64@0.27.7": { + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/sunos-x64@0.18.20": { + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/sunos-x64@0.25.12": { + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/sunos-x64@0.27.7": { + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/win32-arm64@0.18.20": { + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-arm64@0.25.12": { + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-arm64@0.27.7": { + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-ia32@0.18.20": { + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-ia32@0.25.12": { + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-ia32@0.27.7": { + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-x64@0.18.20": { + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@esbuild/win32-x64@0.25.12": { + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@esbuild/win32-x64@0.27.7": { + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@floating-ui/core@1.7.5": { + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "dependencies": [ + "@floating-ui/utils" + ] + }, + "@floating-ui/dom@1.7.6": { + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "dependencies": [ + "@floating-ui/core", + "@floating-ui/utils" + ] + }, + "@floating-ui/react-dom@2.1.8_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "dependencies": [ + "@floating-ui/dom", + "react", + "react-dom" + ] + }, + "@floating-ui/utils@0.2.11": { + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==" + }, + "@hono/swagger-ui@0.6.1_hono@4.12.18": { + "integrity": "sha512-sJTvldu1GPeEPfyeLG7gRj+W4vEuD+JDi+JjJ3TJs/DvMUtBLs0KJO5yokGegWWdy5qrbdnQGekbhgNRmPmYKQ==", + "dependencies": [ + "hono" + ] + }, + "@hono/zod-openapi@1.4.0_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-AFchqR1N/NxfI4hUOSGI2/g8zLROxA1OE7Oh5JJFlTaGxhrdRyH+93gd0tIBpb0z8s9r8hUoNnaOBfHbdb4NMw==", + "dependencies": [ + "@asteasolutions/zod-to-openapi", + "@hono/zod-validator", + "hono", + "openapi3-ts", + "zod@4.4.3" + ] + }, + "@hono/zod-validator@0.8.0_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-5uS4S1/LKtZQYvD4BtpPUFkOv8d1wNxHHrChm26buMiEYc1FrHWvDUaKVBwkiVtvSExHSpLGDvcnpI2Copyj9w==", + "dependencies": [ + "hono", + "zod@4.4.3" + ] + }, + "@ioredis/commands@1.5.1": { + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==" + }, + "@jridgewell/gen-mapping@0.3.13": { + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": [ + "@jridgewell/sourcemap-codec", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/remapping@2.3.5": { + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": [ + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec@1.5.5": { + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping@0.3.31": { + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": [ + "@jridgewell/resolve-uri", + "@jridgewell/sourcemap-codec" + ] + }, + "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": { + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": { + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": { + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": { + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": { + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": { + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@napi-rs/wasm-runtime@0.2.12": { + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util" + ] + }, + "@noble/ciphers@2.2.0": { + "integrity": "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==" + }, + "@noble/hashes@2.2.0": { + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==" + }, + "@nodable/entities@2.1.0": { + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==" + }, + "@node-rs/argon2-android-arm-eabi@2.0.2": { + "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@node-rs/argon2-android-arm64@2.0.2": { + "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-darwin-arm64@2.0.2": { + "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-darwin-x64@2.0.2": { + "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@node-rs/argon2-freebsd-x64@2.0.2": { + "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@node-rs/argon2-linux-arm-gnueabihf@2.0.2": { + "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@node-rs/argon2-linux-arm64-gnu@2.0.2": { + "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-linux-arm64-musl@2.0.2": { + "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-linux-x64-gnu@2.0.2": { + "integrity": "sha512-ZM3jrHuJ0dKOhvA80gKJqBpBRmTJTFSo2+xVZR+phQcbAKRlDMSZMFDiKbSTnctkfwNFtjgDdh5g1vaEV04AvA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@node-rs/argon2-linux-x64-musl@2.0.2": { + "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@node-rs/argon2-wasm32-wasi@2.0.2": { + "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", + "dependencies": [ + "@napi-rs/wasm-runtime" + ], + "cpu": ["wasm32"] + }, + "@node-rs/argon2-win32-arm64-msvc@2.0.2": { + "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-win32-ia32-msvc@2.0.2": { + "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@node-rs/argon2-win32-x64-msvc@2.0.2": { + "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@node-rs/argon2@2.0.2": { + "integrity": "sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg==", + "optionalDependencies": [ + "@node-rs/argon2-android-arm-eabi", + "@node-rs/argon2-android-arm64", + "@node-rs/argon2-darwin-arm64", + "@node-rs/argon2-darwin-x64", + "@node-rs/argon2-freebsd-x64", + "@node-rs/argon2-linux-arm-gnueabihf", + "@node-rs/argon2-linux-arm64-gnu", + "@node-rs/argon2-linux-arm64-musl", + "@node-rs/argon2-linux-x64-gnu", + "@node-rs/argon2-linux-x64-musl", + "@node-rs/argon2-wasm32-wasi", + "@node-rs/argon2-win32-arm64-msvc", + "@node-rs/argon2-win32-ia32-msvc", + "@node-rs/argon2-win32-x64-msvc" + ] + }, + "@opentelemetry/semantic-conventions@1.40.0": { + "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==" + }, + "@pinojs/redact@0.4.0": { + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" + }, + "@rolldown/pluginutils@1.0.0-rc.3": { + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==" + }, + "@rollup/rollup-android-arm-eabi@4.60.3": { + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@rollup/rollup-android-arm64@4.60.3": { + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-arm64@4.60.3": { + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-x64@4.60.3": { + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@rollup/rollup-freebsd-arm64@4.60.3": { + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@rollup/rollup-freebsd-x64@4.60.3": { + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-arm-gnueabihf@4.60.3": { + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm-musleabihf@4.60.3": { + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm64-gnu@4.60.3": { + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-arm64-musl@4.60.3": { + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-loong64-gnu@4.60.3": { + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-loong64-musl@4.60.3": { + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-ppc64-gnu@4.60.3": { + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-ppc64-musl@4.60.3": { + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-riscv64-gnu@4.60.3": { + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-riscv64-musl@4.60.3": { + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-s390x-gnu@4.60.3": { + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@rollup/rollup-linux-x64-gnu@4.60.3": { + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-x64-musl@4.60.3": { + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-openbsd-x64@4.60.3": { + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-openharmony-arm64@4.60.3": { + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-arm64-msvc@4.60.3": { + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-ia32-msvc@4.60.3": { + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@rollup/rollup-win32-x64-gnu@4.60.3": { + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@rollup/rollup-win32-x64-msvc@4.60.3": { + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@scalar/client-side-rendering@0.1.7": { + "integrity": "sha512-IDzjKF93jrOljlvKBsLHXT1FPWgz56jFrMPC+iLihREp1qH8wF92mG8Zpakw8cURkEuw5WijRk0xNBP2moGyuw==", + "dependencies": [ + "@scalar/types" + ] + }, + "@scalar/helpers@0.6.0": { + "integrity": "sha512-pfSamAgBxqFeE8IpEG6uGkHlnPhY1CLeOTttV9+vKQbrBk5b7vvyTsUXv0Hz4kNU1TFrxcTTPE+Akn5S+jlTtQ==" + }, + "@scalar/hono-api-reference@0.10.14_hono@4.12.18": { + "integrity": "sha512-LCIT4ul3c4MyD7shhxsWcvvOABt0fEHNQID2n+2TPeItc/MR2qCjjp/QfqD+JoQ7zbc0nnzh1kwRR06MVBmnUA==", + "dependencies": [ + "@scalar/client-side-rendering", + "hono" + ] + }, + "@scalar/types@0.9.6": { + "integrity": "sha512-UaCQQcscFTJdxZREE8KhUdSJgaDlc44TZbmWcZffs4m1hzqOvEI7lEBS13iBpLq7/cxUXFgyJdecywvNqJ0PkA==", + "dependencies": [ + "@scalar/helpers", + "nanoid@5.1.11", + "type-fest", + "zod@4.4.3" + ] + }, + "@smithy/chunked-blob-reader-native@4.2.3": { + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "dependencies": [ + "@smithy/util-base64", + "tslib" + ] + }, + "@smithy/chunked-blob-reader@5.2.2": { + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/config-resolver@4.4.17": { + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/types", + "@smithy/util-config-provider", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "tslib" + ] + }, + "@smithy/core@3.23.17": { + "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "@smithy/uuid", + "tslib" + ] + }, + "@smithy/credential-provider-imds@4.2.14": { + "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/types", + "@smithy/url-parser", + "tslib" + ] + }, + "@smithy/eventstream-codec@4.2.14": { + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "dependencies": [ + "@aws-crypto/crc32", + "@smithy/types", + "@smithy/util-hex-encoding", + "tslib" + ] + }, + "@smithy/eventstream-serde-browser@4.2.14": { + "integrity": "sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==", + "dependencies": [ + "@smithy/eventstream-serde-universal", + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-config-resolver@4.3.14": { + "integrity": "sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-node@4.2.14": { + "integrity": "sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==", + "dependencies": [ + "@smithy/eventstream-serde-universal", + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-universal@4.2.14": { + "integrity": "sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==", + "dependencies": [ + "@smithy/eventstream-codec", + "@smithy/types", + "tslib" + ] + }, + "@smithy/fetch-http-handler@5.3.17": { + "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/types", + "@smithy/util-base64", + "tslib" + ] + }, + "@smithy/hash-blob-browser@4.2.15": { + "integrity": "sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA==", + "dependencies": [ + "@smithy/chunked-blob-reader", + "@smithy/chunked-blob-reader-native", + "@smithy/types", + "tslib" + ] + }, + "@smithy/hash-node@4.2.14": { + "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==", + "dependencies": [ + "@smithy/types", + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/hash-stream-node@4.2.14": { + "integrity": "sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ==", + "dependencies": [ + "@smithy/types", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/invalid-dependency@4.2.14": { + "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/is-array-buffer@2.2.0": { + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/is-array-buffer@4.2.2": { + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/md5-js@4.2.14": { + "integrity": "sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA==", + "dependencies": [ + "@smithy/types", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/middleware-content-length@4.2.14": { + "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@smithy/middleware-endpoint@4.4.32": { + "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==", + "dependencies": [ + "@smithy/core", + "@smithy/middleware-serde", + "@smithy/node-config-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-middleware", + "tslib" + ] + }, + "@smithy/middleware-retry@4.5.7": { + "integrity": "sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==", + "dependencies": [ + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/service-error-classification", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/uuid", + "tslib" + ] + }, + "@smithy/middleware-serde@4.2.20": { + "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==", + "dependencies": [ + "@smithy/core", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@smithy/middleware-stack@4.2.14": { + "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/node-config-provider@4.3.14": { + "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==", + "dependencies": [ + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@smithy/node-http-handler@4.6.1": { + "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/types", + "tslib" + ] + }, + "@smithy/property-provider@4.2.14": { + "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/protocol-http@5.3.14": { + "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/querystring-builder@4.2.14": { + "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==", + "dependencies": [ + "@smithy/types", + "@smithy/util-uri-escape", + "tslib" + ] + }, + "@smithy/querystring-parser@4.2.14": { + "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/service-error-classification@4.3.1": { + "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==", + "dependencies": [ + "@smithy/types" + ] + }, + "@smithy/shared-ini-file-loader@4.4.9": { + "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/signature-v4@5.3.14": { + "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==", + "dependencies": [ + "@smithy/is-array-buffer@4.2.2", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-hex-encoding", + "@smithy/util-middleware", + "@smithy/util-uri-escape", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/smithy-client@4.12.13": { + "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==", + "dependencies": [ + "@smithy/core", + "@smithy/middleware-endpoint", + "@smithy/middleware-stack", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-stream", + "tslib" + ] + }, + "@smithy/types@4.14.1": { + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/url-parser@4.2.14": { + "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==", + "dependencies": [ + "@smithy/querystring-parser", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-base64@4.3.2": { + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "dependencies": [ + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/util-body-length-browser@4.2.2": { + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-body-length-node@4.2.3": { + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-buffer-from@2.2.0": { + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": [ + "@smithy/is-array-buffer@2.2.0", + "tslib" + ] + }, + "@smithy/util-buffer-from@4.2.2": { + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "dependencies": [ + "@smithy/is-array-buffer@4.2.2", + "tslib" + ] + }, + "@smithy/util-config-provider@4.2.2": { + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-defaults-mode-browser@4.3.49": { + "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==", + "dependencies": [ + "@smithy/property-provider", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-defaults-mode-node@4.2.54": { + "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==", + "dependencies": [ + "@smithy/config-resolver", + "@smithy/credential-provider-imds", + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-endpoints@3.4.2": { + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-hex-encoding@4.2.2": { + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-middleware@4.2.14": { + "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-retry@4.3.8": { + "integrity": "sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==", + "dependencies": [ + "@smithy/service-error-classification", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-stream@4.5.25": { + "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==", + "dependencies": [ + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/types", + "@smithy/util-base64", + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-hex-encoding", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/util-uri-escape@4.2.2": { + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-utf8@2.3.0": { + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": [ + "@smithy/util-buffer-from@2.2.0", + "tslib" + ] + }, + "@smithy/util-utf8@4.2.2": { + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dependencies": [ + "@smithy/util-buffer-from@4.2.2", + "tslib" + ] + }, + "@smithy/util-waiter@4.3.0": { + "integrity": "sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/uuid@1.1.2": { + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "dependencies": [ + "tslib" + ] + }, + "@stablelib/base64@1.0.1": { + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, + "@standard-schema/spec@1.1.0": { + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "@tailwindcss/node@4.3.0": { + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dependencies": [ + "@jridgewell/remapping", + "enhanced-resolve", + "jiti", + "lightningcss", + "magic-string", + "source-map-js", + "tailwindcss" + ] + }, + "@tailwindcss/oxide-android-arm64@4.3.0": { + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-arm64@4.3.0": { + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-x64@4.3.0": { + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-freebsd-x64@4.3.0": { + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0": { + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@tailwindcss/oxide-linux-arm64-gnu@4.3.0": { + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-arm64-musl@4.3.0": { + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-x64-gnu@4.3.0": { + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-x64-musl@4.3.0": { + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-wasm32-wasi@4.3.0": { + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "cpu": ["wasm32"] + }, + "@tailwindcss/oxide-win32-arm64-msvc@4.3.0": { + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-win32-x64-msvc@4.3.0": { + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide@4.3.0": { + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "optionalDependencies": [ + "@tailwindcss/oxide-android-arm64", + "@tailwindcss/oxide-darwin-arm64", + "@tailwindcss/oxide-darwin-x64", + "@tailwindcss/oxide-freebsd-x64", + "@tailwindcss/oxide-linux-arm-gnueabihf", + "@tailwindcss/oxide-linux-arm64-gnu", + "@tailwindcss/oxide-linux-arm64-musl", + "@tailwindcss/oxide-linux-x64-gnu", + "@tailwindcss/oxide-linux-x64-musl", + "@tailwindcss/oxide-wasm32-wasi", + "@tailwindcss/oxide-win32-arm64-msvc", + "@tailwindcss/oxide-win32-x64-msvc" + ] + }, + "@tailwindcss/vite@4.3.0_vite@7.3.3": { + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dependencies": [ + "@tailwindcss/node", + "@tailwindcss/oxide", + "tailwindcss", + "vite" + ] + }, + "@tanstack/devtools-event-client@0.4.3": { + "integrity": "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==", + "bin": true + }, + "@tanstack/form-core@1.31.0": { + "integrity": "sha512-t5G/LnrM/U10mQgzYis/WvHc6yTDj7jyF5cYf6GjVJpEbn0et0wDUQtafiOXY8hiXJ9D1HUqBi0u76ZX05uiHw==", + "dependencies": [ + "@tanstack/devtools-event-client", + "@tanstack/pacer-lite", + "@tanstack/store" + ] + }, + "@tanstack/history@1.161.6": { + "integrity": "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==" + }, + "@tanstack/pacer-lite@0.1.1": { + "integrity": "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==" + }, + "@tanstack/query-core@5.100.9": { + "integrity": "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==" + }, + "@tanstack/react-form@1.31.0_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-31hoeKlk45jCAnry85bOiL89FwySL9HtAhfWLHmCJ5WcvBlHJpoqnMvl6Cy8DM+7ey2eWHKiPiEL29BkG5o12w==", + "dependencies": [ + "@tanstack/form-core", + "@tanstack/react-store", + "react" + ] + }, + "@tanstack/react-query@5.100.9_react@19.2.6": { + "integrity": "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==", + "dependencies": [ + "@tanstack/query-core", + "react" + ] + }, + "@tanstack/react-router-devtools@1.166.13_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_@tanstack+router-core@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6_csstype@3.2.3": { + "integrity": "sha512-6yKRFFJrEEOiGp5RAAuGCYsl81M4XAhJmLcu9PKj+HZle4A3dsP60lwHoqQYWHMK9nKKFkdXR+D8qxzxqtQbEA==", + "dependencies": [ + "@tanstack/react-router", + "@tanstack/router-core", + "@tanstack/router-devtools-core", + "react", + "react-dom" + ], + "optionalPeers": [ + "@tanstack/router-core" + ] + }, + "@tanstack/react-router@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ==", + "dependencies": [ + "@tanstack/history", + "@tanstack/react-store", + "@tanstack/router-core", + "isbot", + "react", + "react-dom" + ] + }, + "@tanstack/react-store@0.9.3_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==", + "dependencies": [ + "@tanstack/store", + "react", + "react-dom", + "use-sync-external-store" + ] + }, + "@tanstack/router-core@1.169.2": { + "integrity": "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw==", + "dependencies": [ + "@tanstack/history", + "cookie-es", + "seroval", + "seroval-plugins" + ] + }, + "@tanstack/router-devtools-core@1.167.3_@tanstack+router-core@1.169.2_csstype@3.2.3": { + "integrity": "sha512-fJ1VMhyQgnoashTrP763c2HRc9kofgF61L7Jb3F6eTHAmCKtGVx8BRtiFt37sr3U0P0jmaaiiSPGP6nT5JtVNg==", + "dependencies": [ + "@tanstack/router-core", + "clsx", + "csstype", + "goober" + ], + "optionalPeers": [ + "csstype" + ] + }, + "@tanstack/router-generator@1.166.42": { + "integrity": "sha512-2qBWC0t78r6b3vI+AbnvCZcFAvbYBDlLuWZrTjQbcjUmwG3qyeQp983tJyDuj9wb5//adG1tgAGXZkJ3aDwdBg==", + "dependencies": [ + "@babel/types", + "@tanstack/router-core", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "jiti", + "magic-string", + "prettier", + "zod@3.25.76" + ] + }, + "@tanstack/router-plugin@1.167.35_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_vite@7.3.3_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-UAScU5VAzLYVY4FML/Cbc5S5TucT4I8Ata05yozGOe4ZfepTKRffA5xWLtD2N+ov5svdv0KTX/kqlZnYPe28mA==", + "dependencies": [ + "@babel/core", + "@babel/plugin-syntax-jsx", + "@babel/plugin-syntax-typescript", + "@babel/template", + "@babel/traverse", + "@babel/types", + "@tanstack/react-router", + "@tanstack/router-core", + "@tanstack/router-generator", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "chokidar", + "unplugin", + "vite", + "zod@3.25.76" + ], + "optionalPeers": [ + "@tanstack/react-router", + "vite" + ] + }, + "@tanstack/router-utils@1.161.8": { + "integrity": "sha512-xyiLWEKjfBAVhauDSSjXxyf7s8elU6SM+V050sbkofvGmIIvkwPFtDsX7Gvwh14kBd6iCwAT+RiPvXTxAptY0Q==", + "dependencies": [ + "@babel/core", + "@babel/generator", + "@babel/parser", + "@babel/types", + "ansis", + "babel-dead-code-elimination", + "diff", + "pathe", + "tinyglobby" + ] + }, + "@tanstack/store@0.9.3": { + "integrity": "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==" + }, + "@tanstack/virtual-file-routes@1.161.7": { + "integrity": "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ==", + "bin": true + }, + "@tybys/wasm-util@0.10.2": { + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dependencies": [ + "tslib" + ] + }, + "@types/babel__core@7.20.5": { + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@types/babel__generator", + "@types/babel__template", + "@types/babel__traverse" + ] + }, + "@types/babel__generator@7.27.0": { + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/babel__template@7.4.4": { + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": [ + "@babel/parser", + "@babel/types" + ] + }, + "@types/babel__traverse@7.28.0": { + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/estree@1.0.8": { + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "@types/react-dom@19.2.3_@types+react@19.2.14": { + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dependencies": [ + "@types/react" + ] + }, + "@types/react@19.2.14": { + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dependencies": [ + "csstype" + ] + }, + "@vitejs/plugin-react@5.2.0_vite@7.3.3": { + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dependencies": [ + "@babel/core", + "@babel/plugin-transform-react-jsx-self", + "@babel/plugin-transform-react-jsx-source", + "@rolldown/pluginutils", + "@types/babel__core", + "react-refresh", + "vite" + ] + }, + "abort-controller@3.0.0": { + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": [ + "event-target-shim" + ] + }, + "ansis@4.2.0": { + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==" + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": [ + "normalize-path", + "picomatch@2.3.2" + ] + }, + "atomic-sleep@1.0.0": { + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "babel-dead-code-elimination@1.0.12": { + "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "dependencies": [ + "@babel/core", + "@babel/parser", + "@babel/traverse", + "@babel/types" + ] + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "baseline-browser-mapping@2.10.29": { + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "bin": true + }, + "better-auth@1.6.10_drizzle-kit@0.31.10_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_react@19.2.6_react-dom@19.2.6__react@19.2.6_postgres@3.4.9": { + "integrity": "sha512-gzYaywJuhAkv9bTuFj1k6zaSKEAcabxAzYsBj0kXSMaQJVE9uS/qp2592IZmuvtMHO1ohLOP92jDPV6xVsZSoQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/drizzle-adapter", + "@better-auth/kysely-adapter", + "@better-auth/memory-adapter", + "@better-auth/mongo-adapter", + "@better-auth/prisma-adapter", + "@better-auth/telemetry", + "@better-auth/utils", + "@better-fetch/fetch", + "@noble/ciphers", + "@noble/hashes", + "better-call", + "defu", + "drizzle-kit", + "drizzle-orm", + "jose", + "kysely", + "nanostores", + "react", + "react-dom", + "zod@4.4.3" + ], + "optionalPeers": [ + "drizzle-kit", + "drizzle-orm", + "react", + "react-dom" + ] + }, + "better-call@1.3.5_zod@4.4.3": { + "integrity": "sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==", + "dependencies": [ + "@better-auth/utils", + "@better-fetch/fetch", + "rou3", + "set-cookie-parser", + "zod@4.4.3" + ], + "optionalPeers": [ + "zod@4.4.3" + ] + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "bowser@2.14.1": { + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==" + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range" + ] + }, + "browserslist@4.28.2": { + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dependencies": [ + "baseline-browser-mapping", + "caniuse-lite", + "electron-to-chromium", + "node-releases", + "update-browserslist-db" + ], + "bin": true + }, + "buffer-from@1.1.2": { + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer@6.0.3": { + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "bullmq@5.76.6": { + "integrity": "sha512-vlmL3B3NVMRy6se3c7jPHn1Nhqxrg7+wlv1t3XAQFBYZNJDMLP0OO5x2AX5ca7DAuS1SU/C+VfYi+NHVoFK1QQ==", + "dependencies": [ + "cron-parser", + "ioredis", + "msgpackr", + "node-abort-controller", + "semver@7.7.4", + "tslib" + ] + }, + "caniuse-lite@1.0.30001792": { + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==" + }, + "chokidar@3.6.0": { + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": [ + "anymatch", + "braces", + "glob-parent", + "is-binary-path", + "is-glob", + "normalize-path", + "readdirp" + ], + "optionalDependencies": [ + "fsevents" + ] + }, + "class-variance-authority@0.7.1": { + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": [ + "clsx" + ] + }, + "clsx@2.1.1": { + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, + "cluster-key-slot@1.1.2": { + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, + "colorette@2.0.20": { + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "convert-source-map@2.0.0": { + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "cookie-es@3.1.1": { + "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==" + }, + "cron-parser@4.9.0": { + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": [ + "luxon" + ] + }, + "csstype@3.2.3": { + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "dateformat@4.6.3": { + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" + }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, + "defu@6.1.7": { + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==" + }, + "denque@2.1.0": { + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, + "detect-libc@2.1.2": { + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, + "diff@8.0.4": { + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==" + }, + "dotenv-expand@13.0.0": { + "integrity": "sha512-aBfBS8eYIeXmpHI9ThIlA7/WLq+SLt18iXUZhb52rW89QLKQFoIpPG1bPeewoPZsTyjSSO3T7234FBVUM1V2rA==", + "dependencies": [ + "dotenv" + ] + }, + "dotenv@17.4.2": { + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==" + }, + "drizzle-kit@0.31.10": { + "integrity": "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==", + "dependencies": [ + "@drizzle-team/brocli", + "@esbuild-kit/esm-loader", + "esbuild@0.25.12", + "tsx" + ], + "bin": true + }, + "drizzle-orm@0.45.2_kysely@0.28.17_postgres@3.4.9": { + "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==", + "dependencies": [ + "kysely", + "postgres" + ], + "optionalPeers": [ + "kysely", + "postgres" + ] + }, + "drizzle-zod@0.8.3_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_zod@4.4.3_kysely@0.28.17_postgres@3.4.9": { + "integrity": "sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==", + "dependencies": [ + "drizzle-orm", + "zod@4.4.3" + ] + }, + "electron-to-chromium@1.5.353": { + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==" + }, + "end-of-stream@1.4.5": { + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": [ + "once" + ] + }, + "enhanced-resolve@5.21.2": { + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", + "dependencies": [ + "graceful-fs", + "tapable" + ] + }, + "esbuild@0.18.20": { + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "optionalDependencies": [ + "@esbuild/android-arm@0.18.20", + "@esbuild/android-arm64@0.18.20", + "@esbuild/android-x64@0.18.20", + "@esbuild/darwin-arm64@0.18.20", + "@esbuild/darwin-x64@0.18.20", + "@esbuild/freebsd-arm64@0.18.20", + "@esbuild/freebsd-x64@0.18.20", + "@esbuild/linux-arm@0.18.20", + "@esbuild/linux-arm64@0.18.20", + "@esbuild/linux-ia32@0.18.20", + "@esbuild/linux-loong64@0.18.20", + "@esbuild/linux-mips64el@0.18.20", + "@esbuild/linux-ppc64@0.18.20", + "@esbuild/linux-riscv64@0.18.20", + "@esbuild/linux-s390x@0.18.20", + "@esbuild/linux-x64@0.18.20", + "@esbuild/netbsd-x64@0.18.20", + "@esbuild/openbsd-x64@0.18.20", + "@esbuild/sunos-x64@0.18.20", + "@esbuild/win32-arm64@0.18.20", + "@esbuild/win32-ia32@0.18.20", + "@esbuild/win32-x64@0.18.20" + ], + "scripts": true, + "bin": true + }, + "esbuild@0.25.12": { + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "optionalDependencies": [ + "@esbuild/aix-ppc64@0.25.12", + "@esbuild/android-arm@0.25.12", + "@esbuild/android-arm64@0.25.12", + "@esbuild/android-x64@0.25.12", + "@esbuild/darwin-arm64@0.25.12", + "@esbuild/darwin-x64@0.25.12", + "@esbuild/freebsd-arm64@0.25.12", + "@esbuild/freebsd-x64@0.25.12", + "@esbuild/linux-arm@0.25.12", + "@esbuild/linux-arm64@0.25.12", + "@esbuild/linux-ia32@0.25.12", + "@esbuild/linux-loong64@0.25.12", + "@esbuild/linux-mips64el@0.25.12", + "@esbuild/linux-ppc64@0.25.12", + "@esbuild/linux-riscv64@0.25.12", + "@esbuild/linux-s390x@0.25.12", + "@esbuild/linux-x64@0.25.12", + "@esbuild/netbsd-arm64@0.25.12", + "@esbuild/netbsd-x64@0.25.12", + "@esbuild/openbsd-arm64@0.25.12", + "@esbuild/openbsd-x64@0.25.12", + "@esbuild/openharmony-arm64@0.25.12", + "@esbuild/sunos-x64@0.25.12", + "@esbuild/win32-arm64@0.25.12", + "@esbuild/win32-ia32@0.25.12", + "@esbuild/win32-x64@0.25.12" + ], + "scripts": true, + "bin": true + }, + "esbuild@0.27.7": { + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "optionalDependencies": [ + "@esbuild/aix-ppc64@0.27.7", + "@esbuild/android-arm@0.27.7", + "@esbuild/android-arm64@0.27.7", + "@esbuild/android-x64@0.27.7", + "@esbuild/darwin-arm64@0.27.7", + "@esbuild/darwin-x64@0.27.7", + "@esbuild/freebsd-arm64@0.27.7", + "@esbuild/freebsd-x64@0.27.7", + "@esbuild/linux-arm@0.27.7", + "@esbuild/linux-arm64@0.27.7", + "@esbuild/linux-ia32@0.27.7", + "@esbuild/linux-loong64@0.27.7", + "@esbuild/linux-mips64el@0.27.7", + "@esbuild/linux-ppc64@0.27.7", + "@esbuild/linux-riscv64@0.27.7", + "@esbuild/linux-s390x@0.27.7", + "@esbuild/linux-x64@0.27.7", + "@esbuild/netbsd-arm64@0.27.7", + "@esbuild/netbsd-x64@0.27.7", + "@esbuild/openbsd-arm64@0.27.7", + "@esbuild/openbsd-x64@0.27.7", + "@esbuild/openharmony-arm64@0.27.7", + "@esbuild/sunos-x64@0.27.7", + "@esbuild/win32-arm64@0.27.7", + "@esbuild/win32-ia32@0.27.7", + "@esbuild/win32-x64@0.27.7" + ], + "scripts": true, + "bin": true + }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, + "event-target-shim@5.0.1": { + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events@3.3.0": { + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-copy@4.0.3": { + "integrity": "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==" + }, + "fast-safe-stringify@2.1.1": { + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fast-sha256@1.3.0": { + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, + "fast-xml-builder@1.2.0": { + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "dependencies": [ + "path-expression-matcher", + "xml-naming" + ] + }, + "fast-xml-parser@5.7.2": { + "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==", + "dependencies": [ + "@nodable/entities", + "fast-xml-builder", + "path-expression-matcher", + "strnum" + ], + "bin": true + }, + "fdir@6.5.0_picomatch@4.0.4": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch@4.0.4" + ], + "optionalPeers": [ + "picomatch@4.0.4" + ] + }, + "fetch-retry@6.0.0": { + "integrity": "sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==" + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "gensync@1.0.0-beta.2": { + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-tsconfig@4.14.0": { + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "goober@2.1.18_csstype@3.2.3": { + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", + "dependencies": [ + "csstype" + ] + }, + "graceful-fs@4.2.11": { + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "help-me@5.0.0": { + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "hono-pino@0.10.3_hono@4.12.18_pino@10.3.1": { + "integrity": "sha512-n0RNPIFOoq25Fg8b4D5gus4sVqI0z+8I17ibl96+p43d07UnZ0EMM/It0qSgfc7UtaC+XP5FkFmRHwBp6owsNA==", + "dependencies": [ + "defu", + "hono", + "pino" + ] + }, + "hono@4.12.18": { + "integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==" + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ioredis@5.10.1": { + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "dependencies": [ + "@ioredis/commands", + "cluster-key-slot", + "debug", + "denque", + "lodash.defaults", + "lodash.isarguments", + "redis-errors", + "redis-parser", + "standard-as-callback" + ] + }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": [ + "binary-extensions" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "isbot@5.1.40": { + "integrity": "sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ==" + }, + "jiti@2.7.0": { + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "bin": true + }, + "jose@6.2.3": { + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==" + }, + "joycon@3.1.1": { + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==" + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsesc@3.1.0": { + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": true + }, + "json5@2.2.3": { + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": true + }, + "kysely@0.28.17": { + "integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==" + }, + "lightningcss-android-arm64@1.32.0": { + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-arm64@1.32.0": { + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-x64@1.32.0": { + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "lightningcss-freebsd-x64@1.32.0": { + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "lightningcss-linux-arm-gnueabihf@1.32.0": { + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "lightningcss-linux-arm64-gnu@1.32.0": { + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-arm64-musl@1.32.0": { + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-x64-gnu@1.32.0": { + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-linux-x64-musl@1.32.0": { + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-win32-arm64-msvc@1.32.0": { + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "lightningcss-win32-x64-msvc@1.32.0": { + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "os": ["win32"], + "cpu": ["x64"] + }, + "lightningcss@1.32.0": { + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dependencies": [ + "detect-libc" + ], + "optionalDependencies": [ + "lightningcss-android-arm64", + "lightningcss-darwin-arm64", + "lightningcss-darwin-x64", + "lightningcss-freebsd-x64", + "lightningcss-linux-arm-gnueabihf", + "lightningcss-linux-arm64-gnu", + "lightningcss-linux-arm64-musl", + "lightningcss-linux-x64-gnu", + "lightningcss-linux-x64-musl", + "lightningcss-win32-arm64-msvc", + "lightningcss-win32-x64-msvc" + ] + }, + "lodash.defaults@4.2.0": { + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.isarguments@3.1.0": { + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, + "lru-cache@5.1.1": { + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": [ + "yallist" + ] + }, + "lucide-react@0.553.0_react@19.2.6": { + "integrity": "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==", + "dependencies": [ + "react" + ] + }, + "luxon@3.7.2": { + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==" + }, + "magic-string@0.30.21": { + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "msgpackr-extract@3.0.3": { + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dependencies": [ + "node-gyp-build-optional-packages" + ], + "optionalDependencies": [ + "@msgpackr-extract/msgpackr-extract-darwin-arm64", + "@msgpackr-extract/msgpackr-extract-darwin-x64", + "@msgpackr-extract/msgpackr-extract-linux-arm", + "@msgpackr-extract/msgpackr-extract-linux-arm64", + "@msgpackr-extract/msgpackr-extract-linux-x64", + "@msgpackr-extract/msgpackr-extract-win32-x64" + ], + "scripts": true, + "bin": true + }, + "msgpackr@2.0.1": { + "integrity": "sha512-9J+tqTEsbHqY8YohazYgty7LgerFIWxvMLpUjqETSmjHojtJm2WnX2kK/2a1fLI7CO7ERP1YSEUXMucz4j+yBA==", + "optionalDependencies": [ + "msgpackr-extract" + ] + }, + "nanoid@3.3.12": { + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "bin": true + }, + "nanoid@5.1.11": { + "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", + "bin": true + }, + "nanostores@1.3.0": { + "integrity": "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==" + }, + "node-abort-controller@3.1.1": { + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node-gyp-build-optional-packages@5.2.2": { + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dependencies": [ + "detect-libc" + ], + "bin": true + }, + "node-releases@2.0.38": { + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==" + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "on-exit-leak-free@2.1.2": { + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "openapi3-ts@4.5.0": { + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "dependencies": [ + "yaml" + ] + }, + "path-expression-matcher@1.5.0": { + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==" + }, + "pathe@2.0.3": { + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@2.3.2": { + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" + }, + "picomatch@4.0.4": { + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" + }, + "pino-abstract-transport@1.2.0": { + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": [ + "readable-stream", + "split2" + ] + }, + "pino-abstract-transport@3.0.0": { + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "dependencies": [ + "split2" + ] + }, + "pino-pretty@13.1.3": { + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "dependencies": [ + "colorette", + "dateformat", + "fast-copy", + "fast-safe-stringify", + "help-me", + "joycon", + "minimist", + "on-exit-leak-free", + "pino-abstract-transport@3.0.0", + "pump", + "secure-json-parse", + "sonic-boom", + "strip-json-comments" + ], + "bin": true + }, + "pino-std-serializers@7.1.0": { + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==" + }, + "pino@10.3.1": { + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "dependencies": [ + "@pinojs/redact", + "atomic-sleep", + "on-exit-leak-free", + "pino-abstract-transport@3.0.0", + "pino-std-serializers", + "process-warning", + "quick-format-unescaped", + "real-require", + "safe-stable-stringify", + "sonic-boom", + "thread-stream" + ], + "bin": true + }, + "postal-mime@2.7.4": { + "integrity": "sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==" + }, + "postcss@8.5.14": { + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dependencies": [ + "nanoid@3.3.12", + "picocolors", + "source-map-js" + ] + }, + "postgres@3.4.9": { + "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==" + }, + "prettier@3.8.3": { + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "bin": true + }, + "process-warning@5.0.0": { + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==" + }, + "process@0.11.10": { + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "pump@3.0.4": { + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dependencies": [ + "end-of-stream", + "once" + ] + }, + "quick-format-unescaped@4.0.4": { + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "react-dom@19.2.6_react@19.2.6": { + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "dependencies": [ + "react", + "scheduler" + ] + }, + "react-refresh@0.18.0": { + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==" + }, + "react@19.2.6": { + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==" + }, + "readable-stream@4.7.0": { + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dependencies": [ + "abort-controller", + "buffer", + "events", + "process", + "string_decoder" + ] + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": [ + "picomatch@2.3.2" + ] + }, + "real-require@0.2.0": { + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "redis-errors@1.2.0": { + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + }, + "redis-parser@3.0.0": { + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": [ + "redis-errors" + ] + }, + "reselect@5.1.1": { + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, + "resend@6.12.3": { + "integrity": "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw==", + "dependencies": [ + "postal-mime", + "svix" + ] + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, + "rollup@4.60.3": { + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dependencies": [ + "@types/estree" + ], + "optionalDependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-loong64-gnu", + "@rollup/rollup-linux-loong64-musl", + "@rollup/rollup-linux-ppc64-gnu", + "@rollup/rollup-linux-ppc64-musl", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-riscv64-musl", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-openbsd-x64", + "@rollup/rollup-openharmony-arm64", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-gnu", + "@rollup/rollup-win32-x64-msvc", + "fsevents" + ], + "bin": true + }, + "rou3@0.7.12": { + "integrity": "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==" + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-stable-stringify@2.5.0": { + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "scheduler@0.27.0": { + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" + }, + "secure-json-parse@4.1.0": { + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==" + }, + "semver@6.3.1": { + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": true + }, + "semver@7.7.4": { + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": true + }, + "seroval-plugins@1.5.4_seroval@1.5.4": { + "integrity": "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==", + "dependencies": [ + "seroval" + ] + }, + "seroval@1.5.4": { + "integrity": "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==" + }, + "set-cookie-parser@3.1.0": { + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==" + }, + "sonic-boom@4.2.1": { + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "dependencies": [ + "atomic-sleep" + ] + }, + "sonner@2.0.7_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "dependencies": [ + "react", + "react-dom" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map-support@0.5.21": { + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": [ + "buffer-from", + "source-map" + ] + }, + "source-map@0.6.1": { + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "split2@4.2.0": { + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "standard-as-callback@2.1.0": { + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "standardwebhooks@1.0.0": { + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "dependencies": [ + "@stablelib/base64", + "fast-sha256" + ] + }, + "stoker@2.0.1_@hono+zod-openapi@1.4.0__hono@4.12.18__zod@4.4.3_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-liSQNnJmn8fWSEan7sVaFe6iSHuN3X02fDGLS6snwW+FUuKi5HmKUHm3P+Kzr5xiDPqRpmSTtmGEBbSL9H2zkQ==", + "dependencies": [ + "@hono/zod-openapi", + "hono" + ], + "optionalPeers": [ + "@hono/zod-openapi" + ] + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer" + ] + }, + "strip-json-comments@5.0.3": { + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==" + }, + "strnum@2.3.0": { + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==" + }, + "svix@1.92.2": { + "integrity": "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ==", + "dependencies": [ + "standardwebhooks" + ] + }, + "tagged-tag@1.0.0": { + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==" + }, + "tailwind-merge@3.5.0": { + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==" + }, + "tailwindcss@4.3.0": { + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==" + }, + "tapable@2.3.3": { + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==" + }, + "thread-stream@4.0.0": { + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", + "dependencies": [ + "real-require" + ] + }, + "tinyglobby@0.2.16": { + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dependencies": [ + "fdir", + "picomatch@4.0.4" + ] + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "tsx@4.21.0": { + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dependencies": [ + "esbuild@0.27.7", + "get-tsconfig" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "type-fest@5.6.0": { + "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", + "dependencies": [ + "tagged-tag" + ] + }, + "typescript@5.9.3": { + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "bin": true + }, + "unplugin@3.0.0": { + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", + "dependencies": [ + "@jridgewell/remapping", + "picomatch@4.0.4", + "webpack-virtual-modules" + ] + }, + "update-browserslist-db@1.2.3_browserslist@4.28.2": { + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dependencies": [ + "browserslist", + "escalade", + "picocolors" + ], + "bin": true + }, + "use-sync-external-store@1.6.0_react@19.2.6": { + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dependencies": [ + "react" + ] + }, + "vite@7.3.3": { + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dependencies": [ + "esbuild@0.27.7", + "fdir", + "picomatch@4.0.4", + "postcss", + "rollup", + "tinyglobby" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "webpack-virtual-modules@0.6.2": { + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xml-naming@0.1.0": { + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==" + }, + "yallist@3.1.1": { + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml@2.8.4": { + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "bin": true + }, + "zod@3.25.76": { + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" + }, + "zod@4.4.3": { + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==" + }, + "zustand@5.0.13_@types+react@19.2.14_react@19.2.6": { + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react", + "react" + ] + } + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@base-ui/react@^1.2.0", + "npm:@better-fetch/fetch@^1.1.18", + "npm:@tailwindcss/vite@^4.1.17", + "npm:@tanstack/react-form@1", + "npm:@tanstack/react-query@^5.90.7", + "npm:@tanstack/react-router-devtools@^1.134.13", + "npm:@tanstack/react-router@^1.134.13", + "npm:@tanstack/router-plugin@^1.134.14", + "npm:@types/react-dom@^19.1.9", + "npm:@types/react@^19.1.16", + "npm:@vitejs/plugin-react@^5.0.4", + "npm:better-auth@^1.3.34", + "npm:class-variance-authority@~0.7.1", + "npm:clsx@^2.1.1", + "npm:lucide-react@0.553", + "npm:react-dom@^19.1.1", + "npm:react@^19.1.1", + "npm:sonner@^2.0.7", + "npm:tailwind-merge@^3.3.1", + "npm:tailwindcss@^4.1.17", + "npm:typescript@~5.9.3", + "npm:vite@^7.1.7", + "npm:zod@^4.2.1", + "npm:zustand@^5.0.8" + ] + } + } +} diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 95002a1..983a785 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -4,18 +4,14 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite dev", - "build": "pnpm -w build:packages && vite build && tsc --noEmit", + "dev": "vite dev --port 3000", + "build": "vite build", "typecheck": "tsc --noEmit", - "lint": "biome check .", - "start": "vite", "preview": "vite preview" }, "dependencies": { "@base-ui/react": "^1.2.0", "@better-fetch/fetch": "^1.1.18", - "@repo/db": "workspace:*", - "@repo/shared": "workspace:*", "@tailwindcss/vite": "^4.1.17", "@tanstack/react-form": "^1.0.0", "@tanstack/react-query": "^5.90.7", @@ -30,16 +26,16 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.17", - "vite-tsconfig-paths": "^5.1.4", "zod": "^4.2.1", "zustand": "^5.0.8" }, "devDependencies": { "@tanstack/router-plugin": "^1.134.14", + "@testing-library/react": "^16.3.2", "@types/react": "^19.1.16", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "^5.0.4", - "tw-animate-css": "^1.4.0", + "jsdom": "^30.0.1", "typescript": "~5.9.3", "vite": "^7.1.7" } diff --git a/apps/frontend/src/components/error-fallback.tsx b/apps/frontend/src/components/error-fallback.tsx new file mode 100644 index 0000000..d5fadcc --- /dev/null +++ b/apps/frontend/src/components/error-fallback.tsx @@ -0,0 +1,42 @@ +import { useRouter } from "@tanstack/react-router"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +type ErrorFallbackProps = { + error: Error; +}; + +function ErrorFallback({ error }: ErrorFallbackProps) { + const router = useRouter(); + + return ( +
+ + + Something went wrong + + +

+ {error.message || "An unexpected error occurred"} +

+ {import.meta.env.DEV && error.stack && ( +
+							{error.stack}
+						
+ )} +
+ + + +
+
+ ); +} + +export { ErrorFallback }; diff --git a/apps/frontend/src/components/ui/__tests__/field-error.test.tsx b/apps/frontend/src/components/ui/__tests__/field-error.test.tsx new file mode 100644 index 0000000..2a6ade0 --- /dev/null +++ b/apps/frontend/src/components/ui/__tests__/field-error.test.tsx @@ -0,0 +1,27 @@ +import "../../../test-setup.ts"; + +import { expect } from "@std/expect"; +import { afterEach, describe, it } from "@std/testing/bdd"; +import { cleanup, render, screen } from "@testing-library/react"; +import { FieldError } from "../field-error.tsx"; + +afterEach(cleanup); + +describe("FieldError", () => { + it("renders nothing when there are no errors", () => { + const { container } = render(); + expect(container.innerHTML).toBe(""); + }); + + it("renders a single error message", () => { + render(); + expect(screen.getByRole("alert").textContent).toBe("Name is required"); + }); + + it("joins multiple error messages with a comma", () => { + render(); + expect(screen.getByRole("alert").textContent).toBe( + "Too short, Must be unique", + ); + }); +}); diff --git a/apps/frontend/src/components/ui/field-error.tsx b/apps/frontend/src/components/ui/field-error.tsx index f9b295c..adc9131 100644 --- a/apps/frontend/src/components/ui/field-error.tsx +++ b/apps/frontend/src/components/ui/field-error.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils.ts"; type FieldErrorProps = { errors: string[]; diff --git a/apps/frontend/src/lib/__tests__/utils.test.ts b/apps/frontend/src/lib/__tests__/utils.test.ts new file mode 100644 index 0000000..1edd2f4 --- /dev/null +++ b/apps/frontend/src/lib/__tests__/utils.test.ts @@ -0,0 +1,23 @@ +// Pure function — no DOM. Call with plain values. + +import { expect } from "@std/expect"; +import { describe, it } from "@std/testing/bdd"; +import { cn } from "../utils.ts"; + +describe("cn", () => { + it("joins multiple class strings", () => { + expect(cn("a", "b", "c")).toBe("a b c"); + }); + + it("drops falsy values", () => { + expect(cn("a", false, null, undefined, "b")).toBe("a b"); + }); + + it("lets a later conflicting Tailwind class win", () => { + expect(cn("px-2", "px-4")).toBe("px-4"); + }); + + it("applies conditional classes via object syntax", () => { + expect(cn("base", { active: true, disabled: false })).toBe("base active"); + }); +}); diff --git a/apps/frontend/src/lib/api.ts b/apps/frontend/src/lib/api.ts index e497453..bc23915 100644 --- a/apps/frontend/src/lib/api.ts +++ b/apps/frontend/src/lib/api.ts @@ -1,4 +1,4 @@ -const BASE_URL = import.meta.env.VITE_API_URL ?? "http://localhost:9999"; +const BASE_URL = import.meta.env.VITE_API_URL ?? ""; // ─── Typed API error ────────────────────────────────────────────────────────── // Thrown for any non-2xx response so callers can distinguish HTTP failures diff --git a/apps/frontend/src/lib/auth-client.ts b/apps/frontend/src/lib/auth-client.ts index 3256f1b..a3ba556 100644 --- a/apps/frontend/src/lib/auth-client.ts +++ b/apps/frontend/src/lib/auth-client.ts @@ -1,7 +1,7 @@ import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ - baseURL: import.meta.env.VITE_API_URL || "http://localhost:9999", + baseURL: import.meta.env.VITE_API_URL, }); export const { signIn, signUp, signOut, useSession } = authClient; diff --git a/apps/frontend/src/routes/__root.tsx b/apps/frontend/src/routes/__root.tsx index e8e61ad..d42bffc 100644 --- a/apps/frontend/src/routes/__root.tsx +++ b/apps/frontend/src/routes/__root.tsx @@ -1,7 +1,12 @@ import type { QueryClient } from "@tanstack/react-query"; -import { createRootRouteWithContext, Outlet } from "@tanstack/react-router"; +import { + createRootRouteWithContext, + Link, + Outlet, +} from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import { Toaster } from "sonner"; +import { ErrorFallback } from "@/components/error-fallback"; import { sessionQueryOptions } from "@/services/auth"; interface RouterContext { @@ -9,15 +14,26 @@ interface RouterContext { } export const Route = createRootRouteWithContext()({ - // Prefetch the session once at the root of the route tree. - // Every child route receives context.session — no per-page auth fetch. - // After any auth mutation (signIn, signOut, signUp), invalidate - // sessionQueryOptions.queryKey and navigate; the router re-runs this. beforeLoad: async ({ context }) => { const session = await context.queryClient.ensureQueryData(sessionQueryOptions); return { session }; }, + errorComponent: ErrorFallback, + notFoundComponent: () => ( +
+
+

404

+

Page not found

+ + Go home + +
+
+ ), component: RootComponent, }); diff --git a/apps/frontend/src/test-setup.ts b/apps/frontend/src/test-setup.ts new file mode 100644 index 0000000..319b437 --- /dev/null +++ b/apps/frontend/src/test-setup.ts @@ -0,0 +1,26 @@ +// Installs a jsdom environment on globalThis so @testing-library/react can +// render components under `deno test`. Import this first in any test file +// that needs a DOM — Deno's test runner has no Vitest-style global setupFiles. + +import { JSDOM } from "jsdom"; + +const dom = new JSDOM("", { + url: "http://localhost/", + pretendToBeVisual: true, +}); + +// biome-ignore lint/suspicious/noExplicitAny: assigning jsdom globals onto globalThis +const g = globalThis as any; + +g.window = dom.window; +g.document = dom.window.document; +g.navigator = dom.window.navigator; +g.HTMLElement = dom.window.HTMLElement; +g.Element = dom.window.Element; +g.Node = dom.window.Node; +g.customElements = dom.window.customElements; +g.getComputedStyle = dom.window.getComputedStyle; +g.requestAnimationFrame = (cb: FrameRequestCallback) => + setTimeout(() => cb(Date.now()), 0); +g.cancelAnimationFrame = (id: number) => clearTimeout(id); +g.IS_REACT_ACT_ENVIRONMENT = true; diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index ec93b6d..64c8ba4 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -23,5 +23,6 @@ "@/*": ["./src/*"] } }, - "include": ["src"] + "include": ["src"], + "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/test-setup.ts"] } diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts index c9816cc..46a2d29 100644 --- a/apps/frontend/vite.config.ts +++ b/apps/frontend/vite.config.ts @@ -3,11 +3,19 @@ import tailwindcss from "@tailwindcss/vite"; import tanstackRouter from "@tanstack/router-plugin/vite"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; -import tsconfigPaths from "vite-tsconfig-paths"; export default defineConfig({ - plugins: [tanstackRouter(), react(), tailwindcss(), tsconfigPaths()], + root: __dirname, + plugins: [ + tanstackRouter({ + routesDirectory: path.resolve(__dirname, "./src/routes"), + }), + react(), + tailwindcss(), + ], + oxc: {}, resolve: { + tsconfigPaths: true, alias: { "@": path.resolve(__dirname, "./src"), }, diff --git a/biome.json b/biome.json index 3246310..334e3d0 100644 --- a/biome.json +++ b/biome.json @@ -1,92 +1,98 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.7/schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true - }, - "files": { - "ignoreUnknown": false - }, - "formatter": { - "enabled": true, - "indentStyle": "tab" - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "a11y": { - "recommended": true, - "useButtonType": "error", - "useAltText": "error", - "useAnchorContent": "error", - "useHeadingContent": "error", - "useKeyWithClickEvents": "error", - "useKeyWithMouseEvents": "error", - "useValidAriaProps": "error", - "useValidAriaRole": "error", - "useValidAriaValues": "error", - "noAccessKey": "error", - "noAutofocus": "warn", - "noPositiveTabindex": "error", - "noRedundantAlt": "error", - "noRedundantRoles": "error", - "useFocusableInteractive": "error", - "useSemanticElements": "error" - }, - "correctness": { - "recommended": true, - "useExhaustiveDependencies": "error", - "useHookAtTopLevel": "error" - }, - "suspicious": { - "recommended": true, - "noExplicitAny": "warn" - }, - "style": { - "recommended": true, - "noNonNullAssertion": "error" - } - } - }, - "css": { - "parser": { - "tailwindDirectives": true - }, - "linter": { - "enabled": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - }, - "globals": ["React"] - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } - } - }, - "overrides": [ - { - "includes": ["**/__tests__/**", "**/*.test.ts", "**/*.test.tsx"], - "linter": { - "rules": { - "suspicious": { "noExplicitAny": "off" }, - "style": { "noNonNullAssertion": "off" } - } - } - }, - { - "includes": ["**/routeTree.gen.ts", "**/migrations/**"], - "linter": { "enabled": false }, - "formatter": { "enabled": false }, - "assist": { "enabled": false } - } - ] + "$schema": "https://biomejs.dev/schemas/2.3.7/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "recommended": true, + "useButtonType": "error", + "useAltText": "error", + "useAnchorContent": "error", + "useHeadingContent": "error", + "useKeyWithClickEvents": "error", + "useKeyWithMouseEvents": "error", + "useValidAriaProps": "error", + "useValidAriaRole": "error", + "useValidAriaValues": "error", + "noAccessKey": "error", + "noAutofocus": "warn", + "noPositiveTabindex": "error", + "noRedundantAlt": "error", + "noRedundantRoles": "error", + "useFocusableInteractive": "error", + "useSemanticElements": "error" + }, + "correctness": { + "recommended": true, + "useExhaustiveDependencies": "error", + "useHookAtTopLevel": "error" + }, + "suspicious": { + "recommended": true, + "noExplicitAny": "warn" + }, + "style": { + "recommended": true, + "noNonNullAssertion": "error" + } + } + }, + "css": { + "parser": { + "tailwindDirectives": true + }, + "linter": { + "enabled": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + }, + "globals": ["React"] + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "overrides": [ + { + "includes": ["**/__tests__/**", "**/*.test.ts", "**/*.test.tsx"], + "linter": { + "rules": { + "suspicious": { "noExplicitAny": "off" }, + "style": { "noNonNullAssertion": "off" } + } + } + }, + { + "includes": ["**/routeTree.gen.ts", "**/migrations/**"], + "linter": { "enabled": false }, + "formatter": { "enabled": false }, + "assist": { "enabled": false } + }, + { + "includes": ["**/deno.json", "**/deno.lock"], + "linter": { "enabled": false }, + "formatter": { "enabled": false }, + "assist": { "enabled": false } + } + ] } diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..a7a5007 --- /dev/null +++ b/deno.json @@ -0,0 +1,33 @@ +{ + "workspace": [ + "apps/backend", + "apps/frontend", + "packages/shared", + "packages/db", + "packages/email-templates" + ], + "tasks": { + "dev": "deno task --cwd=apps/backend dev", + "dev:backend": "deno task --cwd=apps/backend dev", + "dev:frontend": "deno task --cwd=apps/frontend dev", + "start": "deno task --cwd=apps/backend start", + "worker": "deno task --cwd=apps/backend worker", + "test": "deno task --cwd=apps/backend test", + "test:frontend": "deno task --cwd=apps/frontend test", + "lint": "deno lint", + "fmt": "deno fmt", + "check": "deno check apps/backend/src/index.ts apps/backend/src/jobs/worker.ts", + "db:migrate": "deno task --cwd=apps/backend db:migrate", + "db:studio": "deno task --cwd=apps/backend db:studio", + "db:generate": "deno task --cwd=apps/backend db:generate" + }, + "imports": { + "@std/expect": "jsr:@std/expect@^1.0.19", + "@std/testing/bdd": "jsr:@std/testing@^1.0.19/bdd" + }, + "fmt": { + "exclude": ["apps/frontend", "**/*.md", "packages/db/migrations"] + }, + "nodeModulesDir": "auto", + "sloppyImports": true +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..cebf4db --- /dev/null +++ b/deno.lock @@ -0,0 +1,3950 @@ +{ + "version": "5", + "specifiers": { + "jsr:@std/assert@^1.0.19": "1.0.19", + "jsr:@std/expect@^1.0.19": "1.0.19", + "jsr:@std/internal@^1.0.12": "1.0.14", + "jsr:@std/internal@^1.0.13": "1.0.14", + "jsr:@std/internal@^1.0.14": "1.0.14", + "jsr:@std/path@^1.1.4": "1.1.5", + "jsr:@std/testing@^1.0.19": "1.0.19", + "npm:@aws-sdk/client-s3@*": "3.1045.0", + "npm:@aws-sdk/s3-request-presigner@*": "3.1045.0", + "npm:@axiomhq/pino@*": "1.6.1", + "npm:@base-ui/react@^1.2.0": "1.4.1_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@better-fetch/fetch@^1.1.18": "1.1.21", + "npm:@biomejs/biome@2.3.7": "2.3.7", + "npm:@hono/swagger-ui@*": "0.6.1_hono@4.12.18", + "npm:@hono/zod-openapi@*": "1.4.0_hono@4.12.18_zod@4.4.3", + "npm:@hono/zod-validator@*": "0.8.0_hono@4.12.18_zod@4.4.3", + "npm:@node-rs/argon2@*": "2.0.2", + "npm:@scalar/hono-api-reference@*": "0.10.14_hono@4.12.18", + "npm:@tailwindcss/vite@^4.1.17": "4.3.0_vite@7.3.3", + "npm:@tanstack/react-form@1": "1.31.0_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@tanstack/react-query@^5.90.7": "5.100.9_react@19.2.6", + "npm:@tanstack/react-router-devtools@^1.134.13": "1.166.13_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_@tanstack+router-core@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6_csstype@3.2.3", + "npm:@tanstack/react-router@^1.134.13": "1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@tanstack/router-plugin@^1.134.14": "1.167.35_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_vite@7.3.3_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@testing-library/react@^16.3.2": "16.3.2_@testing-library+dom@10.4.1_@types+react@19.2.14_@types+react-dom@19.2.3__@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:@types/react-dom@^19.1.9": "19.2.3_@types+react@19.2.14", + "npm:@types/react@^19.1.16": "19.2.14", + "npm:@vitejs/plugin-react@^5.0.4": "5.2.0_vite@7.3.3", + "npm:better-auth@*": "1.6.10_drizzle-kit@0.31.10_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:better-auth@^1.3.34": "1.6.10_drizzle-kit@0.31.10_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:bullmq@*": "5.76.6", + "npm:class-variance-authority@~0.7.1": "0.7.1", + "npm:clsx@^2.1.1": "2.1.1", + "npm:dotenv-expand@*": "13.0.0", + "npm:dotenv@*": "17.4.2", + "npm:drizzle-kit@*": "0.31.10", + "npm:drizzle-orm@*": "0.45.2_kysely@0.28.17_postgres@3.4.9", + "npm:drizzle-zod@*": "0.8.3_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_zod@4.4.3_kysely@0.28.17_postgres@3.4.9", + "npm:hono-pino@*": "0.10.3_hono@4.12.18_pino@10.3.1", + "npm:hono@*": "4.12.18", + "npm:ioredis@*": "5.10.1", + "npm:jsdom@^30.0.1": "30.0.1", + "npm:lucide-react@0.553": "0.553.0_react@19.2.6", + "npm:pino-pretty@*": "13.1.3", + "npm:pino@*": "10.3.1", + "npm:postgres@*": "3.4.9", + "npm:react-dom@^19.1.1": "19.2.6_react@19.2.6", + "npm:react@^19.1.1": "19.2.6", + "npm:resend@*": "6.12.3", + "npm:sonner@^2.0.7": "2.0.7_react@19.2.6_react-dom@19.2.6__react@19.2.6", + "npm:stoker@*": "2.0.1_@hono+zod-openapi@1.4.0__hono@4.12.18__zod@4.4.3_hono@4.12.18_zod@4.4.3", + "npm:tailwind-merge@^3.3.1": "3.5.0", + "npm:tailwindcss@^4.1.17": "4.3.0", + "npm:typescript@~5.9.3": "5.9.3", + "npm:vite@*": "7.3.3", + "npm:vite@^7.1.7": "7.3.3", + "npm:zod@*": "4.4.3", + "npm:zod@^4.2.1": "4.4.3", + "npm:zustand@^5.0.8": "5.0.13_@types+react@19.2.14_react@19.2.6" + }, + "jsr": { + "@std/assert@1.0.19": { + "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e", + "dependencies": [ + "jsr:@std/internal@^1.0.12" + ] + }, + "@std/expect@1.0.19": { + "integrity": "25271785688c7ff902fa54875d6aa4001f552c5578f7f0fefb5b5aac1fc2ed8a", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/internal@^1.0.13", + "jsr:@std/path" + ] + }, + "@std/internal@1.0.14": { + "integrity": "291516b3d4c35024d6ffbc0a9df5bf4c64116e05b50012cf846710152d2ffdf7" + }, + "@std/path@1.1.5": { + "integrity": "ccea00982ea28c36becaf6e62f855406c76a8c32d462f66f415bbb7d83a271bc", + "dependencies": [ + "jsr:@std/internal@^1.0.14" + ] + }, + "@std/testing@1.0.19": { + "integrity": "f4236172365b216728dc3cc8b5e80a9f4c33083d1e4ede7613d5b25b4014898e", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/internal@^1.0.14" + ] + } + }, + "npm": { + "@asamuzakjp/css-color@6.0.5": { + "integrity": "sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==", + "dependencies": [ + "@csstools/css-calc", + "@csstools/css-color-parser", + "@csstools/css-parser-algorithms", + "@csstools/css-tokenizer", + "lru-cache@11.5.2" + ] + }, + "@asamuzakjp/dom-selector@8.3.2": { + "integrity": "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==", + "dependencies": [ + "bidi-js", + "css-tree", + "is-potential-custom-element-name", + "lru-cache@11.5.2" + ] + }, + "@asteasolutions/zod-to-openapi@8.5.0_zod@4.4.3": { + "integrity": "sha512-SABbKiObg5dLRiTFnqiW1WWwGcg1BJfmHtT2asIBnBHg6Smy/Ms2KHc650+JI4Hw7lSkdiNebEGXpwoxfben8Q==", + "dependencies": [ + "openapi3-ts", + "zod@4.4.3" + ] + }, + "@aws-crypto/crc32@5.2.0": { + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/crc32c@5.2.0": { + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/sha1-browser@5.2.0": { + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "dependencies": [ + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/types", + "@aws-sdk/util-locate-window", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-crypto/sha256-browser@5.2.0": { + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dependencies": [ + "@aws-crypto/sha256-js", + "@aws-crypto/supports-web-crypto", + "@aws-crypto/util", + "@aws-sdk/types", + "@aws-sdk/util-locate-window", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-crypto/sha256-js@5.2.0": { + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dependencies": [ + "@aws-crypto/util", + "@aws-sdk/types", + "tslib" + ] + }, + "@aws-crypto/supports-web-crypto@5.2.0": { + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dependencies": [ + "tslib" + ] + }, + "@aws-crypto/util@5.2.0": { + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/util-utf8@2.3.0", + "tslib" + ] + }, + "@aws-sdk/client-s3@3.1045.0": { + "integrity": "sha512-fsuO3Y6t+3Ro9Bsg41DKj4Sfy53CGSrhnMldNplWmG8Tx0UbYk+YDa4RD1hVlJpERw4JBmPkl0+J9qlxMh1pcA==", + "dependencies": [ + "@aws-crypto/sha1-browser", + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/core", + "@aws-sdk/credential-provider-node", + "@aws-sdk/middleware-bucket-endpoint", + "@aws-sdk/middleware-expect-continue", + "@aws-sdk/middleware-flexible-checksums", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-location-constraint", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-sdk-s3", + "@aws-sdk/middleware-ssec", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/eventstream-serde-browser", + "@smithy/eventstream-serde-config-resolver", + "@smithy/eventstream-serde-node", + "@smithy/fetch-http-handler", + "@smithy/hash-blob-browser", + "@smithy/hash-node", + "@smithy/hash-stream-node", + "@smithy/invalid-dependency", + "@smithy/md5-js", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "@smithy/util-waiter", + "tslib" + ] + }, + "@aws-sdk/core@3.974.8": { + "integrity": "sha512-njR2qoG6ZuB0kvAS2FyICsFZJ6gmCcf2X/7JcD14sUvGDm26wiZ5BrA6LOiUxKFEF+IVe7kdroxyE00YlkiYsw==", + "dependencies": [ + "@aws-sdk/types", + "@aws-sdk/xml-builder", + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-base64", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/crc64-nvme@3.972.7": { + "integrity": "sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-env@3.972.34": { + "integrity": "sha512-XT0jtf8Fw9JE6ppsQeoNnZRiG+jqRixMT1v1ZR17G60UvVdsQmTG8nbEyHuEPfMxDXEhfdARaM/XiEhca4lGHQ==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-http@3.972.36": { + "integrity": "sha512-DPoGWfy7J7RKxvbf5kOKIGQkD2ek3dbKgzKIGrnLuvZBz5myU+Im/H6pmc14QcnFbqHMqxvtWSgRDSJW3qXLQg==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-stream", + "tslib" + ] + }, + "@aws-sdk/credential-provider-ini@3.972.38": { + "integrity": "sha512-oDzUBu2MGJFgoar05sPMCwSrhw44ASyccrHzj66vO69OZqi7I6hZZxXfuPLC8OCzW7C+sU+bI73XHij41yekgQ==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-login", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-login@3.972.38": { + "integrity": "sha512-g1NosS8qe4OF++G2UFCM5ovSkgipC7YYor5KCWatG0UoMSO5YFj9C8muePlyVmOBV/WTI16Jo3/s1NUo/o1Bww==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/protocol-http", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-node@3.972.39": { + "integrity": "sha512-HEswDQyxUtadoZ/bJsPPENHg7R0Lzym5LuMksJeHvqhCOpP+rtkDLKI4/ZChH4w3cf5kG8n6bZuI8PzajoiqMg==", + "dependencies": [ + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/types", + "@smithy/credential-provider-imds", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-process@3.972.34": { + "integrity": "sha512-T3IFs4EVmVi1dVN5RciFnklCANSzvrQd/VuHY9ThHSQmYkTogjcGkoJEr+oNUPQZnso52183088NqysMPji1/Q==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-sso@3.972.38": { + "integrity": "sha512-5ZxG+t0+3Q3QPh8KEjX6syskhgNf7I0MN7oGioTf6Lm1NTjfP7sIcYGNsthXC2qR8vcD3edNZwCr2ovfSSWuRA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/token-providers", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-web-identity@3.972.38": { + "integrity": "sha512-lYHFF30DGI20jZcYX8cm6Ns0V7f1dDN6g/MBDLTyD/5iw+bXs3yBr2iAiHDkx4RFU5JgsnZvCHYKiRVPRdmOgw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-bucket-endpoint@3.972.10": { + "integrity": "sha512-Vbc2frZH7wXlMNd+ZZSXUEs/l1Sv8Jj4zUnIfwrYF5lwaLdXHZ9xx4U3rjUcaye3HRhFVc+E5DbBxpRAbB16BA==", + "dependencies": [ + "@aws-sdk/types", + "@aws-sdk/util-arn-parser", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-config-provider", + "tslib" + ] + }, + "@aws-sdk/middleware-expect-continue@3.972.10": { + "integrity": "sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-flexible-checksums@3.974.16": { + "integrity": "sha512-6ru8doI0/XzszqLIPXf0E/V7HhAw1Pu94010XCKYtBUfD0LxF0BuOzrUf8OQGR6j2o6wgKTHUniOmndQycHwCA==", + "dependencies": [ + "@aws-crypto/crc32", + "@aws-crypto/crc32c", + "@aws-crypto/util", + "@aws-sdk/core", + "@aws-sdk/crc64-nvme", + "@aws-sdk/types", + "@smithy/is-array-buffer@4.2.2", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/middleware-host-header@3.972.10": { + "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-location-constraint@3.972.10": { + "integrity": "sha512-rI3NZvJcEvjoD0+0PI0iUAwlPw2IlSlhyvgBK/3WkKJQE/YiKFedd9dMN2lVacdNxPNhxL/jzQaKQdrGtQagjQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-logger@3.972.10": { + "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-recursion-detection@3.972.11": { + "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==", + "dependencies": [ + "@aws-sdk/types", + "@aws/lambda-invoke-store", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-sdk-s3@3.972.37": { + "integrity": "sha512-Km7M+i8DrLArVzrid1gfxeGhYHBd3uxvE77g0s5a52zPSVosxzQBnJ0gwWb6NIp/DOk8gsBMhi7V+cpJG0ndTA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@aws-sdk/util-arn-parser", + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-config-provider", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/middleware-ssec@3.972.10": { + "integrity": "sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/middleware-user-agent@3.972.38": { + "integrity": "sha512-iz+B29TXcAZsJpwB+AwG/TTGA5l/VnmMZ2UxtiySOZjI6gCdmviXPwdgzcmuazMy16rXoPY4mYCGe7zdNKfx5A==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@smithy/core", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-retry", + "tslib" + ] + }, + "@aws-sdk/nested-clients@3.997.6": { + "integrity": "sha512-WBDnqatJl+kGObpfmfSxqnXeYTu3Me8wx8WCtvoxX3pfWrrTv8I4WTMSSs7PZqcRcVh8WeUKMgGFjMG+52SR1w==", + "dependencies": [ + "@aws-crypto/sha256-browser", + "@aws-crypto/sha256-js", + "@aws-sdk/core", + "@aws-sdk/middleware-host-header", + "@aws-sdk/middleware-logger", + "@aws-sdk/middleware-recursion-detection", + "@aws-sdk/middleware-user-agent", + "@aws-sdk/region-config-resolver", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-endpoints", + "@aws-sdk/util-user-agent-browser", + "@aws-sdk/util-user-agent-node", + "@smithy/config-resolver", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/hash-node", + "@smithy/invalid-dependency", + "@smithy/middleware-content-length", + "@smithy/middleware-endpoint", + "@smithy/middleware-retry", + "@smithy/middleware-serde", + "@smithy/middleware-stack", + "@smithy/node-config-provider", + "@smithy/node-http-handler", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-body-length-node", + "@smithy/util-defaults-mode-browser", + "@smithy/util-defaults-mode-node", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@aws-sdk/region-config-resolver@3.972.13": { + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/config-resolver", + "@smithy/node-config-provider", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/s3-request-presigner@3.1045.0": { + "integrity": "sha512-VDRF8GIuUPX+K4DUYrvcODj/h54LOmdJ7DhpLQ0wrYrdxzIiJEpi0n9jZ1bbjT2UxhwTbOorse5EGo+gnOK2aA==", + "dependencies": [ + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@aws-sdk/util-format-url", + "@smithy/middleware-endpoint", + "@smithy/protocol-http", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/signature-v4-multi-region@3.996.25": { + "integrity": "sha512-+CMIt3e1VzlklAECmG+DtP1sV8iKq25FuA0OKpnJ4KA0kxUtd7CgClY7/RU6VzJBQwbN4EJ9Ue6plvqx1qGadw==", + "dependencies": [ + "@aws-sdk/middleware-sdk-s3", + "@aws-sdk/types", + "@smithy/protocol-http", + "@smithy/signature-v4", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/token-providers@3.1041.0": { + "integrity": "sha512-Th7kPI6YPtvJUcdznooXJMy+9rQWjmEF81LxaJssngBzuysK4a/x+l8kjm1zb7nYsUPbndnBdUnwng/3PLvtGw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/types@3.973.8": { + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/util-arn-parser@3.972.3": { + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "dependencies": [ + "tslib" + ] + }, + "@aws-sdk/util-endpoints@3.996.8": { + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-endpoints", + "tslib" + ] + }, + "@aws-sdk/util-format-url@3.972.10": { + "integrity": "sha512-DEKiHNJVtNxdyTeQspzY+15Po/kHm6sF0Cs4HV9Q2+lplB63+DrvdeiSoOSdWEWAoO2RcY1veoXVDz2tWxWCgQ==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/querystring-builder", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/util-locate-window@3.965.5": { + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dependencies": [ + "tslib" + ] + }, + "@aws-sdk/util-user-agent-browser@3.972.10": { + "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/types", + "bowser", + "tslib" + ] + }, + "@aws-sdk/util-user-agent-node@3.973.24": { + "integrity": "sha512-ZWwlkjcIp7cEL8ZfTpTAPNkwx25p7xol0xlKoWVVf22+nsjwmLcHYtTPjIV1cSpmB/b6DaK4cb1fSkvCXHgRdw==", + "dependencies": [ + "@aws-sdk/middleware-user-agent", + "@aws-sdk/types", + "@smithy/node-config-provider", + "@smithy/types", + "@smithy/util-config-provider", + "tslib" + ] + }, + "@aws-sdk/xml-builder@3.972.22": { + "integrity": "sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==", + "dependencies": [ + "@nodable/entities", + "@smithy/types", + "fast-xml-parser", + "tslib" + ] + }, + "@aws/lambda-invoke-store@0.2.4": { + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==" + }, + "@axiomhq/js@1.6.1": { + "integrity": "sha512-iNWOnGvP+R2lIWYk9jkfvRI/rfhOR9hlWJOBJhMKwZ9mfpE0KdnFnG2XPLoMcrbyrOBKj6Jw0hpIH5GhPucLog==", + "dependencies": [ + "fetch-retry" + ] + }, + "@axiomhq/pino@1.6.1": { + "integrity": "sha512-T5mdwsrbPOkPu7dkBU/HnQvO8EoiVEO4HpqvGliDEgYv2bTVwE/ELiHep02UMVze/Gv3DEgQ/GQSIjbOqTAzYw==", + "dependencies": [ + "@axiomhq/js", + "pino-abstract-transport@1.2.0" + ] + }, + "@babel/code-frame@7.29.0": { + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dependencies": [ + "@babel/helper-validator-identifier", + "js-tokens", + "picocolors" + ] + }, + "@babel/compat-data@7.29.3": { + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==" + }, + "@babel/core@7.29.0": { + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dependencies": [ + "@babel/code-frame", + "@babel/generator", + "@babel/helper-compilation-targets", + "@babel/helper-module-transforms", + "@babel/helpers", + "@babel/parser", + "@babel/template", + "@babel/traverse", + "@babel/types", + "@jridgewell/remapping", + "convert-source-map", + "debug", + "gensync", + "json5", + "semver@6.3.1" + ] + }, + "@babel/generator@7.29.1": { + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping", + "jsesc" + ] + }, + "@babel/helper-compilation-targets@7.28.6": { + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dependencies": [ + "@babel/compat-data", + "@babel/helper-validator-option", + "browserslist", + "lru-cache@5.1.1", + "semver@6.3.1" + ] + }, + "@babel/helper-globals@7.28.0": { + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" + }, + "@babel/helper-module-imports@7.28.6": { + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dependencies": [ + "@babel/traverse", + "@babel/types" + ] + }, + "@babel/helper-module-transforms@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dependencies": [ + "@babel/core", + "@babel/helper-module-imports", + "@babel/helper-validator-identifier", + "@babel/traverse" + ] + }, + "@babel/helper-plugin-utils@7.28.6": { + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==" + }, + "@babel/helper-string-parser@7.27.1": { + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" + }, + "@babel/helper-validator-identifier@7.28.5": { + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" + }, + "@babel/helper-validator-option@7.27.1": { + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" + }, + "@babel/helpers@7.29.2": { + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dependencies": [ + "@babel/template", + "@babel/types" + ] + }, + "@babel/parser@7.29.3": { + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dependencies": [ + "@babel/types" + ], + "bin": true + }, + "@babel/plugin-syntax-jsx@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-syntax-typescript@7.28.6_@babel+core@7.29.0": { + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-self@7.27.1_@babel+core@7.29.0": { + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/plugin-transform-react-jsx-source@7.27.1_@babel+core@7.29.0": { + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dependencies": [ + "@babel/core", + "@babel/helper-plugin-utils" + ] + }, + "@babel/runtime@7.29.2": { + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==" + }, + "@babel/template@7.28.6": { + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dependencies": [ + "@babel/code-frame", + "@babel/parser", + "@babel/types" + ] + }, + "@babel/traverse@7.29.0": { + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dependencies": [ + "@babel/code-frame", + "@babel/generator", + "@babel/helper-globals", + "@babel/parser", + "@babel/template", + "@babel/types", + "debug" + ] + }, + "@babel/types@7.29.0": { + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dependencies": [ + "@babel/helper-string-parser", + "@babel/helper-validator-identifier" + ] + }, + "@base-ui/react@1.4.1_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-Ab5/LIhcmL8BQcsBUYiOfkSDRdLpvgUBzMK30cu684JPcLclYlztharvCZyNNgzJtbAiREzI9q0pI5erHCMgCw==", + "dependencies": [ + "@babel/runtime", + "@base-ui/utils", + "@floating-ui/react-dom", + "@floating-ui/utils", + "@types/react", + "react", + "react-dom", + "use-sync-external-store" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@base-ui/utils@0.2.8_@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ==", + "dependencies": [ + "@babel/runtime", + "@floating-ui/utils", + "@types/react", + "react", + "react-dom", + "reselect", + "use-sync-external-store" + ], + "optionalPeers": [ + "@types/react" + ] + }, + "@better-auth/core@1.6.10_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-13h/rfSGMLl7zwyOb1BSlxAQZs2nQqn/xFI/bxB7zQuS95hVgTmNbKqhHtJYkNDtuJCcjEf1sNtLBHkvaPT/vw==", + "dependencies": [ + "@better-auth/utils", + "@better-fetch/fetch", + "@opentelemetry/semantic-conventions", + "@standard-schema/spec", + "better-call", + "jose", + "kysely", + "nanostores", + "zod@4.4.3" + ] + }, + "@better-auth/drizzle-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0_postgres@3.4.9": { + "integrity": "sha512-Ax0Jlpvuu35P3U6FtUGfkLAUmBwYIF+JwwtHt+jBlOEQNIiBhbLHh8ArQxrKJFRTDYv/XoSsrvJ5OluPsUFuuQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "drizzle-orm" + ], + "optionalPeers": [ + "drizzle-orm" + ] + }, + "@better-auth/kysely-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_kysely@0.28.17_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_nanostores@1.3.0": { + "integrity": "sha512-Mp27qHgnvNCkkVEMRwhtMVpiiVFBnww0V+bunRWNU8fkxsm6H+GIBihUQOnkSCnIjV7f2HNjrf5DeYI2IhDePQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "kysely" + ], + "optionalPeers": [ + "kysely" + ] + }, + "@better-auth/memory-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-zXk7GXnOpafAjCJ3+boh8hTEmUozGCLQpCl4plH9sAix6UlMdpYmdDTEGd+I8zungiEK+jzw4oevy7IirzKrrw==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/mongo-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-EkbK3j8qwE9STteWoUh7vkve7n7/jSlkI0e9onAwr3YuE+an8scG7BgTHoDXku2qPlVa+KFPmcWc1FX6K/N6xA==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/prisma-adapter@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-p/eJXl/RtHLt/A75chX/P55gjMzo5tWSJyfAyICts1miGHFUsu6D2TmKSzF7KNtjucjjzRKmtFl6BwMOxXXf2Q==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils" + ] + }, + "@better-auth/telemetry@1.6.10_@better-auth+core@1.6.10__@better-auth+utils@0.4.0__@better-fetch+fetch@1.1.21__better-call@1.3.5___zod@4.4.3__jose@6.2.3__kysely@0.28.17__nanostores@1.3.0_@better-auth+utils@0.4.0_@better-fetch+fetch@1.1.21_better-call@1.3.5__zod@4.4.3_jose@6.2.3_kysely@0.28.17_nanostores@1.3.0": { + "integrity": "sha512-7lcx4btKGe4tP7Y1Nk6MWQuaiKI+qOk08B4vZFJeNKxRXyCNjVmdck78NyOTEj3iaVxN69MiXDoBZ4fEdvVbBw==", + "dependencies": [ + "@better-auth/core", + "@better-auth/utils", + "@better-fetch/fetch" + ] + }, + "@better-auth/utils@0.4.0": { + "integrity": "sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==", + "dependencies": [ + "@noble/hashes" + ] + }, + "@better-fetch/fetch@1.1.21": { + "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==" + }, + "@biomejs/biome@2.3.7": { + "integrity": "sha512-CTbAS/jNAiUc6rcq94BrTB8z83O9+BsgWj2sBCQg9rD6Wkh2gjfR87usjx0Ncx0zGXP1NKgT7JNglay5Zfs9jw==", + "optionalDependencies": [ + "@biomejs/cli-darwin-arm64", + "@biomejs/cli-darwin-x64", + "@biomejs/cli-linux-arm64", + "@biomejs/cli-linux-arm64-musl", + "@biomejs/cli-linux-x64", + "@biomejs/cli-linux-x64-musl", + "@biomejs/cli-win32-arm64", + "@biomejs/cli-win32-x64" + ], + "bin": true + }, + "@biomejs/cli-darwin-arm64@2.3.7": { + "integrity": "sha512-LirkamEwzIUULhXcf2D5b+NatXKeqhOwilM+5eRkbrnr6daKz9rsBL0kNZ16Hcy4b8RFq22SG4tcLwM+yx/wFA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@biomejs/cli-darwin-x64@2.3.7": { + "integrity": "sha512-Q4TO633kvrMQkKIV7wmf8HXwF0dhdTD9S458LGE24TYgBjSRbuhvio4D5eOQzirEYg6eqxfs53ga/rbdd8nBKg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-arm64-musl@2.3.7": { + "integrity": "sha512-/afy8lto4CB8scWfMdt+NoCZtatBUF62Tk3ilWH2w8ENd5spLhM77zKlFZEvsKJv9AFNHknMl03zO67CiklL2Q==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-arm64@2.3.7": { + "integrity": "sha512-inHOTdlstUBzgjDcx0ge71U4SVTbwAljmkfi3MC5WzsYCRhancqfeL+sa4Ke6v2ND53WIwCFD5hGsYExoI3EZQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@biomejs/cli-linux-x64-musl@2.3.7": { + "integrity": "sha512-CQUtgH1tIN6e5wiYSJqzSwJumHYolNtaj1dwZGCnZXm2PZU1jOJof9TsyiP3bXNDb+VOR7oo7ZvY01If0W3iFQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-linux-x64@2.3.7": { + "integrity": "sha512-fJMc3ZEuo/NaMYo5rvoWjdSS5/uVSW+HPRQujucpZqm2ZCq71b8MKJ9U4th9yrv2L5+5NjPF0nqqILCl8HY/fg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@biomejs/cli-win32-arm64@2.3.7": { + "integrity": "sha512-aJAE8eCNyRpcfx2JJAtsPtISnELJ0H4xVVSwnxm13bzI8RwbXMyVtxy2r5DV1xT3WiSP+7LxORcApWw0LM8HiA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@biomejs/cli-win32-x64@2.3.7": { + "integrity": "sha512-pulzUshqv9Ed//MiE8MOUeeEkbkSHVDVY5Cz5wVAnH1DUqliCQG3j6s1POaITTFqFfo7AVIx2sWdKpx/GS+Nqw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@bramus/specificity@2.4.2": { + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dependencies": [ + "css-tree" + ], + "bin": true + }, + "@csstools/color-helpers@6.1.0": { + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==" + }, + "@csstools/css-calc@3.3.0_@csstools+css-parser-algorithms@4.0.0__@csstools+css-tokenizer@4.0.0_@csstools+css-tokenizer@4.0.0": { + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dependencies": [ + "@csstools/css-parser-algorithms", + "@csstools/css-tokenizer" + ] + }, + "@csstools/css-color-parser@4.1.10_@csstools+css-parser-algorithms@4.0.0__@csstools+css-tokenizer@4.0.0_@csstools+css-tokenizer@4.0.0": { + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", + "dependencies": [ + "@csstools/color-helpers", + "@csstools/css-calc", + "@csstools/css-parser-algorithms", + "@csstools/css-tokenizer" + ] + }, + "@csstools/css-parser-algorithms@4.0.0_@csstools+css-tokenizer@4.0.0": { + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dependencies": [ + "@csstools/css-tokenizer" + ] + }, + "@csstools/css-syntax-patches-for-csstree@1.1.7_css-tree@3.2.1": { + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", + "dependencies": [ + "css-tree" + ], + "optionalPeers": [ + "css-tree" + ] + }, + "@csstools/css-tokenizer@4.0.0": { + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==" + }, + "@drizzle-team/brocli@0.10.2": { + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==" + }, + "@emnapi/core@1.10.0": { + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dependencies": [ + "@emnapi/wasi-threads", + "tslib" + ] + }, + "@emnapi/runtime@1.10.0": { + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dependencies": [ + "tslib" + ] + }, + "@emnapi/wasi-threads@1.2.1": { + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dependencies": [ + "tslib" + ] + }, + "@esbuild-kit/core-utils@3.3.2": { + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "dependencies": [ + "esbuild@0.18.20", + "source-map-support" + ], + "deprecated": true + }, + "@esbuild-kit/esm-loader@2.6.5": { + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "dependencies": [ + "@esbuild-kit/core-utils", + "get-tsconfig" + ], + "deprecated": true + }, + "@esbuild/aix-ppc64@0.25.12": { + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/aix-ppc64@0.27.7": { + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/android-arm64@0.18.20": { + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm64@0.25.12": { + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm64@0.27.7": { + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm@0.18.20": { + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-arm@0.25.12": { + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-arm@0.27.7": { + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-x64@0.18.20": { + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/android-x64@0.25.12": { + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/android-x64@0.27.7": { + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/darwin-arm64@0.18.20": { + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-arm64@0.25.12": { + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-arm64@0.27.7": { + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-x64@0.18.20": { + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/darwin-x64@0.25.12": { + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/darwin-x64@0.27.7": { + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-arm64@0.18.20": { + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-arm64@0.25.12": { + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-arm64@0.27.7": { + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-x64@0.18.20": { + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-x64@0.25.12": { + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-x64@0.27.7": { + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/linux-arm64@0.18.20": { + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm64@0.25.12": { + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm64@0.27.7": { + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm@0.18.20": { + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-arm@0.25.12": { + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-arm@0.27.7": { + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-ia32@0.18.20": { + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-ia32@0.25.12": { + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-ia32@0.27.7": { + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-loong64@0.18.20": { + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-loong64@0.25.12": { + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-loong64@0.27.7": { + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-mips64el@0.18.20": { + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-mips64el@0.25.12": { + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-mips64el@0.27.7": { + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-ppc64@0.18.20": { + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-ppc64@0.25.12": { + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-ppc64@0.27.7": { + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-riscv64@0.18.20": { + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-riscv64@0.25.12": { + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-riscv64@0.27.7": { + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-s390x@0.18.20": { + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-s390x@0.25.12": { + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-s390x@0.27.7": { + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-x64@0.18.20": { + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/linux-x64@0.25.12": { + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/linux-x64@0.27.7": { + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-arm64@0.25.12": { + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-arm64@0.27.7": { + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-x64@0.18.20": { + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-x64@0.25.12": { + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-x64@0.27.7": { + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-arm64@0.25.12": { + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-arm64@0.27.7": { + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-x64@0.18.20": { + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-x64@0.25.12": { + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-x64@0.27.7": { + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openharmony-arm64@0.25.12": { + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/openharmony-arm64@0.27.7": { + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/sunos-x64@0.18.20": { + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/sunos-x64@0.25.12": { + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/sunos-x64@0.27.7": { + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/win32-arm64@0.18.20": { + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-arm64@0.25.12": { + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-arm64@0.27.7": { + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-ia32@0.18.20": { + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-ia32@0.25.12": { + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-ia32@0.27.7": { + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-x64@0.18.20": { + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@esbuild/win32-x64@0.25.12": { + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@esbuild/win32-x64@0.27.7": { + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@exodus/bytes@1.15.1": { + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==" + }, + "@floating-ui/core@1.7.5": { + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "dependencies": [ + "@floating-ui/utils" + ] + }, + "@floating-ui/dom@1.7.6": { + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "dependencies": [ + "@floating-ui/core", + "@floating-ui/utils" + ] + }, + "@floating-ui/react-dom@2.1.8_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "dependencies": [ + "@floating-ui/dom", + "react", + "react-dom" + ] + }, + "@floating-ui/utils@0.2.11": { + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==" + }, + "@hono/swagger-ui@0.6.1_hono@4.12.18": { + "integrity": "sha512-sJTvldu1GPeEPfyeLG7gRj+W4vEuD+JDi+JjJ3TJs/DvMUtBLs0KJO5yokGegWWdy5qrbdnQGekbhgNRmPmYKQ==", + "dependencies": [ + "hono" + ] + }, + "@hono/zod-openapi@1.4.0_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-AFchqR1N/NxfI4hUOSGI2/g8zLROxA1OE7Oh5JJFlTaGxhrdRyH+93gd0tIBpb0z8s9r8hUoNnaOBfHbdb4NMw==", + "dependencies": [ + "@asteasolutions/zod-to-openapi", + "@hono/zod-validator", + "hono", + "openapi3-ts", + "zod@4.4.3" + ] + }, + "@hono/zod-validator@0.8.0_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-5uS4S1/LKtZQYvD4BtpPUFkOv8d1wNxHHrChm26buMiEYc1FrHWvDUaKVBwkiVtvSExHSpLGDvcnpI2Copyj9w==", + "dependencies": [ + "hono", + "zod@4.4.3" + ] + }, + "@ioredis/commands@1.5.1": { + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==" + }, + "@jridgewell/gen-mapping@0.3.13": { + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dependencies": [ + "@jridgewell/sourcemap-codec", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/remapping@2.3.5": { + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dependencies": [ + "@jridgewell/gen-mapping", + "@jridgewell/trace-mapping" + ] + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" + }, + "@jridgewell/sourcemap-codec@1.5.5": { + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@jridgewell/trace-mapping@0.3.31": { + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dependencies": [ + "@jridgewell/resolve-uri", + "@jridgewell/sourcemap-codec" + ] + }, + "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": { + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": { + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": { + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": { + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": { + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": { + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@napi-rs/wasm-runtime@0.2.12": { + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dependencies": [ + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util" + ] + }, + "@noble/ciphers@2.2.0": { + "integrity": "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==" + }, + "@noble/hashes@2.2.0": { + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==" + }, + "@nodable/entities@2.1.0": { + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==" + }, + "@node-rs/argon2-android-arm-eabi@2.0.2": { + "integrity": "sha512-DV/H8p/jt40lrao5z5g6nM9dPNPGEHL+aK6Iy/og+dbL503Uj0AHLqj1Hk9aVUSCNnsDdUEKp4TVMi0YakDYKw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@node-rs/argon2-android-arm64@2.0.2": { + "integrity": "sha512-1LKwskau+8O1ktKx7TbK7jx1oMOMt4YEXZOdSNIar1TQKxm6isZ0cRXgHLibPHEcNHgYRsJWDE9zvDGBB17QDg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-darwin-arm64@2.0.2": { + "integrity": "sha512-3TTNL/7wbcpNju5YcqUrCgXnXUSbD7ogeAKatzBVHsbpjZQbNb1NDxDjqqrWoTt6XL3z9mJUMGwbAk7zQltHtA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-darwin-x64@2.0.2": { + "integrity": "sha512-vNPfkLj5Ij5111UTiYuwgxMqE7DRbOS2y58O2DIySzSHbcnu+nipmRKg+P0doRq6eKIJStyBK8dQi5Ic8pFyDw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@node-rs/argon2-freebsd-x64@2.0.2": { + "integrity": "sha512-M8vQZk01qojQfCqQU0/O1j1a4zPPrz93zc9fSINY7Q/6RhQRBCYwDw7ltDCZXg5JRGlSaeS8cUXWyhPGar3cGg==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@node-rs/argon2-linux-arm-gnueabihf@2.0.2": { + "integrity": "sha512-7EmmEPHLzcu0G2GDh30L6G48CH38roFC2dqlQJmtRCxs6no3tTE/pvgBGatTp/o2n2oyOJcfmgndVFcUpwMnww==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@node-rs/argon2-linux-arm64-gnu@2.0.2": { + "integrity": "sha512-6lsYh3Ftbk+HAIZ7wNuRF4SZDtxtFTfK+HYFAQQyW7Ig3LHqasqwfUKRXVSV5tJ+xTnxjqgKzvZSUJCAyIfHew==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-linux-arm64-musl@2.0.2": { + "integrity": "sha512-p3YqVMNT/4DNR67tIHTYGbedYmXxW9QlFmF39SkXyEbGQwpgSf6pH457/fyXBIYznTU/smnG9EH+C1uzT5j4hA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-linux-x64-gnu@2.0.2": { + "integrity": "sha512-ZM3jrHuJ0dKOhvA80gKJqBpBRmTJTFSo2+xVZR+phQcbAKRlDMSZMFDiKbSTnctkfwNFtjgDdh5g1vaEV04AvA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@node-rs/argon2-linux-x64-musl@2.0.2": { + "integrity": "sha512-of5uPqk7oCRF/44a89YlWTEfjsftPywyTULwuFDKyD8QtVZoonrJR6ZWvfFE/6jBT68S0okAkAzzMEdBVWdxWw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@node-rs/argon2-wasm32-wasi@2.0.2": { + "integrity": "sha512-U3PzLYKSQYzTERstgtHLd4ZTkOF9co57zTXT77r0cVUsleGZOrd6ut7rHzeWwoJSiHOVxxa0OhG1JVQeB7lLoQ==", + "dependencies": [ + "@napi-rs/wasm-runtime" + ], + "cpu": ["wasm32"] + }, + "@node-rs/argon2-win32-arm64-msvc@2.0.2": { + "integrity": "sha512-Eisd7/NM0m23ijrGr6xI2iMocdOuyl6gO27gfMfya4C5BODbUSP7ljKJ7LrA0teqZMdYHesRDzx36Js++/vhiQ==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@node-rs/argon2-win32-ia32-msvc@2.0.2": { + "integrity": "sha512-GsE2ezwAYwh72f9gIjbGTZOf4HxEksb5M2eCaj+Y5rGYVwAdt7C12Q2e9H5LRYxWcFvLH4m4jiSZpQQ4upnPAQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@node-rs/argon2-win32-x64-msvc@2.0.2": { + "integrity": "sha512-cJxWXanH4Ew9CfuZ4IAEiafpOBCe97bzoKowHCGk5lG/7kR4WF/eknnBlHW9m8q7t10mKq75kruPLtbSDqgRTw==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@node-rs/argon2@2.0.2": { + "integrity": "sha512-t64wIsPEtNd4aUPuTAyeL2ubxATCBGmeluaKXEMAFk/8w6AJIVVkeLKMBpgLW6LU2t5cQxT+env/c6jxbtTQBg==", + "optionalDependencies": [ + "@node-rs/argon2-android-arm-eabi", + "@node-rs/argon2-android-arm64", + "@node-rs/argon2-darwin-arm64", + "@node-rs/argon2-darwin-x64", + "@node-rs/argon2-freebsd-x64", + "@node-rs/argon2-linux-arm-gnueabihf", + "@node-rs/argon2-linux-arm64-gnu", + "@node-rs/argon2-linux-arm64-musl", + "@node-rs/argon2-linux-x64-gnu", + "@node-rs/argon2-linux-x64-musl", + "@node-rs/argon2-wasm32-wasi", + "@node-rs/argon2-win32-arm64-msvc", + "@node-rs/argon2-win32-ia32-msvc", + "@node-rs/argon2-win32-x64-msvc" + ] + }, + "@opentelemetry/semantic-conventions@1.40.0": { + "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==" + }, + "@pinojs/redact@0.4.0": { + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" + }, + "@rolldown/pluginutils@1.0.0-rc.3": { + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==" + }, + "@rollup/rollup-android-arm-eabi@4.60.3": { + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "os": ["android"], + "cpu": ["arm"] + }, + "@rollup/rollup-android-arm64@4.60.3": { + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-arm64@4.60.3": { + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@rollup/rollup-darwin-x64@4.60.3": { + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@rollup/rollup-freebsd-arm64@4.60.3": { + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@rollup/rollup-freebsd-x64@4.60.3": { + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-arm-gnueabihf@4.60.3": { + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm-musleabihf@4.60.3": { + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rollup/rollup-linux-arm64-gnu@4.60.3": { + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-arm64-musl@4.60.3": { + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rollup/rollup-linux-loong64-gnu@4.60.3": { + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-loong64-musl@4.60.3": { + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@rollup/rollup-linux-ppc64-gnu@4.60.3": { + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-ppc64-musl@4.60.3": { + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rollup/rollup-linux-riscv64-gnu@4.60.3": { + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-riscv64-musl@4.60.3": { + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@rollup/rollup-linux-s390x-gnu@4.60.3": { + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@rollup/rollup-linux-x64-gnu@4.60.3": { + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-linux-x64-musl@4.60.3": { + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rollup/rollup-openbsd-x64@4.60.3": { + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@rollup/rollup-openharmony-arm64@4.60.3": { + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-arm64-msvc@4.60.3": { + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@rollup/rollup-win32-ia32-msvc@4.60.3": { + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@rollup/rollup-win32-x64-gnu@4.60.3": { + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@rollup/rollup-win32-x64-msvc@4.60.3": { + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@scalar/client-side-rendering@0.1.7": { + "integrity": "sha512-IDzjKF93jrOljlvKBsLHXT1FPWgz56jFrMPC+iLihREp1qH8wF92mG8Zpakw8cURkEuw5WijRk0xNBP2moGyuw==", + "dependencies": [ + "@scalar/types" + ] + }, + "@scalar/helpers@0.6.0": { + "integrity": "sha512-pfSamAgBxqFeE8IpEG6uGkHlnPhY1CLeOTttV9+vKQbrBk5b7vvyTsUXv0Hz4kNU1TFrxcTTPE+Akn5S+jlTtQ==" + }, + "@scalar/hono-api-reference@0.10.14_hono@4.12.18": { + "integrity": "sha512-LCIT4ul3c4MyD7shhxsWcvvOABt0fEHNQID2n+2TPeItc/MR2qCjjp/QfqD+JoQ7zbc0nnzh1kwRR06MVBmnUA==", + "dependencies": [ + "@scalar/client-side-rendering", + "hono" + ] + }, + "@scalar/types@0.9.6": { + "integrity": "sha512-UaCQQcscFTJdxZREE8KhUdSJgaDlc44TZbmWcZffs4m1hzqOvEI7lEBS13iBpLq7/cxUXFgyJdecywvNqJ0PkA==", + "dependencies": [ + "@scalar/helpers", + "nanoid@5.1.11", + "type-fest", + "zod@4.4.3" + ] + }, + "@smithy/chunked-blob-reader-native@4.2.3": { + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "dependencies": [ + "@smithy/util-base64", + "tslib" + ] + }, + "@smithy/chunked-blob-reader@5.2.2": { + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/config-resolver@4.4.17": { + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/types", + "@smithy/util-config-provider", + "@smithy/util-endpoints", + "@smithy/util-middleware", + "tslib" + ] + }, + "@smithy/core@3.23.17": { + "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-base64", + "@smithy/util-body-length-browser", + "@smithy/util-middleware", + "@smithy/util-stream", + "@smithy/util-utf8@4.2.2", + "@smithy/uuid", + "tslib" + ] + }, + "@smithy/credential-provider-imds@4.2.14": { + "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/types", + "@smithy/url-parser", + "tslib" + ] + }, + "@smithy/eventstream-codec@4.2.14": { + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "dependencies": [ + "@aws-crypto/crc32", + "@smithy/types", + "@smithy/util-hex-encoding", + "tslib" + ] + }, + "@smithy/eventstream-serde-browser@4.2.14": { + "integrity": "sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==", + "dependencies": [ + "@smithy/eventstream-serde-universal", + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-config-resolver@4.3.14": { + "integrity": "sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-node@4.2.14": { + "integrity": "sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==", + "dependencies": [ + "@smithy/eventstream-serde-universal", + "@smithy/types", + "tslib" + ] + }, + "@smithy/eventstream-serde-universal@4.2.14": { + "integrity": "sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==", + "dependencies": [ + "@smithy/eventstream-codec", + "@smithy/types", + "tslib" + ] + }, + "@smithy/fetch-http-handler@5.3.17": { + "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/types", + "@smithy/util-base64", + "tslib" + ] + }, + "@smithy/hash-blob-browser@4.2.15": { + "integrity": "sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA==", + "dependencies": [ + "@smithy/chunked-blob-reader", + "@smithy/chunked-blob-reader-native", + "@smithy/types", + "tslib" + ] + }, + "@smithy/hash-node@4.2.14": { + "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==", + "dependencies": [ + "@smithy/types", + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/hash-stream-node@4.2.14": { + "integrity": "sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ==", + "dependencies": [ + "@smithy/types", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/invalid-dependency@4.2.14": { + "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/is-array-buffer@2.2.0": { + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/is-array-buffer@4.2.2": { + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/md5-js@4.2.14": { + "integrity": "sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA==", + "dependencies": [ + "@smithy/types", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/middleware-content-length@4.2.14": { + "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@smithy/middleware-endpoint@4.4.32": { + "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==", + "dependencies": [ + "@smithy/core", + "@smithy/middleware-serde", + "@smithy/node-config-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "@smithy/url-parser", + "@smithy/util-middleware", + "tslib" + ] + }, + "@smithy/middleware-retry@4.5.7": { + "integrity": "sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==", + "dependencies": [ + "@smithy/core", + "@smithy/node-config-provider", + "@smithy/protocol-http", + "@smithy/service-error-classification", + "@smithy/smithy-client", + "@smithy/types", + "@smithy/util-middleware", + "@smithy/util-retry", + "@smithy/uuid", + "tslib" + ] + }, + "@smithy/middleware-serde@4.2.20": { + "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==", + "dependencies": [ + "@smithy/core", + "@smithy/protocol-http", + "@smithy/types", + "tslib" + ] + }, + "@smithy/middleware-stack@4.2.14": { + "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/node-config-provider@4.3.14": { + "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==", + "dependencies": [ + "@smithy/property-provider", + "@smithy/shared-ini-file-loader", + "@smithy/types", + "tslib" + ] + }, + "@smithy/node-http-handler@4.6.1": { + "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==", + "dependencies": [ + "@smithy/protocol-http", + "@smithy/querystring-builder", + "@smithy/types", + "tslib" + ] + }, + "@smithy/property-provider@4.2.14": { + "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/protocol-http@5.3.14": { + "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/querystring-builder@4.2.14": { + "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==", + "dependencies": [ + "@smithy/types", + "@smithy/util-uri-escape", + "tslib" + ] + }, + "@smithy/querystring-parser@4.2.14": { + "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/service-error-classification@4.3.1": { + "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==", + "dependencies": [ + "@smithy/types" + ] + }, + "@smithy/shared-ini-file-loader@4.4.9": { + "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/signature-v4@5.3.14": { + "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==", + "dependencies": [ + "@smithy/is-array-buffer@4.2.2", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-hex-encoding", + "@smithy/util-middleware", + "@smithy/util-uri-escape", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/smithy-client@4.12.13": { + "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==", + "dependencies": [ + "@smithy/core", + "@smithy/middleware-endpoint", + "@smithy/middleware-stack", + "@smithy/protocol-http", + "@smithy/types", + "@smithy/util-stream", + "tslib" + ] + }, + "@smithy/types@4.14.1": { + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/url-parser@4.2.14": { + "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==", + "dependencies": [ + "@smithy/querystring-parser", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-base64@4.3.2": { + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "dependencies": [ + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/util-body-length-browser@4.2.2": { + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-body-length-node@4.2.3": { + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-buffer-from@2.2.0": { + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dependencies": [ + "@smithy/is-array-buffer@2.2.0", + "tslib" + ] + }, + "@smithy/util-buffer-from@4.2.2": { + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "dependencies": [ + "@smithy/is-array-buffer@4.2.2", + "tslib" + ] + }, + "@smithy/util-config-provider@4.2.2": { + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-defaults-mode-browser@4.3.49": { + "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==", + "dependencies": [ + "@smithy/property-provider", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-defaults-mode-node@4.2.54": { + "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==", + "dependencies": [ + "@smithy/config-resolver", + "@smithy/credential-provider-imds", + "@smithy/node-config-provider", + "@smithy/property-provider", + "@smithy/smithy-client", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-endpoints@3.4.2": { + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", + "dependencies": [ + "@smithy/node-config-provider", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-hex-encoding@4.2.2": { + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-middleware@4.2.14": { + "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-retry@4.3.8": { + "integrity": "sha512-LUIxbTBi+OpvXpg91poGA6BdyoleMDLnfXjVDqyi2RvZmTveY5loE/FgYUBCR5LU2BThW2SoZRh8dTIIy38IPw==", + "dependencies": [ + "@smithy/service-error-classification", + "@smithy/types", + "tslib" + ] + }, + "@smithy/util-stream@4.5.25": { + "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==", + "dependencies": [ + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/types", + "@smithy/util-base64", + "@smithy/util-buffer-from@4.2.2", + "@smithy/util-hex-encoding", + "@smithy/util-utf8@4.2.2", + "tslib" + ] + }, + "@smithy/util-uri-escape@4.2.2": { + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "dependencies": [ + "tslib" + ] + }, + "@smithy/util-utf8@2.3.0": { + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dependencies": [ + "@smithy/util-buffer-from@2.2.0", + "tslib" + ] + }, + "@smithy/util-utf8@4.2.2": { + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dependencies": [ + "@smithy/util-buffer-from@4.2.2", + "tslib" + ] + }, + "@smithy/util-waiter@4.3.0": { + "integrity": "sha512-JyjYmLAfS+pdxF92o4yLgEoy0zhayKTw73FU1aofLWwLcJw7iSqIY2exGmMTrl/lmZugP5p/zxdFSippJDfKWA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/uuid@1.1.2": { + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "dependencies": [ + "tslib" + ] + }, + "@stablelib/base64@1.0.1": { + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, + "@standard-schema/spec@1.1.0": { + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "@tailwindcss/node@4.3.0": { + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dependencies": [ + "@jridgewell/remapping", + "enhanced-resolve", + "jiti", + "lightningcss", + "magic-string", + "source-map-js", + "tailwindcss" + ] + }, + "@tailwindcss/oxide-android-arm64@4.3.0": { + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-arm64@4.3.0": { + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-darwin-x64@4.3.0": { + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-freebsd-x64@4.3.0": { + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0": { + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@tailwindcss/oxide-linux-arm64-gnu@4.3.0": { + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-arm64-musl@4.3.0": { + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-linux-x64-gnu@4.3.0": { + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-linux-x64-musl@4.3.0": { + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide-wasm32-wasi@4.3.0": { + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "cpu": ["wasm32"] + }, + "@tailwindcss/oxide-win32-arm64-msvc@4.3.0": { + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@tailwindcss/oxide-win32-x64-msvc@4.3.0": { + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@tailwindcss/oxide@4.3.0": { + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "optionalDependencies": [ + "@tailwindcss/oxide-android-arm64", + "@tailwindcss/oxide-darwin-arm64", + "@tailwindcss/oxide-darwin-x64", + "@tailwindcss/oxide-freebsd-x64", + "@tailwindcss/oxide-linux-arm-gnueabihf", + "@tailwindcss/oxide-linux-arm64-gnu", + "@tailwindcss/oxide-linux-arm64-musl", + "@tailwindcss/oxide-linux-x64-gnu", + "@tailwindcss/oxide-linux-x64-musl", + "@tailwindcss/oxide-wasm32-wasi", + "@tailwindcss/oxide-win32-arm64-msvc", + "@tailwindcss/oxide-win32-x64-msvc" + ] + }, + "@tailwindcss/vite@4.3.0_vite@7.3.3": { + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dependencies": [ + "@tailwindcss/node", + "@tailwindcss/oxide", + "tailwindcss", + "vite" + ] + }, + "@tanstack/devtools-event-client@0.4.3": { + "integrity": "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==", + "bin": true + }, + "@tanstack/form-core@1.31.0": { + "integrity": "sha512-t5G/LnrM/U10mQgzYis/WvHc6yTDj7jyF5cYf6GjVJpEbn0et0wDUQtafiOXY8hiXJ9D1HUqBi0u76ZX05uiHw==", + "dependencies": [ + "@tanstack/devtools-event-client", + "@tanstack/pacer-lite", + "@tanstack/store" + ] + }, + "@tanstack/history@1.161.6": { + "integrity": "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==" + }, + "@tanstack/pacer-lite@0.1.1": { + "integrity": "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==" + }, + "@tanstack/query-core@5.100.9": { + "integrity": "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==" + }, + "@tanstack/react-form@1.31.0_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-31hoeKlk45jCAnry85bOiL89FwySL9HtAhfWLHmCJ5WcvBlHJpoqnMvl6Cy8DM+7ey2eWHKiPiEL29BkG5o12w==", + "dependencies": [ + "@tanstack/form-core", + "@tanstack/react-store", + "react" + ] + }, + "@tanstack/react-query@5.100.9_react@19.2.6": { + "integrity": "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==", + "dependencies": [ + "@tanstack/query-core", + "react" + ] + }, + "@tanstack/react-router-devtools@1.166.13_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_@tanstack+router-core@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6_csstype@3.2.3": { + "integrity": "sha512-6yKRFFJrEEOiGp5RAAuGCYsl81M4XAhJmLcu9PKj+HZle4A3dsP60lwHoqQYWHMK9nKKFkdXR+D8qxzxqtQbEA==", + "dependencies": [ + "@tanstack/react-router", + "@tanstack/router-core", + "@tanstack/router-devtools-core", + "react", + "react-dom" + ], + "optionalPeers": [ + "@tanstack/router-core" + ] + }, + "@tanstack/react-router@1.169.2_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-OJM7Kguc7ERnweaNRWsyWgIKcl3z23rD1B4jaxjzd9RGdnzpt2HfrWa9rggbT0Hfzhfo4D2ZmsfoTme035tniQ==", + "dependencies": [ + "@tanstack/history", + "@tanstack/react-store", + "@tanstack/router-core", + "isbot", + "react", + "react-dom" + ] + }, + "@tanstack/react-store@0.9.3_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==", + "dependencies": [ + "@tanstack/store", + "react", + "react-dom", + "use-sync-external-store" + ] + }, + "@tanstack/router-core@1.169.2": { + "integrity": "sha512-5sm0DJF1A7Mz+9gy4Gz/lLovNailK3yot4vYvz9MkBUPw26uLnhQiR8hSCYxucjE0wD6Mdlc5l+Z0/XTlZ7xHw==", + "dependencies": [ + "@tanstack/history", + "cookie-es", + "seroval", + "seroval-plugins" + ] + }, + "@tanstack/router-devtools-core@1.167.3_@tanstack+router-core@1.169.2_csstype@3.2.3": { + "integrity": "sha512-fJ1VMhyQgnoashTrP763c2HRc9kofgF61L7Jb3F6eTHAmCKtGVx8BRtiFt37sr3U0P0jmaaiiSPGP6nT5JtVNg==", + "dependencies": [ + "@tanstack/router-core", + "clsx", + "csstype", + "goober" + ], + "optionalPeers": [ + "csstype" + ] + }, + "@tanstack/router-generator@1.166.42": { + "integrity": "sha512-2qBWC0t78r6b3vI+AbnvCZcFAvbYBDlLuWZrTjQbcjUmwG3qyeQp983tJyDuj9wb5//adG1tgAGXZkJ3aDwdBg==", + "dependencies": [ + "@babel/types", + "@tanstack/router-core", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "jiti", + "magic-string", + "prettier", + "zod@3.25.76" + ] + }, + "@tanstack/router-plugin@1.167.35_@tanstack+react-router@1.169.2__react@19.2.6__react-dom@19.2.6___react@19.2.6_vite@7.3.3_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-UAScU5VAzLYVY4FML/Cbc5S5TucT4I8Ata05yozGOe4ZfepTKRffA5xWLtD2N+ov5svdv0KTX/kqlZnYPe28mA==", + "dependencies": [ + "@babel/core", + "@babel/plugin-syntax-jsx", + "@babel/plugin-syntax-typescript", + "@babel/template", + "@babel/traverse", + "@babel/types", + "@tanstack/react-router", + "@tanstack/router-core", + "@tanstack/router-generator", + "@tanstack/router-utils", + "@tanstack/virtual-file-routes", + "chokidar", + "unplugin", + "vite", + "zod@3.25.76" + ], + "optionalPeers": [ + "@tanstack/react-router", + "vite" + ] + }, + "@tanstack/router-utils@1.161.8": { + "integrity": "sha512-xyiLWEKjfBAVhauDSSjXxyf7s8elU6SM+V050sbkofvGmIIvkwPFtDsX7Gvwh14kBd6iCwAT+RiPvXTxAptY0Q==", + "dependencies": [ + "@babel/core", + "@babel/generator", + "@babel/parser", + "@babel/types", + "ansis", + "babel-dead-code-elimination", + "diff", + "pathe", + "tinyglobby" + ] + }, + "@tanstack/store@0.9.3": { + "integrity": "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==" + }, + "@tanstack/virtual-file-routes@1.161.7": { + "integrity": "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ==", + "bin": true + }, + "@testing-library/dom@10.4.1": { + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dependencies": [ + "@babel/code-frame", + "@babel/runtime", + "@types/aria-query", + "aria-query", + "dom-accessibility-api", + "lz-string", + "picocolors", + "pretty-format" + ] + }, + "@testing-library/react@16.3.2_@testing-library+dom@10.4.1_@types+react@19.2.14_@types+react-dom@19.2.3__@types+react@19.2.14_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dependencies": [ + "@babel/runtime", + "@testing-library/dom", + "@types/react", + "@types/react-dom", + "react", + "react-dom" + ], + "optionalPeers": [ + "@types/react", + "@types/react-dom" + ] + }, + "@tybys/wasm-util@0.10.2": { + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dependencies": [ + "tslib" + ] + }, + "@types/aria-query@5.0.4": { + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==" + }, + "@types/babel__core@7.20.5": { + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dependencies": [ + "@babel/parser", + "@babel/types", + "@types/babel__generator", + "@types/babel__template", + "@types/babel__traverse" + ] + }, + "@types/babel__generator@7.27.0": { + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/babel__template@7.4.4": { + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dependencies": [ + "@babel/parser", + "@babel/types" + ] + }, + "@types/babel__traverse@7.28.0": { + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dependencies": [ + "@babel/types" + ] + }, + "@types/estree@1.0.8": { + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "@types/react-dom@19.2.3_@types+react@19.2.14": { + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dependencies": [ + "@types/react" + ] + }, + "@types/react@19.2.14": { + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dependencies": [ + "csstype" + ] + }, + "@vitejs/plugin-react@5.2.0_vite@7.3.3": { + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dependencies": [ + "@babel/core", + "@babel/plugin-transform-react-jsx-self", + "@babel/plugin-transform-react-jsx-source", + "@rolldown/pluginutils", + "@types/babel__core", + "react-refresh", + "vite" + ] + }, + "abort-controller@3.0.0": { + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": [ + "event-target-shim" + ] + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles@5.2.0": { + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "ansis@4.2.0": { + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==" + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": [ + "normalize-path", + "picomatch@2.3.2" + ] + }, + "aria-query@5.3.0": { + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dependencies": [ + "dequal" + ] + }, + "atomic-sleep@1.0.0": { + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, + "babel-dead-code-elimination@1.0.12": { + "integrity": "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==", + "dependencies": [ + "@babel/core", + "@babel/parser", + "@babel/traverse", + "@babel/types" + ] + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "baseline-browser-mapping@2.10.29": { + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "bin": true + }, + "better-auth@1.6.10_drizzle-kit@0.31.10_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-gzYaywJuhAkv9bTuFj1k6zaSKEAcabxAzYsBj0kXSMaQJVE9uS/qp2592IZmuvtMHO1ohLOP92jDPV6xVsZSoQ==", + "dependencies": [ + "@better-auth/core", + "@better-auth/drizzle-adapter", + "@better-auth/kysely-adapter", + "@better-auth/memory-adapter", + "@better-auth/mongo-adapter", + "@better-auth/prisma-adapter", + "@better-auth/telemetry", + "@better-auth/utils", + "@better-fetch/fetch", + "@noble/ciphers", + "@noble/hashes", + "better-call", + "defu", + "drizzle-kit", + "drizzle-orm", + "jose", + "kysely", + "nanostores", + "react", + "react-dom", + "zod@4.4.3" + ], + "optionalPeers": [ + "drizzle-kit", + "drizzle-orm", + "react", + "react-dom" + ] + }, + "better-call@1.3.5_zod@4.4.3": { + "integrity": "sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==", + "dependencies": [ + "@better-auth/utils", + "@better-fetch/fetch", + "rou3", + "set-cookie-parser", + "zod@4.4.3" + ], + "optionalPeers": [ + "zod@4.4.3" + ] + }, + "bidi-js@1.0.3": { + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": [ + "require-from-string" + ] + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, + "bowser@2.14.1": { + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==" + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": [ + "fill-range" + ] + }, + "browserslist@4.28.2": { + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dependencies": [ + "baseline-browser-mapping", + "caniuse-lite", + "electron-to-chromium", + "node-releases", + "update-browserslist-db" + ], + "bin": true + }, + "buffer-from@1.1.2": { + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "buffer@6.0.3": { + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "bullmq@5.76.6": { + "integrity": "sha512-vlmL3B3NVMRy6se3c7jPHn1Nhqxrg7+wlv1t3XAQFBYZNJDMLP0OO5x2AX5ca7DAuS1SU/C+VfYi+NHVoFK1QQ==", + "dependencies": [ + "cron-parser", + "ioredis", + "msgpackr", + "node-abort-controller", + "semver@7.7.4", + "tslib" + ] + }, + "caniuse-lite@1.0.30001792": { + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==" + }, + "chokidar@3.6.0": { + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": [ + "anymatch", + "braces", + "glob-parent", + "is-binary-path", + "is-glob", + "normalize-path", + "readdirp" + ], + "optionalDependencies": [ + "fsevents" + ] + }, + "class-variance-authority@0.7.1": { + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dependencies": [ + "clsx" + ] + }, + "clsx@2.1.1": { + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, + "cluster-key-slot@1.1.2": { + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, + "colorette@2.0.20": { + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "convert-source-map@2.0.0": { + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "cookie-es@3.1.1": { + "integrity": "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==" + }, + "cron-parser@4.9.0": { + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": [ + "luxon" + ], + "deprecated": true + }, + "css-tree@3.2.1": { + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dependencies": [ + "mdn-data", + "source-map-js" + ] + }, + "csstype@3.2.3": { + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "data-urls@7.0.0": { + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dependencies": [ + "whatwg-mimetype", + "whatwg-url@16.0.1" + ] + }, + "dateformat@4.6.3": { + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==" + }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, + "decimal.js@10.6.0": { + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==" + }, + "defu@6.1.7": { + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==" + }, + "denque@2.1.0": { + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "detect-libc@2.1.2": { + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" + }, + "diff@8.0.4": { + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==" + }, + "dom-accessibility-api@0.5.16": { + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==" + }, + "dotenv-expand@13.0.0": { + "integrity": "sha512-aBfBS8eYIeXmpHI9ThIlA7/WLq+SLt18iXUZhb52rW89QLKQFoIpPG1bPeewoPZsTyjSSO3T7234FBVUM1V2rA==", + "dependencies": [ + "dotenv" + ] + }, + "dotenv@17.4.2": { + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==" + }, + "drizzle-kit@0.31.10": { + "integrity": "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==", + "dependencies": [ + "@drizzle-team/brocli", + "@esbuild-kit/esm-loader", + "esbuild@0.25.12", + "tsx" + ], + "bin": true + }, + "drizzle-orm@0.45.2_kysely@0.28.17_postgres@3.4.9": { + "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==", + "dependencies": [ + "kysely", + "postgres" + ], + "optionalPeers": [ + "kysely", + "postgres" + ] + }, + "drizzle-zod@0.8.3_drizzle-orm@0.45.2__kysely@0.28.17__postgres@3.4.9_zod@4.4.3_kysely@0.28.17_postgres@3.4.9": { + "integrity": "sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==", + "dependencies": [ + "drizzle-orm", + "zod@4.4.3" + ] + }, + "electron-to-chromium@1.5.353": { + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==" + }, + "end-of-stream@1.4.5": { + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": [ + "once" + ] + }, + "enhanced-resolve@5.21.2": { + "integrity": "sha512-xe9vQb5kReirPUxgQrXA3ihgbCqssmTiM7cOZ+Gzu+VeGWgpV98lLZvp0dl4yriyAePcewxGUs9UpKD8PET9KQ==", + "dependencies": [ + "graceful-fs", + "tapable" + ] + }, + "entities@8.0.0": { + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==" + }, + "esbuild@0.18.20": { + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "optionalDependencies": [ + "@esbuild/android-arm@0.18.20", + "@esbuild/android-arm64@0.18.20", + "@esbuild/android-x64@0.18.20", + "@esbuild/darwin-arm64@0.18.20", + "@esbuild/darwin-x64@0.18.20", + "@esbuild/freebsd-arm64@0.18.20", + "@esbuild/freebsd-x64@0.18.20", + "@esbuild/linux-arm@0.18.20", + "@esbuild/linux-arm64@0.18.20", + "@esbuild/linux-ia32@0.18.20", + "@esbuild/linux-loong64@0.18.20", + "@esbuild/linux-mips64el@0.18.20", + "@esbuild/linux-ppc64@0.18.20", + "@esbuild/linux-riscv64@0.18.20", + "@esbuild/linux-s390x@0.18.20", + "@esbuild/linux-x64@0.18.20", + "@esbuild/netbsd-x64@0.18.20", + "@esbuild/openbsd-x64@0.18.20", + "@esbuild/sunos-x64@0.18.20", + "@esbuild/win32-arm64@0.18.20", + "@esbuild/win32-ia32@0.18.20", + "@esbuild/win32-x64@0.18.20" + ], + "scripts": true, + "bin": true + }, + "esbuild@0.25.12": { + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "optionalDependencies": [ + "@esbuild/aix-ppc64@0.25.12", + "@esbuild/android-arm@0.25.12", + "@esbuild/android-arm64@0.25.12", + "@esbuild/android-x64@0.25.12", + "@esbuild/darwin-arm64@0.25.12", + "@esbuild/darwin-x64@0.25.12", + "@esbuild/freebsd-arm64@0.25.12", + "@esbuild/freebsd-x64@0.25.12", + "@esbuild/linux-arm@0.25.12", + "@esbuild/linux-arm64@0.25.12", + "@esbuild/linux-ia32@0.25.12", + "@esbuild/linux-loong64@0.25.12", + "@esbuild/linux-mips64el@0.25.12", + "@esbuild/linux-ppc64@0.25.12", + "@esbuild/linux-riscv64@0.25.12", + "@esbuild/linux-s390x@0.25.12", + "@esbuild/linux-x64@0.25.12", + "@esbuild/netbsd-arm64@0.25.12", + "@esbuild/netbsd-x64@0.25.12", + "@esbuild/openbsd-arm64@0.25.12", + "@esbuild/openbsd-x64@0.25.12", + "@esbuild/openharmony-arm64@0.25.12", + "@esbuild/sunos-x64@0.25.12", + "@esbuild/win32-arm64@0.25.12", + "@esbuild/win32-ia32@0.25.12", + "@esbuild/win32-x64@0.25.12" + ], + "scripts": true, + "bin": true + }, + "esbuild@0.27.7": { + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "optionalDependencies": [ + "@esbuild/aix-ppc64@0.27.7", + "@esbuild/android-arm@0.27.7", + "@esbuild/android-arm64@0.27.7", + "@esbuild/android-x64@0.27.7", + "@esbuild/darwin-arm64@0.27.7", + "@esbuild/darwin-x64@0.27.7", + "@esbuild/freebsd-arm64@0.27.7", + "@esbuild/freebsd-x64@0.27.7", + "@esbuild/linux-arm@0.27.7", + "@esbuild/linux-arm64@0.27.7", + "@esbuild/linux-ia32@0.27.7", + "@esbuild/linux-loong64@0.27.7", + "@esbuild/linux-mips64el@0.27.7", + "@esbuild/linux-ppc64@0.27.7", + "@esbuild/linux-riscv64@0.27.7", + "@esbuild/linux-s390x@0.27.7", + "@esbuild/linux-x64@0.27.7", + "@esbuild/netbsd-arm64@0.27.7", + "@esbuild/netbsd-x64@0.27.7", + "@esbuild/openbsd-arm64@0.27.7", + "@esbuild/openbsd-x64@0.27.7", + "@esbuild/openharmony-arm64@0.27.7", + "@esbuild/sunos-x64@0.27.7", + "@esbuild/win32-arm64@0.27.7", + "@esbuild/win32-ia32@0.27.7", + "@esbuild/win32-x64@0.27.7" + ], + "scripts": true, + "bin": true + }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" + }, + "event-target-shim@5.0.1": { + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events@3.3.0": { + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-copy@4.0.3": { + "integrity": "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==" + }, + "fast-safe-stringify@2.1.1": { + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fast-sha256@1.3.0": { + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, + "fast-xml-builder@1.2.0": { + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "dependencies": [ + "path-expression-matcher", + "xml-naming" + ] + }, + "fast-xml-parser@5.7.2": { + "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==", + "dependencies": [ + "@nodable/entities", + "fast-xml-builder", + "path-expression-matcher", + "strnum" + ], + "bin": true + }, + "fdir@6.5.0_picomatch@4.0.4": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch@4.0.4" + ], + "optionalPeers": [ + "picomatch@4.0.4" + ] + }, + "fetch-retry@6.0.0": { + "integrity": "sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==" + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": [ + "to-regex-range" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "gensync@1.0.0-beta.2": { + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" + }, + "get-tsconfig@4.14.0": { + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": [ + "is-glob" + ] + }, + "goober@2.1.18_csstype@3.2.3": { + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", + "dependencies": [ + "csstype" + ] + }, + "graceful-fs@4.2.11": { + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "help-me@5.0.0": { + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==" + }, + "hono-pino@0.10.3_hono@4.12.18_pino@10.3.1": { + "integrity": "sha512-n0RNPIFOoq25Fg8b4D5gus4sVqI0z+8I17ibl96+p43d07UnZ0EMM/It0qSgfc7UtaC+XP5FkFmRHwBp6owsNA==", + "dependencies": [ + "defu", + "hono", + "pino" + ] + }, + "hono@4.12.18": { + "integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==" + }, + "html-encoding-sniffer@6.0.0": { + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dependencies": [ + "@exodus/bytes" + ] + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ioredis@5.10.1": { + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "dependencies": [ + "@ioredis/commands", + "cluster-key-slot", + "debug", + "denque", + "lodash.defaults", + "lodash.isarguments", + "redis-errors", + "redis-parser", + "standard-as-callback" + ] + }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": [ + "binary-extensions" + ] + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": [ + "is-extglob" + ] + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-potential-custom-element-name@1.0.1": { + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "isbot@5.1.40": { + "integrity": "sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ==" + }, + "jiti@2.7.0": { + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "bin": true + }, + "jose@6.2.3": { + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==" + }, + "joycon@3.1.1": { + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==" + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jsdom@30.0.1": { + "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==", + "dependencies": [ + "@asamuzakjp/css-color", + "@asamuzakjp/dom-selector", + "@bramus/specificity", + "@csstools/css-syntax-patches-for-csstree", + "@exodus/bytes", + "css-tree", + "data-urls", + "decimal.js", + "html-encoding-sniffer", + "is-potential-custom-element-name", + "lru-cache@11.5.2", + "parse5", + "saxes", + "symbol-tree", + "tough-cookie", + "undici", + "w3c-xmlserializer", + "webidl-conversions", + "whatwg-mimetype", + "whatwg-url@17.1.0", + "xml-name-validator" + ] + }, + "jsesc@3.1.0": { + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "bin": true + }, + "json5@2.2.3": { + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": true + }, + "kysely@0.28.17": { + "integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==" + }, + "lightningcss-android-arm64@1.32.0": { + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-arm64@1.32.0": { + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-x64@1.32.0": { + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "lightningcss-freebsd-x64@1.32.0": { + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "lightningcss-linux-arm-gnueabihf@1.32.0": { + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "lightningcss-linux-arm64-gnu@1.32.0": { + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-arm64-musl@1.32.0": { + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-x64-gnu@1.32.0": { + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-linux-x64-musl@1.32.0": { + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-win32-arm64-msvc@1.32.0": { + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "lightningcss-win32-x64-msvc@1.32.0": { + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "os": ["win32"], + "cpu": ["x64"] + }, + "lightningcss@1.32.0": { + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dependencies": [ + "detect-libc" + ], + "optionalDependencies": [ + "lightningcss-android-arm64", + "lightningcss-darwin-arm64", + "lightningcss-darwin-x64", + "lightningcss-freebsd-x64", + "lightningcss-linux-arm-gnueabihf", + "lightningcss-linux-arm64-gnu", + "lightningcss-linux-arm64-musl", + "lightningcss-linux-x64-gnu", + "lightningcss-linux-x64-musl", + "lightningcss-win32-arm64-msvc", + "lightningcss-win32-x64-msvc" + ] + }, + "lodash.defaults@4.2.0": { + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.isarguments@3.1.0": { + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, + "lru-cache@11.5.2": { + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==" + }, + "lru-cache@5.1.1": { + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": [ + "yallist" + ] + }, + "lucide-react@0.553.0_react@19.2.6": { + "integrity": "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==", + "dependencies": [ + "react" + ] + }, + "luxon@3.7.2": { + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==" + }, + "lz-string@1.5.0": { + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "bin": true + }, + "magic-string@0.30.21": { + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dependencies": [ + "@jridgewell/sourcemap-codec" + ] + }, + "mdn-data@2.27.1": { + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==" + }, + "minimist@1.2.8": { + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "msgpackr-extract@3.0.3": { + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dependencies": [ + "node-gyp-build-optional-packages" + ], + "optionalDependencies": [ + "@msgpackr-extract/msgpackr-extract-darwin-arm64", + "@msgpackr-extract/msgpackr-extract-darwin-x64", + "@msgpackr-extract/msgpackr-extract-linux-arm", + "@msgpackr-extract/msgpackr-extract-linux-arm64", + "@msgpackr-extract/msgpackr-extract-linux-x64", + "@msgpackr-extract/msgpackr-extract-win32-x64" + ], + "scripts": true, + "bin": true + }, + "msgpackr@2.0.1": { + "integrity": "sha512-9J+tqTEsbHqY8YohazYgty7LgerFIWxvMLpUjqETSmjHojtJm2WnX2kK/2a1fLI7CO7ERP1YSEUXMucz4j+yBA==", + "optionalDependencies": [ + "msgpackr-extract" + ] + }, + "nanoid@3.3.12": { + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "bin": true + }, + "nanoid@5.1.11": { + "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", + "bin": true + }, + "nanostores@1.3.0": { + "integrity": "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==" + }, + "node-abort-controller@3.1.1": { + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node-gyp-build-optional-packages@5.2.2": { + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dependencies": [ + "detect-libc" + ], + "bin": true + }, + "node-releases@2.0.38": { + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==" + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "on-exit-leak-free@2.1.2": { + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==" + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "openapi3-ts@4.5.0": { + "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==", + "dependencies": [ + "yaml" + ] + }, + "parse5@8.0.1": { + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dependencies": [ + "entities" + ] + }, + "path-expression-matcher@1.5.0": { + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==" + }, + "pathe@2.0.3": { + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@2.3.2": { + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" + }, + "picomatch@4.0.4": { + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" + }, + "pino-abstract-transport@1.2.0": { + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": [ + "readable-stream", + "split2" + ] + }, + "pino-abstract-transport@3.0.0": { + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "dependencies": [ + "split2" + ] + }, + "pino-pretty@13.1.3": { + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "dependencies": [ + "colorette", + "dateformat", + "fast-copy", + "fast-safe-stringify", + "help-me", + "joycon", + "minimist", + "on-exit-leak-free", + "pino-abstract-transport@3.0.0", + "pump", + "secure-json-parse", + "sonic-boom", + "strip-json-comments" + ], + "bin": true + }, + "pino-std-serializers@7.1.0": { + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==" + }, + "pino@10.3.1": { + "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", + "dependencies": [ + "@pinojs/redact", + "atomic-sleep", + "on-exit-leak-free", + "pino-abstract-transport@3.0.0", + "pino-std-serializers", + "process-warning", + "quick-format-unescaped", + "real-require", + "safe-stable-stringify", + "sonic-boom", + "thread-stream" + ], + "bin": true + }, + "postal-mime@2.7.4": { + "integrity": "sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==" + }, + "postcss@8.5.14": { + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dependencies": [ + "nanoid@3.3.12", + "picocolors", + "source-map-js" + ] + }, + "postgres@3.4.9": { + "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==" + }, + "prettier@3.8.3": { + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "bin": true + }, + "pretty-format@27.5.1": { + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": [ + "ansi-regex", + "ansi-styles", + "react-is" + ] + }, + "process-warning@5.0.0": { + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==" + }, + "process@0.11.10": { + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "pump@3.0.4": { + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dependencies": [ + "end-of-stream", + "once" + ] + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "quick-format-unescaped@4.0.4": { + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "react-dom@19.2.6_react@19.2.6": { + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "dependencies": [ + "react", + "scheduler" + ] + }, + "react-is@17.0.2": { + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "react-refresh@0.18.0": { + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==" + }, + "react@19.2.6": { + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==" + }, + "readable-stream@4.7.0": { + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dependencies": [ + "abort-controller", + "buffer", + "events", + "process", + "string_decoder" + ] + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": [ + "picomatch@2.3.2" + ] + }, + "real-require@0.2.0": { + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "redis-errors@1.2.0": { + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + }, + "redis-parser@3.0.0": { + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": [ + "redis-errors" + ] + }, + "require-from-string@2.0.2": { + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "reselect@5.1.1": { + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, + "resend@6.12.3": { + "integrity": "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw==", + "dependencies": [ + "postal-mime", + "svix" + ] + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, + "rollup@4.60.3": { + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dependencies": [ + "@types/estree" + ], + "optionalDependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-loong64-gnu", + "@rollup/rollup-linux-loong64-musl", + "@rollup/rollup-linux-ppc64-gnu", + "@rollup/rollup-linux-ppc64-musl", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-riscv64-musl", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-openbsd-x64", + "@rollup/rollup-openharmony-arm64", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-gnu", + "@rollup/rollup-win32-x64-msvc", + "fsevents" + ], + "bin": true + }, + "rou3@0.7.12": { + "integrity": "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==" + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-stable-stringify@2.5.0": { + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "saxes@6.0.0": { + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": [ + "xmlchars" + ] + }, + "scheduler@0.27.0": { + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" + }, + "secure-json-parse@4.1.0": { + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==" + }, + "semver@6.3.1": { + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": true + }, + "semver@7.7.4": { + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "bin": true + }, + "seroval-plugins@1.5.4_seroval@1.5.4": { + "integrity": "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==", + "dependencies": [ + "seroval" + ] + }, + "seroval@1.5.4": { + "integrity": "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==" + }, + "set-cookie-parser@3.1.0": { + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==" + }, + "sonic-boom@4.2.1": { + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "dependencies": [ + "atomic-sleep" + ] + }, + "sonner@2.0.7_react@19.2.6_react-dom@19.2.6__react@19.2.6": { + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "dependencies": [ + "react", + "react-dom" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "source-map-support@0.5.21": { + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": [ + "buffer-from", + "source-map" + ] + }, + "source-map@0.6.1": { + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "split2@4.2.0": { + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, + "standard-as-callback@2.1.0": { + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, + "standardwebhooks@1.0.0": { + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "dependencies": [ + "@stablelib/base64", + "fast-sha256" + ] + }, + "stoker@2.0.1_@hono+zod-openapi@1.4.0__hono@4.12.18__zod@4.4.3_hono@4.12.18_zod@4.4.3": { + "integrity": "sha512-liSQNnJmn8fWSEan7sVaFe6iSHuN3X02fDGLS6snwW+FUuKi5HmKUHm3P+Kzr5xiDPqRpmSTtmGEBbSL9H2zkQ==", + "dependencies": [ + "@hono/zod-openapi", + "hono" + ], + "optionalPeers": [ + "@hono/zod-openapi" + ] + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer" + ] + }, + "strip-json-comments@5.0.3": { + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==" + }, + "strnum@2.3.0": { + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==" + }, + "svix@1.92.2": { + "integrity": "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ==", + "dependencies": [ + "standardwebhooks" + ] + }, + "symbol-tree@3.2.4": { + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tagged-tag@1.0.0": { + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==" + }, + "tailwind-merge@3.5.0": { + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==" + }, + "tailwindcss@4.3.0": { + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==" + }, + "tapable@2.3.3": { + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==" + }, + "thread-stream@4.0.0": { + "integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==", + "dependencies": [ + "real-require" + ] + }, + "tinyglobby@0.2.16": { + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dependencies": [ + "fdir", + "picomatch@4.0.4" + ] + }, + "tldts-core@7.4.10": { + "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==" + }, + "tldts@7.4.10": { + "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==", + "dependencies": [ + "tldts-core" + ], + "bin": true + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": [ + "is-number" + ] + }, + "tough-cookie@6.0.2": { + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dependencies": [ + "tldts" + ] + }, + "tr46@6.0.0": { + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dependencies": [ + "punycode" + ] + }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "tsx@4.21.0": { + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dependencies": [ + "esbuild@0.27.7", + "get-tsconfig" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "type-fest@5.6.0": { + "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", + "dependencies": [ + "tagged-tag" + ] + }, + "typescript@5.9.3": { + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "bin": true + }, + "undici@8.9.0": { + "integrity": "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==" + }, + "unplugin@3.0.0": { + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", + "dependencies": [ + "@jridgewell/remapping", + "picomatch@4.0.4", + "webpack-virtual-modules" + ] + }, + "update-browserslist-db@1.2.3_browserslist@4.28.2": { + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dependencies": [ + "browserslist", + "escalade", + "picocolors" + ], + "bin": true + }, + "use-sync-external-store@1.6.0_react@19.2.6": { + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dependencies": [ + "react" + ] + }, + "vite@7.3.3": { + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dependencies": [ + "esbuild@0.27.7", + "fdir", + "picomatch@4.0.4", + "postcss", + "rollup", + "tinyglobby" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "w3c-xmlserializer@5.0.0": { + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": [ + "xml-name-validator" + ] + }, + "webidl-conversions@8.0.1": { + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==" + }, + "webpack-virtual-modules@0.6.2": { + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==" + }, + "whatwg-mimetype@5.0.0": { + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==" + }, + "whatwg-url@16.0.1": { + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dependencies": [ + "@exodus/bytes", + "tr46", + "webidl-conversions" + ] + }, + "whatwg-url@17.1.0": { + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "dependencies": [ + "@exodus/bytes", + "tr46", + "webidl-conversions" + ] + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "xml-name-validator@5.0.0": { + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==" + }, + "xml-naming@0.1.0": { + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==" + }, + "xmlchars@2.2.0": { + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "yallist@3.1.1": { + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml@2.8.4": { + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", + "bin": true + }, + "zod@3.25.76": { + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==" + }, + "zod@4.4.3": { + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==" + }, + "zustand@5.0.13_@types+react@19.2.14_react@19.2.6": { + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", + "dependencies": [ + "@types/react", + "react" + ], + "optionalPeers": [ + "@types/react", + "react" + ] + } + }, + "workspace": { + "dependencies": [ + "jsr:@std/expect@^1.0.19", + "jsr:@std/testing@^1.0.19" + ], + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@2.3.7" + ] + }, + "members": { + "apps/backend": { + "dependencies": [ + "npm:@aws-sdk/client-s3@*", + "npm:@aws-sdk/s3-request-presigner@*", + "npm:@axiomhq/pino@*", + "npm:@hono/swagger-ui@*", + "npm:@hono/zod-openapi@*", + "npm:@hono/zod-validator@*", + "npm:@node-rs/argon2@*", + "npm:@scalar/hono-api-reference@*", + "npm:better-auth@*", + "npm:bullmq@*", + "npm:dotenv-expand@*", + "npm:dotenv@*", + "npm:drizzle-kit@*", + "npm:drizzle-orm@*", + "npm:drizzle-zod@*", + "npm:hono-pino@*", + "npm:hono@*", + "npm:ioredis@*", + "npm:pino-pretty@*", + "npm:pino@*", + "npm:postgres@*", + "npm:resend@*", + "npm:stoker@*", + "npm:zod@*" + ] + }, + "apps/frontend": { + "packageJson": { + "dependencies": [ + "npm:@base-ui/react@^1.2.0", + "npm:@better-fetch/fetch@^1.1.18", + "npm:@tailwindcss/vite@^4.1.17", + "npm:@tanstack/react-form@1", + "npm:@tanstack/react-query@^5.90.7", + "npm:@tanstack/react-router-devtools@^1.134.13", + "npm:@tanstack/react-router@^1.134.13", + "npm:@tanstack/router-plugin@^1.134.14", + "npm:@testing-library/react@^16.3.2", + "npm:@types/react-dom@^19.1.9", + "npm:@types/react@^19.1.16", + "npm:@vitejs/plugin-react@^5.0.4", + "npm:better-auth@^1.3.34", + "npm:class-variance-authority@~0.7.1", + "npm:clsx@^2.1.1", + "npm:jsdom@^30.0.1", + "npm:lucide-react@0.553", + "npm:react-dom@^19.1.1", + "npm:react@^19.1.1", + "npm:sonner@^2.0.7", + "npm:tailwind-merge@^3.3.1", + "npm:tailwindcss@^4.1.17", + "npm:typescript@~5.9.3", + "npm:vite@^7.1.7", + "npm:zod@^4.2.1", + "npm:zustand@^5.0.8" + ] + } + }, + "packages/db": { + "dependencies": [ + "npm:dotenv@*", + "npm:drizzle-kit@*", + "npm:drizzle-orm@*", + "npm:drizzle-zod@*", + "npm:postgres@*" + ] + }, + "packages/shared": { + "dependencies": [ + "npm:zod@*" + ] + } + } + } +} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 13173d5..d62d737 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,10 +1,18 @@ # Production docker-compose — used by the CD pipeline on the VPS. # Dev services (MinIO, exposed DB ports) are intentionally excluded. # -# The backend image is built by CI and pushed to GHCR. +# Images are built by CI and pushed to GHCR. # Required: .env.production on the VPS (never committed to git). services: + frontend: + image: ghcr.io/orctatech-engineering-team/orcta-frontend:${IMAGE_TAG:-latest} + restart: unless-stopped + ports: + - "80:80" + depends_on: + - backend + db: image: postgres:16-alpine restart: unless-stopped @@ -17,7 +25,7 @@ services: volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: [ "CMD", "pg_isready", "-U", "${POSTGRES_USER:-orcta}" ] + test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER:-orcta}"] interval: 10s timeout: 5s retries: 5 @@ -28,7 +36,7 @@ services: volumes: - redis_data:/data healthcheck: - test: [ "CMD", "redis-cli", "ping" ] + test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 @@ -37,14 +45,40 @@ services: image: ghcr.io/orctatech-engineering-team/orcta-backend:${IMAGE_TAG:-latest} restart: unless-stopped env_file: .env.production - ports: - - "9292:9292" + expose: + - "9999" depends_on: db: condition: service_healthy redis: condition: service_healthy + # Runs migrations from the repo checkout on the VPS. The backend image + # (orcta-backend) is a compiled standalone binary on bare alpine — no deno + # executable, no source tree — so it can't run migrate.ts itself. This + # service uses the same Deno base image the backend Dockerfile builds + # with, mounting the deploy directory instead of baking source into an + # image. Opt-in only (profiles) — never starts via `docker compose up`. + migrate: + image: denoland/deno:debian-2.7.14 + working_dir: /app + volumes: + - .:/app + env_file: .env.production + depends_on: + db: + condition: service_healthy + command: [ + "deno", + "run", + "--allow-env", + "--allow-net", + "--allow-read", + "--allow-sys", + "apps/backend/src/db/migrate.ts", + ] + profiles: ["tools"] + volumes: postgres_data: redis_data: diff --git a/docker-compose.yml b/docker-compose.yml index b8ad6a5..9c87bb0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,44 @@ services: + frontend: + build: + context: . + dockerfile: apps/frontend/Dockerfile + ports: + - "80:80" + depends_on: + - backend + + backend: + build: + context: . + dockerfile: apps/backend/Dockerfile + expose: + - "9999" + environment: + PORT: 9999 + DATABASE_URL: postgres://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-orcta_dev} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET} + BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost} + REDIS_URL: redis://redis:6379 + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + db: image: postgres:16-alpine + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-orcta_dev} ports: - "5432:5432" - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: orcta_dev volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres"] - interval: 5s + test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER:-postgres}"] + interval: 10s timeout: 5s retries: 5 @@ -23,24 +50,10 @@ services: - redis_data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] - interval: 5s + interval: 10s timeout: 5s retries: 5 - # Optional: MinIO for local S3-compatible storage - # minio: - # image: minio/minio - # ports: - # - "9000:9000" - # - "9001:9001" - # environment: - # MINIO_ROOT_USER: minioadmin - # MINIO_ROOT_PASSWORD: minioadmin - # command: server /data --console-address ":9001" - # volumes: - # - minio_data:/data - volumes: postgres_data: redis_data: - # minio_data: diff --git a/docs/BATTERIES.md b/docs/BATTERIES.md index 6e39798..5966543 100644 --- a/docs/BATTERIES.md +++ b/docs/BATTERIES.md @@ -1,7 +1,9 @@ + # Batteries -Everything opt-in beyond the basics — each section only activates once you add the relevant env vars. +Everything opt-in beyond the basics — each section only activates once you add +the relevant env vars. **Contents** @@ -19,35 +21,38 @@ Everything opt-in beyond the basics — each section only activates once you add ## Auth -Authentication is always on — no env var required. Powered by [better-auth](https://better-auth.com) with a Drizzle adapter. +Authentication is always on — no env var required. Powered by +[better-auth](https://better-auth.com) with a Drizzle adapter. ### What's included - Email + password sign-up / sign-in - Session management (7-day expiry, rolling 1-day refresh) -- A `role` field on every user (default `"user"`) +- A `role` field on every user (default `"buyer"`) - Auth routes mounted at `/api/auth/**` - OpenAPI docs auto-generated for all auth endpoints ### Session in a handler -The `authMiddleware` (applied to all routes in the `routes` array) populates `c.get("session")`: +The `authMiddleware` (applied to all routes in the `routes` array) populates +`c.get("session")`: ```typescript import type { AppRouteHandler } from "@/lib/types"; export const myHandler: AppRouteHandler = async (c) => { const session = c.get("session"); - const userId = session.userId; - const role = session.user.role; // "user" | your custom roles + const userId = session.userId; + const role = session.user.role; // "buyer" | "seller" | "admin" // ... }; ``` ### Extending the user model -Add fields in `apps/backend/src/lib/auth.ts` under `user.additionalFields`, then add the -matching column to `packages/db/src/schema/users.ts` and run a migration. +Add fields in `apps/backend/src/lib/auth.ts` under `user.additionalFields`, then +add the matching column to `packages/db/src/schema/users.ts` and run a +migration. ### Frontend client @@ -67,11 +72,12 @@ await signInWithProvider("github", "/dashboard"); ### Social OAuth (Google + GitHub) -**Requires env vars:** at least one of `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` or `GITHUB_CLIENT_ID`/`GITHUB_CLIENT_SECRET` +**Requires env vars:** at least one of `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` +or `GITHUB_CLIENT_ID`/`GITHUB_CLIENT_SECRET` -Providers are activated only when both their vars are present — leaving vars blank will -not break the app, the button just will not appear. No DB migration needed (the existing -`accounts` table already handles OAuth tokens). +Providers are activated only when both their vars are present — leaving vars +blank will not break the app, the button just will not appear. No DB migration +needed (the existing `accounts` table already handles OAuth tokens). Add to `.env`: @@ -98,9 +104,11 @@ http://localhost:9999/api/auth/callback/github ## File Uploads -Upload files directly to S3 or Cloudflare R2 using presigned URLs. Files never touch the server. +Upload files directly to S3 or Cloudflare R2 using presigned URLs. Files never +touch the server. -**Requires env vars:** `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION`, `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` +**Requires env vars:** `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION`, +`S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` ### Setup @@ -119,7 +127,12 @@ Works with AWS S3, Cloudflare R2, MinIO, or any S3-compatible store. ### API ```typescript -import { generateKey, getUploadUrl, getDownloadUrl, deleteFile } from "@/lib/storage"; +import { + deleteFile, + generateKey, + getDownloadUrl, + getUploadUrl, +} from "@/lib/storage"; // 1. Generate a collision-free key const key = generateKey("photo.jpg", "avatars"); @@ -188,43 +201,46 @@ Real-time communication with room support. import { upgradeWebSocket } from "hono/ws"; import { wsManager } from "@/lib/ws"; -app.get("/ws", upgradeWebSocket((c) => { - // capture the id in the closure so all callbacks share it - let connectionId: string; - - return { - onOpen(_event, ws) { - connectionId = crypto.randomUUID(); - wsManager.add(connectionId, ws, c.get("session")?.userId); - wsManager.join(connectionId, "global"); - }, - - onMessage(event, _ws) { - const data = JSON.parse(String(event.data)); - // handle incoming messages - }, - - onClose() { - wsManager.remove(connectionId); - }, - }; -})); +app.get( + "/ws", + upgradeWebSocket((c) => { + // capture the id in the closure so all callbacks share it + let connectionId: string; + + return { + onOpen(_event, ws) { + connectionId = crypto.randomUUID(); + wsManager.add(connectionId, ws, c.get("session")?.userId); + wsManager.join(connectionId, "global"); + }, + + onMessage(event, _ws) { + const data = JSON.parse(String(event.data)); + // handle incoming messages + }, + + onClose() { + wsManager.remove(connectionId); + }, + }; + }), +); ``` ### WebSocketManager API -| Method | Description | -|--------|-------------| -| `add(id, ws, userId?)` | Register a new connection | -| `remove(id)` | Remove a connection | -| `get(id)` | Get a connection by id | -| `join(id, room)` | Add a connection to a room | -| `leave(id, room)` | Remove a connection from a room | -| `send(id, data)` | Send to one connection | -| `broadcast(room, data, excludeId?)` | Send to all connections in a room | -| `sendToUser(userId, data)` | Send to all connections belonging to a user | -| `broadcastAll(data, excludeId?)` | Send to every connected client | -| `size` | Number of active connections | +| Method | Description | +| ----------------------------------- | ------------------------------------------- | +| `add(id, ws, userId?)` | Register a new connection | +| `remove(id)` | Remove a connection | +| `get(id)` | Get a connection by id | +| `join(id, room)` | Add a connection to a room | +| `leave(id, room)` | Remove a connection from a room | +| `send(id, data)` | Send to one connection | +| `broadcast(room, data, excludeId?)` | Send to all connections in a room | +| `sendToUser(userId, data)` | Send to all connections belonging to a user | +| `broadcastAll(data, excludeId?)` | Send to every connected client | +| `size` | Number of active connections | ### Client usage @@ -239,15 +255,23 @@ ws.onmessage = (event) => { ws.send(JSON.stringify({ type: "join", room: "chat-123" })); ``` -> **Production note** — `wsManager` is in-process. If you run multiple server instances, connections are not shared across them. Add a Redis pub/sub layer if you need cross-instance broadcasting. +> **Production note** — `wsManager` is in-process. If you run multiple server +> instances, connections are not shared across them. Add a Redis pub/sub layer +> if you need cross-instance broadcasting. --- ## Background Jobs -Process work asynchronously with [BullMQ](https://docs.bullmq.io). +Process work asynchronously with [BullMQ](https://docs.bullmq.io). The one +built-in job type is `email` — it's what powers the [Email](#email) battery's +`queueEmail` helper, used by better-auth's sign-up and password-reset flows +(`apps/backend/src/lib/auth.ts`) so those requests don't block on an outbound +Resend API call. -**Requires env var:** `REDIS_URL` +**Requires env var:** `REDIS_URL` — without it, `queueEmail` degrades to +sending inline instead of queuing (see [Email](#email)); code that calls +`addJob` directly requires Redis. ### Setup @@ -260,76 +284,86 @@ REDIS_URL=redis://localhost:6379 Defined in `apps/backend/src/jobs/index.ts`: ```typescript -export type JobName = "email" | "cleanup" | "sync"; +export type EmailTemplateName = "welcome" | "passwordReset"; +export type JobName = "email"; export interface JobData { - email: { to: string; template: string; data: Record }; - cleanup: { olderThanDays: number }; - sync: { userId: string }; + email: { + to: string; + template: EmailTemplateName; + props: { name: string; actionUrl?: string }; + }; } ``` ### Queue a job +Prefer `queueEmail` (from `@/lib/email`) over `addJob` directly for emails — +it queues through Redis when available and falls back to sending inline when +it isn't, so the call site doesn't need to care: + ```typescript -import { addJob } from "@/jobs"; +import { queueEmail } from "@/lib/email"; -// Fire and forget -await addJob("email", { +await queueEmail({ to: "user@example.com", template: "welcome", - data: { name: "Alex" }, + props: { name: "Alex" }, }); +``` -// With options -await addJob("cleanup", { olderThanDays: 30 }, { - delay: 60_000, // wait 1 min before processing - priority: 10, // higher = processed first -}); +`addJob` is the lower-level primitive `queueEmail` and any future job types +build on — it always requires Redis: + +```typescript +import { addJob } from "@/jobs"; + +await addJob( + "email", + { to: "user@example.com", template: "welcome", props: { name: "Alex" } }, + { delay: 60_000, priority: 10 }, // optional: wait 1 min, higher priority = processed first +); ``` -Jobs are automatically kept for the last 100 successes and 1 000 failures in Redis. +Jobs are automatically kept for the last 100 successes and 1 000 failures in +Redis. ### Process jobs -Add your logic in `apps/backend/src/jobs/worker.ts` inside the `processors` object: +`apps/backend/src/jobs/worker.ts`'s `processors.email` looks up the template +by name (via the `emailTemplates` map exported from `jobs/index.ts`) and sends +it: ```typescript -const processors = { +const processors: { [K in JobName]: (job: Job) => Promise } = { async email(job) { - const { to, template, data } = job.data; - await sendEmail(to, template, data); // wire up your email sender - }, - - async sync(job) { - const { userId } = job.data; - // fetch external data, update DB, etc. - }, - - async cleanup(job) { - const { olderThanDays } = job.data; - // delete old records + const { to, template, props } = job.data; + const { subject, html, text } = emailTemplates[template](props); + await sendEmail({ to, subject, html, text }); }, }; ``` -Each worker runs with **concurrency 5** and handles graceful shutdown on `SIGTERM`/`SIGINT`. +Each worker runs with **concurrency 5** and handles graceful shutdown on +`SIGTERM`/`SIGINT`. ### Run workers ```bash -pnpm --filter backend jobs +deno run --env-file=apps/backend/.env -A apps/backend/src/jobs/worker.ts ``` -In production, run this as a separate process or container alongside the HTTP server. +In production, run this as a separate process or container alongside the HTTP +server. ### Adding a new job type -1. Add the name to the `JobName` union and its payload to `JobData` in `jobs/index.ts` -2. Create a queue getter following the existing pattern (`getSyncQueue` etc.) +1. Add the name to the `JobName` union and its payload to `JobData` in + `jobs/index.ts` +2. Add a queue getter following the existing `getEmailQueue` pattern 3. Add the queue to the `queueMap` inside `addJob` 4. Add a processor in `worker.ts` -5. Add the job name to the workers array: `(["email", "cleanup", "sync", "yourJob"] as JobName[])` +5. Add the job name to the workers array: `(["email", "yourJob"] as JobName[])` --- @@ -346,10 +380,13 @@ import { rateLimit } from "@/lib/rate-limit"; app.use("/api/*", rateLimit()); // Custom window -app.use("/api/search", rateLimit({ - windowMs: 60_000, // 1 minute - max: 20, -})); +app.use( + "/api/search", + rateLimit({ + windowMs: 60_000, // 1 minute + max: 20, + }), +); ``` ### Presets @@ -357,39 +394,51 @@ app.use("/api/search", rateLimit({ ```typescript import { authRateLimit, strictRateLimit } from "@/lib/rate-limit"; -app.post("/api/auth/*", authRateLimit); // 5 req / 5 min — for login / sign-up +app.post("/api/auth/*", authRateLimit); // 5 req / 5 min — for login / sign-up app.post("/api/export", strictRateLimit); // 10 req / min — for expensive operations ``` ### Rate limit by user ```typescript -app.use("/api/*", rateLimit({ - keyGenerator: (c) => c.get("session")?.userId - ?? c.req.header("x-forwarded-for") - ?? "anon", -})); +app.use( + "/api/*", + rateLimit({ + keyGenerator: (c) => + c.get("session")?.userId ?? + c.req.header("x-forwarded-for") ?? + "anon", + }), +); ``` ### Response headers Every rate-limited response includes: -| Header | Meaning | -|--------|---------| -| `X-RateLimit-Limit` | Max requests allowed in the window | -| `X-RateLimit-Remaining` | Requests left this window | -| `Retry-After` | Seconds until reset (only on 429) | +| Header | Meaning | +| ----------------------- | ---------------------------------- | +| `X-RateLimit-Limit` | Max requests allowed in the window | +| `X-RateLimit-Remaining` | Requests left this window | +| `Retry-After` | Seconds until reset (only on 429) | -> **Production note** — the store is in-memory and resets on restart. It is not shared across multiple server instances. For multi-instance deployments, replace the store with a Redis-backed implementation (e.g. `rate-limit-redis`). +> **Production note** — the store is in-memory and resets on restart. It is not +> shared across multiple server instances. For multi-instance deployments, +> replace the store with a Redis-backed implementation (e.g. +> `rate-limit-redis`). --- ## Email -Send transactional emails with [Resend](https://resend.com). +Send transactional emails with [Resend](https://resend.com). Templates live +in `@repo/email-templates`; `apps/backend/src/lib/email.ts` wraps Resend +(`sendEmail`) and adds the [Background Jobs](#background-jobs)-aware +`queueEmail` — the one both better-auth hooks (`sendResetPassword`, +`sendVerificationEmail` in `apps/backend/src/lib/auth.ts`) actually call. -**Requires env var:** `RESEND_API_KEY` +**Requires env var:** `RESEND_API_KEY` — without it, `sendEmail` logs instead +of sending, so email works end-to-end in dev with no setup. ### Setup @@ -400,39 +449,35 @@ RESEND_API_KEY=re_xxxxx ### Usage ```typescript -import { Resend } from "resend"; -import { welcomeEmail, passwordResetEmail } from "@repo/email-templates"; +import { queueEmail } from "@/lib/email"; -const resend = new Resend(process.env.RESEND_API_KEY); - -// Welcome email -const welcome = welcomeEmail({ name: "Alex", actionUrl: "https://app.example.com/verify?token=xxx" }); - -await resend.emails.send({ - from: "hello@yourdomain.com", +// Queues through Redis if REDIS_URL is set, otherwise sends inline +await queueEmail({ to: "alex@example.com", - subject: welcome.subject, - html: welcome.html, - text: welcome.text, + template: "welcome", // or "passwordReset" + props: { name: "Alex", actionUrl: "https://app.example.com/verify?token=xxx" }, }); +``` -// Password reset -const reset = passwordResetEmail({ name: "Alex", actionUrl: "https://app.example.com/reset?token=xxx" }); +Need to send immediately, bypassing the queue entirely? Use `sendEmail` with a +template directly: -await resend.emails.send({ - from: "hello@yourdomain.com", - to: "alex@example.com", - subject: reset.subject, - html: reset.html, - text: reset.text, +```typescript +import { sendEmail } from "@/lib/email"; +import { welcomeEmail } from "@repo/email-templates"; + +const { subject, html, text } = welcomeEmail({ + name: "Alex", + actionUrl: "https://app.example.com/verify?token=xxx", }); +await sendEmail({ to: "alex@example.com", subject, html, text }); ``` ### Available templates -| Function | Subject | -|----------|---------| -| `welcomeEmail({ name, actionUrl? })` | `Welcome, {name}!` | +| Function | Subject | +| ----------------------------------------- | --------------------- | +| `welcomeEmail({ name, actionUrl? })` | `Welcome, {name}!` | | `passwordResetEmail({ name, actionUrl })` | `Reset your password` | ### Adding templates @@ -440,29 +485,36 @@ await resend.emails.send({ Edit `packages/email-templates/src/index.ts`: ```typescript -export function invoiceEmail({ amount, dueDate }: { amount: number; dueDate: string }): EmailTemplate { +export function invoiceEmail( + { amount, dueDate }: { amount: number; dueDate: string }, +): EmailTemplate { return { subject: `Invoice for $${amount}`, - html: baseTemplate("Invoice", `

Amount due: $${amount}

Due by: ${dueDate}

`), + html: baseTemplate( + "Invoice", + `

Amount due: $${amount}

Due by: ${dueDate}

`, + ), text: `Invoice\n\nAmount due: $${amount}\nDue by: ${dueDate}`, }; } ``` -`baseTemplate(title, body)` handles the outer HTML shell and sign-off. All templates export `{ subject, html, text }`. +`baseTemplate(title, body)` handles the outer HTML shell and sign-off. All +templates export `{ subject, html, text }`. --- ## Redis -A shared `ioredis` client is available for caching, pub/sub, or anything else that needs Redis. +A shared `ioredis` client is available for caching, pub/sub, or anything else +that needs Redis. **Requires env var:** `REDIS_URL` ### Usage ```typescript -import { redis, getRedis } from "@/lib/redis"; +import { getRedis, redis } from "@/lib/redis"; // redis is null if REDIS_URL is not set — safe to import unconditionally if (redis) { @@ -487,17 +539,17 @@ Pagination utilities live in `@repo/shared` — no infrastructure required. ```typescript import { - paginationSchema, - paginationQuery, - paginatedSuccessSchema, paginate, + paginatedSuccessSchema, + paginationQuery, + paginationSchema, } from "@repo/shared"; ``` ### Route definition ```typescript -import { paginationSchema, paginatedSuccessSchema } from "@repo/shared"; +import { paginatedSuccessSchema, paginationSchema } from "@repo/shared"; import { apiErrorSchema } from "@repo/shared"; export const listPosts = createRoute({ @@ -505,7 +557,7 @@ export const listPosts = createRoute({ path: "/posts", tags, request: { - query: paginationSchema, // parses ?page=1&limit=20 with defaults + query: paginationSchema, // parses ?page=1&limit=20 with defaults }, responses: { [OK]: jsonRes(paginatedSuccessSchema(selectPostSchema), "Paginated posts"), @@ -557,12 +609,13 @@ export const listPostsHandler: AppRouteHandler = async (c) => { A thin read-through cache over the existing Redis client. -**Requires env var:** `REDIS_URL` — degrades gracefully to a direct function call when Redis is absent. +**Requires env var:** `REDIS_URL` — degrades gracefully to a direct function +call when Redis is absent. ### API ```typescript -import { withCache, invalidateCache, cacheKey } from "@/lib/cache"; +import { cacheKey, invalidateCache, withCache } from "@/lib/cache"; ``` ### Read-through cache @@ -571,8 +624,8 @@ import { withCache, invalidateCache, cacheKey } from "@/lib/cache"; // In a repository: export async function findUserById(id: string) { return withCache( - cacheKey("user", id), // → "user:abc123" - 300, // TTL: 5 minutes + cacheKey("user", id), // → "user:abc123" + 300, // TTL: 5 minutes () => db.query.users.findFirst({ where: eq(users.id, id) }), ); } @@ -594,9 +647,9 @@ await invalidateCache( ### Behaviour -| Scenario | Result | -|----------|--------| -| Redis available, key exists | Returns cached value (no DB call) | -| Redis available, cache miss | Calls `fn`, stores result, returns value | -| Redis down or absent | Calls `fn` directly — request never fails | -| Cache write fails | Swallowed — the value is still returned | +| Scenario | Result | +| --------------------------- | ----------------------------------------- | +| Redis available, key exists | Returns cached value (no DB call) | +| Redis available, cache miss | Calls `fn`, stores result, returns value | +| Redis down or absent | Calls `fn` directly — request never fails | +| Cache write fails | Swallowed — the value is still returned | diff --git a/docs/DENO_WORKSPACE_SCOPE.md b/docs/DENO_WORKSPACE_SCOPE.md new file mode 100644 index 0000000..894fcc8 --- /dev/null +++ b/docs/DENO_WORKSPACE_SCOPE.md @@ -0,0 +1,267 @@ +# Deno Workspace Architecture + +The monorepo uses Deno's native workspace system. Each package is +self-describing with its own `deno.json`, and bare specifiers like `@repo/shared` +resolve through workspace member names — no global import map entries needed. + +--- + +## Why + +A flat root `deno.json` with all imports works, but doesn't scale. Every package +change touches the same file, per-package concerns are mingled, and the LSP +can't scope imports to the correct context. Workspaces give us: + +- **Self-describing packages** — each `deno.json` declares its own deps +- **Bare specifier resolution** — `@repo/shared` resolves from workspace member + `name`, not an import map entry +- **LSP accuracy** — per-package config means the editor resolves imports per + context +- **Progressive abstraction** — packages can escalate from no config to full + config as they grow + +--- + +## Current State + +``` +deno.json (root) + workspace: [apps/backend, apps/frontend, packages/shared, packages/db, packages/email-templates] + tasks: dev, dev:frontend, dev:backend, start, worker, test, lint, fmt, check, db:* + imports: @std/expect, @std/testing/bdd (shared dev/test deps only) + nodeModulesDir: auto + sloppyImports: true + ├── apps/ + │ ├── backend/ deno.json — 40+ import map entries (npm: + @/* aliases) + │ └── frontend/ deno.json — @/ import map, vite tasks (consumer only, no name) + └── packages/ + ├── shared/ deno.json — name @repo/shared, pure TS, zero npm deps + ├── db/ deno.json — name @repo/db, exports ./ and ./schema + └── email-templates/ deno.json — name @repo/email-templates, zero npm deps +``` + +### Dependency graph + +``` +apps/backend + └── @repo/shared (20+ import sites) + └── @repo/db (7 import sites, mostly @repo/db/schema) + └── @repo/email-templates + +packages/db + └── drizzle-orm, postgres, drizzle-zod (npm: in its own imports) + +packages/shared — zero external deps, pure TypeScript +packages/email-templates — zero external deps, pure TypeScript + +apps/frontend — consumer only (Vite/browser), not imported by anything else +``` + +--- + +## Config Files + +### Root `deno.json` + +```json +{ + "workspace": [ + "apps/backend", + "apps/frontend", + "packages/shared", + "packages/db", + "packages/email-templates" + ], + "tasks": { + "dev": "deno task --cwd=apps/backend dev", + "dev:backend": "deno task --cwd=apps/backend dev", + "dev:frontend": "deno task --cwd=apps/frontend dev", + "start": "deno task --cwd=apps/backend start", + "worker": "deno task --cwd=apps/backend worker", + "test": "deno task --cwd=apps/backend test", + "lint": "deno lint", + "fmt": "deno fmt", + "check": "deno check apps/backend/src/index.ts apps/backend/src/jobs/worker.ts", + "db:migrate": "deno task --cwd=apps/backend db:migrate", + "db:studio": "deno task --cwd=apps/backend db:studio", + "db:generate": "deno task --cwd=apps/backend db:generate" + }, + "imports": { + "@std/expect": "jsr:@std/expect@^1.0.19", + "@std/testing/bdd": "jsr:@std/testing@^1.0.18/bdd" + }, + "nodeModulesDir": "auto", + "sloppyImports": true +} +``` + +Root owns: workspace membership, shared dev/test deps, top-level convenience +tasks. No npm runtime deps, no `@repo/*` entries, no `@/*` aliases. + +### `apps/backend/deno.json` + +Owns: all npm runtime deps, all `@/*` path aliases, tasks for dev/test/DB, lint +and test config. + +No `@repo/*` entries — those resolve through workspace bare specifiers. No +`nodeModulesDir` — that's a root-level concern per Deno docs. + +Its `compilerOptions.module`/`moduleResolution` are set to `NodeNext`, unlike +every other workspace member. This isn't inherited boilerplate — the backend's +`imports` map pulls in several CJS-authored npm packages with dual-package +`exports` maps (`bullmq`, `ioredis`, `pino`, `@aws-sdk/*`). `NodeNext` +resolution is what correctly types their default exports and `require()`-style +interop; the plain resolution the other members use would misinfer or reject +some of those imports. + +### `apps/frontend/deno.json` + +Consumer member only. Has `@/*` import map for Vite/React imports. No `name` or +`exports` — nothing imports the frontend. Uses `deno run -A npm:vite` for dev +and build tasks. + +It coexists with a separate `apps/frontend/tsconfig.json` rather than folding +everything into `compilerOptions` here — `tsconfig.json` is load-bearing for +`tsc --noEmit` (the actual `typecheck` task) and for Vite's `tsconfigPaths` +resolution (`vite.config.ts`'s `resolve.tsconfigPaths: true`), neither of +which reads `deno.json`. `deno.json`'s own `compilerOptions` (`jsx`, `lib`, +`types`) exist separately for `deno check`/`deno test`/the Deno LSP. They're +two different type-checkers with two different config files by necessity, not +an inconsistency to clean up. + +### `packages/shared/deno.json` + +```json +{ + "name": "@repo/shared", + "version": "0.1.0", + "exports": "./src/index.ts", + "exclude": ["node_modules"] +} +``` + +Zero external deps — pure TypeScript. The `name` field lets Deno resolve +`@repo/shared` as a bare specifier via workspace resolution. + +### `packages/db/deno.json` + +```json +{ + "name": "@repo/db", + "version": "0.1.0", + "exports": { + ".": "./src/index.ts", + "./schema": "./src/schema/index.ts" + }, + "imports": { + "drizzle-orm": "npm:drizzle-orm", + "drizzle-orm/pg-core": "npm:drizzle-orm/pg-core", + "drizzle-orm/postgres-js": "npm:drizzle-orm/postgres-js", + "drizzle-zod": "npm:drizzle-zod", + "postgres": "npm:postgres", + "dotenv": "npm:dotenv", + "drizzle-kit": "npm:drizzle-kit" + }, + "exclude": ["node_modules"] +} +``` + +The `exports` with sub-path `./schema` preserves `@repo/db/schema` imports +without needing a root import map entry. + +### `packages/email-templates/deno.json` + +```json +{ + "name": "@repo/email-templates", + "version": "0.1.0", + "exports": "./src/index.ts", + "exclude": ["node_modules"] +} +``` + +Zero external deps. + +--- + +## Module Resolution Order + +Deno resolves bare specifiers in this order: + +1. Check if specifier matches a workspace member's `name` +2. Check the local `deno.json` `imports` +3. Check the root `deno.json` `imports` + +So `apps/backend` imports `@repo/shared` and it resolves through step 1 +(workspace member name). No import map entry needed. This is the core mechanism +that lets us keep the root `imports` clean. + +--- + +## Edge Cases + +### 1. All members must exist + +If root has a `"workspace"` array, ANY `deno.json` under the root directory MUST +be a workspace member. The `"exclude"` field only affects `deno fmt`/`deno +lint`/`deno test`, NOT workspace validation. This means the Dockerfile for the +backend must copy stub `deno.json` files for all members before `deno cache` +runs. + +### 2. `nodeModulesDir` placement + +Per Deno docs, `nodeModulesDir` is only valid at the workspace root level (OK in +root, rejected in member configs). It lives in root `deno.json` only. + +### 3. Duplicate npm import declarations + +Both `packages/db` and `apps/backend` import `drizzle-orm` in their own +`deno.json`. Deno deduplicates these to a single npm install. + +### 4. Frontend is a consumer-only member + +The frontend `deno.json` has no `name` or `exports`. It needs to be a workspace +member (to pass validation) but isn't imported by anything. The Docker build +copies its `deno.json` as a stub. + +### 5. pnpm workspace + +The frontend's `package.json` still uses pnpm for dependency management (Vite, +React, etc.). The Deno workspace and pnpm workspace coexist — Deno handles the +backend and packages, pnpm handles the frontend's npm deps. The `package.json` +has been cleaned up: no `@repo/*` workspace deps (those are resolved via Deno +workspace now). + +### 6. Frontend tests need Deno ≥ 2.8.0, and they run in their own process + +`apps/frontend`'s tests use `npm:jsdom` under `deno test` +(`apps/frontend/src/test-setup.ts` installs the DOM globals). `jsdom@30` +pulls in `undici@8.9.0`, whose `CacheStorage` constructor calls +`webidl.util.markAsUncloneable` — a Node-compat function Deno doesn't +provide until 2.8.0. On 2.7.14 (what the Dockerfiles pin for +production), it throws: + +``` +error: (in promise) TypeError: webidl.util.markAsUncloneable is not a function + at new CacheStorage (.../undici@8.9.0/node_modules/undici/lib/web/cache/cachestorage.js:20:17) + at Object. (.../jsdom@30.0.1/node_modules/jsdom/lib/api.js:12:33) +``` + +I found this by installing 2.7.14 with `mise` and reproducing it directly, +then bisecting up to 2.8.0 where it's fixed. CI pins `2.9.0` — a full +minor ahead of what ships in the Docker images — because test-tooling +correctness and production-runtime parity are different concerns here: the +Dockerfiles never run `deno test`, so they don't need the newer Deno, and +floating CI's version just to chase this fix would reintroduce the exact +drift `deno-version` pinning was meant to prevent. + +The second half of this: don't run `deno test -A` unscoped from the repo +root once `apps/frontend` has tests. Deno's workspace auto-discovery will +happily sweep frontend and backend test files into one process, and an +uncaught rejection in one (like the jsdom crash above, before it was fixed) +corrupts shared runtime state — like the global `fetch`/`undici` +implementation — for the other. I watched it take out unrelated +`handlers.test.ts` assertions in the same CI run, with no code connecting +the two. CI runs `deno test -A --ignore=apps/frontend` and a separate +`deno task test` inside `apps/frontend` as two different steps — two +processes, two failure domains. diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 1ce3309..eeed358 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -4,12 +4,13 @@ Get your app live in 15 minutes. ## TL;DR -| Part | Where | Cost | -|------|-------|------| -| Backend | Railway, Render, or any VPS | $5-20/mo | -| Frontend | Vercel | Free | -| Database | Supabase, Neon, or Railway | Free tier available | -| Redis | Upstash or Railway | Free tier available | +| Part | Where | Cost | +| ---------- | --------------------------- | ------------------- | +| Full stack | Docker Compose on any VPS | $5-20/mo | +| Backend | Railway, Render, or any VPS | $5-20/mo | +| Frontend | Vercel, or Docker on VPS | Free | +| Database | Supabase, Neon, or Railway | Free tier available | +| Redis | Upstash or Railway | Free tier available | ## 1. Database @@ -50,7 +51,33 @@ FRONTEND_URL=https:// 1. Railway auto-deploys on push -### Option B: Any VPS (more control) +### Option B: Docker Compose (whole stack on one VPS) + +The simplest production setup: one subdomain, Caddy reverse proxy with +path-based routing. Caddy serves the SPA frontend at `/` and proxies `/api/*` to +the backend. + +```bash +# Install dependencies +curl -fsSL https://get.docker.com | sh + +# Clone +git clone app && cd app + +# Set secrets +echo "BETTER_AUTH_SECRET=$(openssl rand -hex 32)" >> .env +echo "DATABASE_URL=postgres://..." >> .env +echo "BETTER_AUTH_URL=https://yourdomain.com" >> .env +echo "FRONTEND_URL=https://yourdomain.com" >> .env + +# Start everything +docker compose up -d +``` + +See `docker-compose.yml` for the full service definition. The frontend runs on +port 80 (Caddy) and the backend on port 9999 (internal). + +### Option C: Any VPS (separate services) SSH into your server: @@ -58,7 +85,7 @@ SSH into your server: # Install dependencies curl -fsSL https://get.docker.com | sh -# Clone and build +# Clone and build backend git clone app && cd app docker build -t api -f apps/backend/Dockerfile . @@ -92,7 +119,7 @@ sudo systemctl reload caddy 2. Import at [vercel.com/new](https://vercel.com/new) 3. Set: - **Root Directory**: `apps/frontend` - - **Build Command**: `cd ../.. && pnpm build:frontend` + - **Build Command**: `cd ../.. && pnpm --filter frontend build` - **Output Directory**: `dist` 4. Add environment variable: - `VITE_API_URL` = `https://api.yourdomain.com` @@ -146,7 +173,8 @@ If you're using the job queue, run the worker alongside your API: Add a second service pointing to the same repo: -- **Start Command**: `pnpm --filter backend jobs` +- **Start Command**: + `deno run --env-file=.env -A apps/backend/src/jobs/worker.ts` ### VPS @@ -157,38 +185,51 @@ docker run -d \ -e DATABASE_URL="..." \ -e REDIS_URL="..." \ api \ - node src/jobs/worker.js + deno run -A src/jobs/worker.ts ``` --- ## Environment Variables Reference +Every var below is set in `apps/backend/.env` (see `.env.example` for the +full annotated template). See [docs/BATTERIES.md](BATTERIES.md) for what each +battery does once its vars are set, not just which vars exist. + ### Required ```bash DATABASE_URL=postgres://user:pass@host:5432/db BETTER_AUTH_SECRET=<32+ random characters> BETTER_AUTH_URL=https://api.yourdomain.com +SERVER_URL=https://api.yourdomain.com # usually the same as BETTER_AUTH_URL FRONTEND_URL=https://yourdomain.com ``` ### Optional ```bash -# Redis +# Redis — background jobs (BATTERIES.md#background-jobs), caching (#caching) REDIS_URL=redis://... -# File storage +# File storage — presigned S3/R2 uploads (BATTERIES.md#file-uploads) S3_ENDPOINT=https://... S3_BUCKET=uploads S3_REGION=auto S3_ACCESS_KEY_ID=... S3_SECRET_ACCESS_KEY=... -# Email +# Email — Resend (BATTERIES.md#email). Without this, sendEmail logs instead +# of sending, so auth flows still work end-to-end in dev with no setup. RESEND_API_KEY=re_... +# Social OAuth — activates only when both vars for a provider are set +# (BATTERIES.md#social-oauth-google--github) +GOOGLE_CLIENT_ID=... +GOOGLE_CLIENT_SECRET=... +GITHUB_CLIENT_ID=... +GITHUB_CLIENT_SECRET=... + # Tuning PORT=9999 LOG_LEVEL=info @@ -207,14 +248,11 @@ curl https://api.yourdomain.com/api/health ## Troubleshooting -**502 Bad Gateway** -→ Backend isn't running. Check logs: `docker logs api` +**502 Bad Gateway** → Backend isn't running. Check logs: `docker logs api` -**CORS errors** -→ Make sure `FRONTEND_URL` matches exactly (including https) +**CORS errors** → Make sure `FRONTEND_URL` matches exactly (including https) -**Auth not working** -→ Check `BETTER_AUTH_URL` matches your API domain +**Auth not working** → Check `BETTER_AUTH_URL` matches your API domain -**Database connection refused** -→ Whitelist your server IP in your database provider's dashboard +**Database connection refused** → Whitelist your server IP in your database +provider's dashboard diff --git a/docs/PHILOSOPHY.md b/docs/PHILOSOPHY.md index 3487e1f..756ac1a 100644 --- a/docs/PHILOSOPHY.md +++ b/docs/PHILOSOPHY.md @@ -1,18 +1,30 @@ # Engineering Philosophy -The beliefs that anchor every decision in this codebase. Not rules — beliefs. Rules can be followed without understanding. Beliefs change how you see the problem. +The beliefs that anchor every decision in this codebase. Not rules — beliefs. +Rules can be followed without understanding. Beliefs change how you see the +problem. --- ## Simple Is Not Easy -Simple code is the hardest code to write. It requires you to fully understand the problem before you touch the keyboard, make real choices about what to leave out, and resist the pull of every interesting abstraction that presents itself along the way. +Simple code is the hardest code to write. It requires you to fully understand +the problem before you touch the keyboard, make real choices about what to leave +out, and resist the pull of every interesting abstraction that presents itself +along the way. -Complex code is easy. Every time you're uncertain, you add a layer. Every time requirements might change, you add an interface. Every time a pattern seems reusable, you generalize it. The result is a codebase that looks thorough and feels impressive — until you have to change something, and suddenly you're paying for every assumption you ever deferred. +Complex code is easy. Every time you're uncertain, you add a layer. Every time +requirements might change, you add an interface. Every time a pattern seems +reusable, you generalize it. The result is a codebase that looks thorough and +feels impressive — until you have to change something, and suddenly you're +paying for every assumption you ever deferred. -Simple systems scale. Not because they're small, but because they're honest. They say exactly what they do, do exactly what they say, and leave no hidden state for the next person to stumble into. +Simple systems scale. Not because they're small, but because they're honest. +They say exactly what they do, do exactly what they say, and leave no hidden +state for the next person to stumble into. -**The discipline:** before you add something, ask what it would cost to remove it. If you can't answer that, you don't understand it well enough to add it. +**The discipline:** before you add something, ask what it would cost to remove +it. If you can't answer that, you don't understand it well enough to add it. --- @@ -20,21 +32,37 @@ Simple systems scale. Not because they're small, but because they're honest. The Tools change. The principles that make tools worth choosing don't. -ESLint becomes Biome. Prisma becomes Drizzle. Radix becomes Base UI. The tools in this codebase are specific choices made at a specific time — they can and will be replaced. What doesn't change is the question we ask when choosing them: does this tool help the next person understand what's happening, or does it add machinery they have to learn before they can read the code? +ESLint becomes Biome. Prisma becomes Drizzle. Radix becomes Base UI. The tools +in this codebase are specific choices made at a specific time — they can and +will be replaced. What doesn't change is the question we ask when choosing them: +does this tool help the next person understand what's happening, or does it add +machinery they have to learn before they can read the code? -The best tool is often the one that teaches you the least on the way to the thing you were actually trying to build. +The best tool is often the one that teaches you the least on the way to the +thing you were actually trying to build. --- ## Progressive Abstraction -Start with the simplest implementation that solves the real problem in front of you. +Start with the simplest implementation that solves the real problem in front of +you. -Not the problem you might have in six months. Not the pattern you read about last week. The exact problem you have right now, in the simplest form that handles it correctly. +Not the problem you might have in six months. Not the pattern you read about +last week. The exact problem you have right now, in the simplest form that +handles it correctly. -Abstractions earn their existence by appearing more than once. A `tryInfra` wrapper exists because every repository function needed to catch infrastructure errors and every one needed to do it the same way. A `Result` type exists because every fallible function needed a way to return failures without throwing. Neither of these was designed upfront — they crystallized after the pattern repeated. +Abstractions earn their existence by appearing more than once. A `tryInfra` +wrapper exists because every repository function needed to catch infrastructure +errors and every one needed to do it the same way. A `Result` type exists +because every fallible function needed a way to return failures without +throwing. Neither of these was designed upfront — they crystallized after the +pattern repeated. -The danger of premature abstraction is that it looks like wisdom. It has interfaces and generics and thoughtful naming. It also locks in assumptions about how the system will be used before you know how it will be used. Those assumptions are wrong more often than they're right. +The danger of premature abstraction is that it looks like wisdom. It has +interfaces and generics and thoughtful naming. It also locks in assumptions +about how the system will be used before you know how it will be used. Those +assumptions are wrong more often than they're right. **The discipline:** write it twice before you abstract it. @@ -42,39 +70,77 @@ The danger of premature abstraction is that it looks like wisdom. It has interfa ## Craftsmanship -Code is read far more than it is written. The primary audience for the code you're writing is not the computer — it's the person who reads it next, which will usually be you. +Code is read far more than it is written. The primary audience for the code +you're writing is not the computer — it's the person who reads it next, which +will usually be you. -Craftsmanship is not cleverness. A clever solution is one that only the person who wrote it understands. A crafted solution is one where the next reader can see exactly what it does and why — where the variable names, the function boundaries, and the comment choices all reduce the cognitive load rather than increasing it. +Craftsmanship is not cleverness. A clever solution is one that only the person +who wrote it understands. A crafted solution is one where the next reader can +see exactly what it does and why — where the variable names, the function +boundaries, and the comment choices all reduce the cognitive load rather than +increasing it. -This shows up in small things: naming a variable `userId` instead of `id`, splitting a 40-line handler into a use-case and a handler, writing a comment that says *why* instead of *what*. None of these changes are individually significant. Accumulated across a codebase, they're the difference between code that new teammates can navigate in a day and code that requires a guided tour. +This shows up in small things: naming a variable `userId` instead of `id`, +splitting a 40-line handler into a use-case and a handler, writing a comment +that says _why_ instead of _what_. None of these changes are individually +significant. Accumulated across a codebase, they're the difference between code +that new teammates can navigate in a day and code that requires a guided tour. --- ## Human Experience First -Performance, reliability, and security are not engineering concerns — they're user concerns. Every millisecond of latency is a real person waiting. Every 500 error is a real person seeing a broken page. Every security gap is a real person's data at risk. +Performance, reliability, and security are not engineering concerns — they're +user concerns. Every millisecond of latency is a real person waiting. Every 500 +error is a real person seeing a broken page. Every security gap is a real +person's data at risk. -This doesn't mean premature optimization. It means holding onto the fact that the code is not the end product. The experience of the person using what you built is the end product. Engineering choices that look neutral — how errors are returned, how loading states are handled, how auth tokens are managed — are all choices that eventually affect a real human in a real moment. +This doesn't mean premature optimization. It means holding onto the fact that +the code is not the end product. The experience of the person using what you +built is the end product. Engineering choices that look neutral — how errors are +returned, how loading states are handled, how auth tokens are managed — are all +choices that eventually affect a real human in a real moment. -Build with that person in mind. Not as an abstraction, but as a real person with limited time and zero patience for things that don't work. +Build with that person in mind. Not as an abstraction, but as a real person with +limited time and zero patience for things that don't work. --- ## Influences -These are the thinkers and practitioners whose work shaped how we think about building software. +These are the thinkers and practitioners whose work shaped how we think about +building software. -**[37signals / Jason Fried / DHH](https://37signals.com)** — the source of "simple is not easy". Their argument: most complexity is chosen, not necessary. Constraints produce better software. Doing less, deliberately, is a form of quality. Read *Getting Real*, *REWORK*, and *Shape Up*. +**[37signals / Jason Fried / DHH](https://37signals.com)** — the source of +"simple is not easy". Their argument: most complexity is chosen, not necessary. +Constraints produce better software. Doing less, deliberately, is a form of +quality. Read _Getting Real_, _REWORK_, and _Shape Up_. -**[The Primeagen](https://www.youtube.com/@ThePrimeagen)** — the performance-first mindset and the insistence on understanding what your code actually does. Not what you think it does, not what the abstraction says it does — what it *actually* does, at the level of the machine. That clarity transfers upward to architecture. +**[The Primeagen](https://www.youtube.com/@ThePrimeagen)** — the +performance-first mindset and the insistence on understanding what your code +actually does. Not what you think it does, not what the abstraction says it does +— what it _actually_ does, at the level of the machine. That clarity transfers +upward to architecture. -**[Charity Majors](https://charity.wtf)** — observability and the idea that you're not done when it's deployed, you're done when you understand how it behaves in production. Also: earned opinions. Don't recommend things you haven't been burned by. Write from scar tissue. +**[Charity Majors](https://charity.wtf)** — observability and the idea that +you're not done when it's deployed, you're done when you understand how it +behaves in production. Also: earned opinions. Don't recommend things you haven't +been burned by. Write from scar tissue. -**[Vercel](https://vercel.com)** — what great developer experience looks like in practice. The best DX is the one that makes the right path the easy path. Zero-config defaults. Errors that tell you what to do. A tool that gets out of your way. +**[Vercel](https://vercel.com)** — what great developer experience looks like in +practice. The best DX is the one that makes the right path the easy path. +Zero-config defaults. Errors that tell you what to do. A tool that gets out of +your way. -**[Netflix Engineering](https://netflixtechblog.com)** — what quality engineering looks like at scale. Not because we're at that scale, but because the principles — chaos engineering, observability, culture of ownership — apply at any size. You own what you ship, past the merge button. +**[Netflix Engineering](https://netflixtechblog.com)** — what quality +engineering looks like at scale. Not because we're at that scale, but because +the principles — chaos engineering, observability, culture of ownership — apply +at any size. You own what you ship, past the merge button. -**[Cal Newport](https://calnewport.com)** — the case for depth over breadth, and for protecting the cognitive space required to do hard things well. Multi-tasking is a myth. Half-done work is worse than not-started work. Finish what you start. +**[Cal Newport](https://calnewport.com)** — the case for depth over breadth, and +for protecting the cognitive space required to do hard things well. +Multi-tasking is a myth. Half-done work is worse than not-started work. Finish +what you start. --- @@ -82,10 +148,19 @@ These are the thinkers and practitioners whose work shaped how we think about bu Every decision in this codebase connects back to these beliefs: -- **The 40-line Result type** instead of Effect or neverthrow — progressive abstraction. We own exactly what we need, nothing more. -- **Biome over ESLint + Prettier** — tools over principles, but the simpler tool. One binary, one config, zero plugin conflicts. -- **Base UI over Radix** — the tool that stays out of our way. No z-index negotiation, no portal fighting. -- **`tryInfra` as the single catch boundary** — craftsmanship. One place where infrastructure errors are caught, so there's one place to look when something breaks. -- **The module scaffolder** — making the right path the easy path. Convention enforced by a script is better than convention enforced by a wiki. - -When you're making a decision that isn't covered by the existing patterns, come back to these beliefs. They're the question to ask before you add something. +- **The 40-line Result type** instead of Effect or neverthrow — progressive + abstraction. We own exactly what we need, nothing more. +- **Deno lint/fmt (backend) + Biome (frontend) over ESLint + Prettier** — tools + over principles, but the simpler tool. Deno's built-in linter and formatter + handle the backend; Biome handles the frontend. One binary each, no plugin + conflicts. +- **Base UI over Radix** — the tool that stays out of our way. No z-index + negotiation, no portal fighting. +- **`tryInfra` as the single catch boundary** — craftsmanship. One place where + infrastructure errors are caught, so there's one place to look when something + breaks. +- **The module scaffolder** — making the right path the easy path. Convention + enforced by a script is better than convention enforced by a wiki. + +When you're making a decision that isn't covered by the existing patterns, come +back to these beliefs. They're the question to ask before you add something. diff --git a/docs/WRITING.md b/docs/WRITING.md index b71c2f5..85cd8be 100644 --- a/docs/WRITING.md +++ b/docs/WRITING.md @@ -1,72 +1,110 @@ # Writing Style Guide -This document covers how we write — articles, docs, READMEs, commit messages, everything that a human reads rather than a compiler. +This document covers how we write — articles, docs, READMEs, commit messages, +everything that a human reads rather than a compiler. --- ## The Four Teachers -These four writers shaped the voice we're going for. Each one contributes something specific. +These four writers shaped the voice we're going for. Each one contributes +something specific. -### William Zinsser — *On Writing Well* +### William Zinsser — _On Writing Well_ -Every sentence earns its place. Cut the word that restates what the previous word already said. No "in order to" when "to" works. No "the reason is because" when "because" works. No filler clause that delays the real sentence. +Every sentence earns its place. Cut the word that restates what the previous +word already said. No "in order to" when "to" works. No "the reason is because" +when "because" works. No filler clause that delays the real sentence. The reader's time is not yours to waste. -Read: *On Writing Well* (the whole book, not a summary) +Read: _On Writing Well_ (the whole book, not a summary) ### Charity Majors — earned opinions, no hedging -Write from scar tissue, not authority. Don't say *"you should handle errors as values"* — say *"I've debugged enough midnight incidents where a thrown exception vanished into a catch-all to know: if it can fail, the type needs to say so."* +Write from scar tissue, not authority. Don't say _"you should handle errors as +values"_ — say _"I've debugged enough midnight incidents where a thrown +exception vanished into a catch-all to know: if it can fail, the type needs to +say so."_ -State opinions flatly. Qualifiers only when the qualifier is the point. "It depends" is never a final answer — follow it with the actual answer for the context you're in. +State opinions flatly. Qualifiers only when the qualifier is the point. "It +depends" is never a final answer — follow it with the actual answer for the +context you're in. -Read: [charity.wtf](https://charity.wtf) — particularly her posts on observability and engineering management +Read: [charity.wtf](https://charity.wtf) — particularly her posts on +observability and engineering management ### Casey Muratori — show the machine -Don't describe the pattern, show the actual code, then explain *why* the shape of it is right. Walk the reader through the thinking as it happened, not the cleaned-up post-hoc version. Treat the reader as someone smart who just hasn't seen this yet. +Don't describe the pattern, show the actual code, then explain _why_ the shape +of it is right. Walk the reader through the thinking as it happened, not the +cleaned-up post-hoc version. Treat the reader as someone smart who just hasn't +seen this yet. -The tutorial that works is the one that never skips the step where things were confusing. +The tutorial that works is the one that never skips the step where things were +confusing. -Read: [Handmade Hero](https://handmadehero.org) — watch how he narrates code as he writes it +Read: [Handmade Hero](https://handmadehero.org) — watch how he narrates code as +he writes it ### Cal Newport — one claim per section, defended, closed -Each section has one argument. You know what the section argues before you read it. You know it's done when the argument lands. No meandering. No "as we've seen above". No recap of what was just said. +Each section has one argument. You know what the section argues before you read +it. You know it's done when the argument lands. No meandering. No "as we've seen +above". No recap of what was just said. -Read: *Deep Work* and *Digital Minimalism* — notice the structure more than the content +Read: _Deep Work_ and _Digital Minimalism_ — notice the structure more than the +content --- ## The Synthesized Voice -These rules apply whether you're writing an article, a PR description, or a section of the README. +These rules apply whether you're writing an article, a PR description, or a +section of the README. -**Open with the real problem.** Make the reader feel the friction before you sell the solution. If the opener is "here is a solution", you've skipped the reason anyone should care. +**Open with the real problem.** Make the reader feel the friction before you +sell the solution. If the opener is "here is a solution", you've skipped the +reason anyone should care. -**Use "I" and "you" freely.** This is knowledge transfer between two people, not a whitepaper. "We recommend" is the voice of a committee. "I use this because" is the voice of someone who's actually been there. +**Use "I" and "you" freely.** This is knowledge transfer between two people, not +a whitepaper. "We recommend" is the voice of a committee. "I use this because" +is the voice of someone who's actually been there. -**Show code early and let it do work.** Prose explains what the code alone can't — the *why*, the tradeoffs, the history. If you're writing prose to describe something fully expressible in code, write the code instead. +**Show code early and let it do work.** Prose explains what the code alone can't +— the _why_, the tradeoffs, the history. If you're writing prose to describe +something fully expressible in code, write the code instead. **Short paragraphs.** One idea. Three sentences max. Move on. -**No throat-clearing introductions.** The first sentence of each section is already in the middle of the thought. "In this section we will explore..." is a sentence that could always be deleted. +**No throat-clearing introductions.** The first sentence of each section is +already in the middle of the thought. "In this section we will explore..." is a +sentence that could always be deleted. -**Last sentence of a section closes the claim.** Not "and that's why X matters" — that's a restatement. The last sentence draws the conclusion or sets up the next section directly. +**Last sentence of a section closes the claim.** Not "and that's why X matters" +— that's a restatement. The last sentence draws the conclusion or sets up the +next section directly. --- ## On Technical Writing Specifically -**Don't soften opinions you've earned.** If you've debugged something, built it, shipped it, and formed a view — state the view. Hedging with "this might work for some teams" when you mean "this worked for me and here's why" is a disservice to the reader. +**Don't soften opinions you've earned.** If you've debugged something, built it, +shipped it, and formed a view — state the view. Hedging with "this might work +for some teams" when you mean "this worked for me and here's why" is a +disservice to the reader. -**Explain the tradeoff, not just the choice.** Every technical decision has a cost. Readers trust writing that acknowledges what was given up more than writing that only defends what was gained. +**Explain the tradeoff, not just the choice.** Every technical decision has a +cost. Readers trust writing that acknowledges what was given up more than +writing that only defends what was gained. -**Real code from the real repo.** No pseudocode, no cleaned-up examples that don't compile. If the example changed to make it prettier for the article, say so. If it didn't, don't say so — just show it. +**Real code from the real repo.** No pseudocode, no cleaned-up examples that +don't compile. If the example changed to make it prettier for the article, say +so. If it didn't, don't say so — just show it. -**Name alternatives you rejected and say why.** "I chose X" is half the information. "I chose X over Y because Z" is the part that helps someone in a different context make their own call. +**Name alternatives you rejected and say why.** "I chose X" is half the +information. "I chose X over Y because Z" is the part that helps someone in a +different context make their own call. --- @@ -82,4 +120,7 @@ These rules apply whether you're writing an article, a PR description, or a sect ## Applied to This Project -When writing `docs/DECISIONS.md`, `docs/PATTERNS.md`, inline code comments, or PR descriptions, the same rules apply at a smaller scale. A good comment is a single sentence that says *why*, not *what*. A good PR description opens with the problem, not the solution. +When writing `docs/DECISIONS.md`, `docs/PATTERNS.md`, inline code comments, or +PR descriptions, the same rules apply at a smaller scale. A good comment is a +single sentence that says _why_, not _what_. A good PR description opens with +the problem, not the solution. diff --git a/package.json b/package.json index f1df854..128c5b8 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,17 @@ { - "name": "orcta-stack", - "version": "1.0.0", - "license": "MIT", - "private": true, - "type": "module", - "scripts": { - "setup": "./scripts/setup.sh", - "dev": "pnpm build:packages && pnpm --parallel --filter \"./apps/*\" dev", - "dev:backend": "pnpm build:packages && pnpm --filter backend dev", - "dev:frontend": "pnpm build:packages && pnpm --filter frontend dev", - "build": "pnpm build:packages && pnpm build:apps", - "build:packages": "pnpm -r --filter \"@repo/*\" build", - "build:apps": "pnpm -r --filter ./apps/frontend build", - "typecheck": "pnpm --recursive typecheck", - "lint": "pnpm --recursive lint", - "test": "pnpm --recursive test", - "clean": "rm -rf apps/*/dist packages/*/dist node_modules/.cache", - "db:generate": "pnpm --filter @repo/db db:generate", - "db:migrate": "pnpm --filter @repo/db db:migrate", - "db:studio": "pnpm --filter @repo/db db:studio", - "new:module": "./scripts/new-module.sh" - }, - "devDependencies": { - "@biomejs/biome": "2.3.7", - "@types/node": "^25.0.3", - "tsx": "^4.21.0", - "typescript": "^5.9.3" - }, - "dependencies": { - "zod": "4.2.1" - }, - "engines": { - "node": ">=20.0.0" - }, - "packageManager": "pnpm@9.15.0" + "name": "orcta-stack", + "version": "1.0.0", + "license": "MIT", + "private": true, + "type": "module", + "scripts": { + "setup": "./scripts/setup.sh", + "lint": "deno lint", + "fmt": "deno fmt", + "clean": "rm -rf apps/frontend/dist apps/frontend/node_modules node_modules/.cache" + }, + "devDependencies": { + "@biomejs/biome": "2.3.7" + }, + "packageManager": "pnpm@9.15.0" } diff --git a/packages/db/deno.json b/packages/db/deno.json new file mode 100644 index 0000000..1e0e08e --- /dev/null +++ b/packages/db/deno.json @@ -0,0 +1,26 @@ +{ + "name": "@repo/db", + "version": "0.1.0", + "exports": { + ".": "./src/index.ts", + "./schema": "./src/schema/index.ts" + }, + "imports": { + "drizzle-orm": "npm:drizzle-orm", + "drizzle-orm/pg-core": "npm:drizzle-orm/pg-core", + "drizzle-orm/postgres-js": "npm:drizzle-orm/postgres-js", + "drizzle-zod": "npm:drizzle-zod", + "postgres": "npm:postgres", + "dotenv": "npm:dotenv", + "drizzle-kit": "npm:drizzle-kit" + }, + "exclude": ["node_modules"], + "lint": { + "rules": { + "exclude": ["no-slow-types"] + } + }, + "test": { + "include": ["src/**/*.test.ts"] + } +} diff --git a/packages/db/drizzle.config.ts b/packages/db/drizzle.config.ts index 230ba39..431ecc6 100644 --- a/packages/db/drizzle.config.ts +++ b/packages/db/drizzle.config.ts @@ -1,14 +1,15 @@ +import process from "node:process"; import { config } from "dotenv"; import { defineConfig } from "drizzle-kit"; config({ path: "../../apps/backend/.env" }); export default defineConfig({ - schema: "./src/schema/*.ts", - out: "./migrations", - dialect: "postgresql", - dbCredentials: { - url: process.env.DATABASE_URL || "", - }, - verbose: true, - strict: true, + schema: "./src/schema/*.ts", + out: "./migrations", + dialect: "postgresql", + dbCredentials: { + url: process.env.DATABASE_URL || "", + }, + verbose: true, + strict: true, }); diff --git a/packages/db/migrations/0001_curious_fantastic_four.sql b/packages/db/migrations/0001_curious_fantastic_four.sql new file mode 100644 index 0000000..642a21d --- /dev/null +++ b/packages/db/migrations/0001_curious_fantastic_four.sql @@ -0,0 +1,10 @@ +ALTER TABLE "users" ALTER COLUMN "role" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "role" SET DEFAULT 'buyer'::text;--> statement-breakpoint +UPDATE "users" SET "role" = 'buyer' WHERE "role" = 'user';--> statement-breakpoint +DROP TYPE "public"."user_role";--> statement-breakpoint +CREATE TYPE "public"."user_role" AS ENUM('buyer', 'seller', 'admin');--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "role" SET DEFAULT 'buyer'::"public"."user_role";--> statement-breakpoint +ALTER TABLE "users" ALTER COLUMN "role" SET DATA TYPE "public"."user_role" USING "role"::"public"."user_role";--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "two_factor_enabled" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "two_factor_secret" text;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "backup_codes" text; \ No newline at end of file diff --git a/packages/db/migrations/meta/0000_snapshot.json b/packages/db/migrations/meta/0000_snapshot.json index 42d5486..5defa07 100644 --- a/packages/db/migrations/meta/0000_snapshot.json +++ b/packages/db/migrations/meta/0000_snapshot.json @@ -336,4 +336,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/packages/db/migrations/meta/0001_snapshot.json b/packages/db/migrations/meta/0001_snapshot.json new file mode 100644 index 0000000..add9c3a --- /dev/null +++ b/packages/db/migrations/meta/0001_snapshot.json @@ -0,0 +1,359 @@ +{ + "id": "161a6497-08a5-4c29-8d47-6e0139642c4b", + "prevId": "d0249629-e758-4ae0-9c54-abb9f15f502f", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'buyer'" + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "two_factor_secret": { + "name": "two_factor_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verifications": { + "name": "verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.user_role": { + "name": "user_role", + "schema": "public", + "values": [ + "buyer", + "seller", + "admin" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json index 472214b..7113f41 100644 --- a/packages/db/migrations/meta/_journal.json +++ b/packages/db/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1771546879712, "tag": "0000_loose_cassandra_nova", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1778377769195, + "tag": "0001_curious_fantastic_four", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/package.json b/packages/db/package.json index 1b79a9a..2555c7a 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,50 +1,5 @@ { - "name": "@repo/db", - "version": "0.0.0", - "private": true, - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./schema": { - "types": "./dist/schema/index.d.ts", - "import": "./dist/schema/index.js" - }, - "./types": { - "types": "./dist/types.d.ts", - "import": "./dist/types.js" - } - }, - "scripts": { - "prebuild": "rimraf dist tsconfig.tsbuildinfo", - "build": "tsc --build --verbose", - "dev": "tsc --build --watch", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit push", - "db:push:ci": "drizzle-kit push --force", - "db:studio": "drizzle-kit studio" - }, - "dependencies": { - "dotenv": "^16.6.1", - "drizzle-orm": "^0.44.7", - "drizzle-zod": "0.8.3", - "zod": "^4.2.1" - }, - "devDependencies": { - "@types/node": "^22.19.3", - "drizzle-kit": "^0.31.8", - "postgres": "^3.4.7", - "rimraf": "^6.1.2", - "typescript": "^5.9.3" - }, - "sideEffects": false, - "engines": { - "node": ">=20.0.0" - } + "name": "@repo/db", + "private": true, + "type": "module" } diff --git a/packages/db/src/__tests__/schema.test.ts b/packages/db/src/__tests__/schema.test.ts new file mode 100644 index 0000000..d973209 --- /dev/null +++ b/packages/db/src/__tests__/schema.test.ts @@ -0,0 +1,80 @@ +// Schema/type-level tests — no DB connection. packages/db has no live client +// of its own (that lives in apps/backend/src/db); these tests validate the +// drizzle table definitions and drizzle-zod schemas in isolation. + +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; +import { + insertUserSchema, + selectUserSchema, + userRoleEnum, +} from "../schema/users.ts"; + +// ─── userRoleEnum ─────────────────────────────────────────────────────────── + +describe("userRoleEnum", () => { + it("has exactly buyer, seller, and admin", () => { + expect(userRoleEnum.enumValues).toEqual(["buyer", "seller", "admin"]); + }); +}); + +// ─── insertUserSchema ─────────────────────────────────────────────────────── + +describe("insertUserSchema", () => { + const validInsert = { + id: "user-1", + email: "alice@example.com", + name: "Alice", + }; + + it("accepts a minimal valid insert, defaulting role-dependent fields", () => { + expect(() => insertUserSchema.parse(validInsert)).not.toThrow(); + }); + + it("accepts an explicit valid role", () => { + const result = insertUserSchema.parse({ ...validInsert, role: "seller" }); + expect(result.role).toBe("seller"); + }); + + it("rejects a role outside the enum", () => { + expect(() => insertUserSchema.parse({ ...validInsert, role: "superadmin" })) + .toThrow(); + }); + + it("rejects when a required field is missing", () => { + const { email: _email, ...withoutEmail } = validInsert; + expect(() => insertUserSchema.parse(withoutEmail)).toThrow(); + }); +}); + +// ─── selectUserSchema ─────────────────────────────────────────────────────── + +describe("selectUserSchema", () => { + const validRow = { + id: "user-1", + email: "alice@example.com", + name: "Alice", + image: null, + role: "buyer" as const, + emailVerified: true, + twoFactorEnabled: false, + twoFactorSecret: null, + backupCodes: null, + createdAt: new Date("2024-01-01"), + updatedAt: new Date("2024-01-01"), + }; + + it("accepts a fully-populated valid row", () => { + expect(() => selectUserSchema.parse(validRow)).not.toThrow(); + }); + + it("rejects a row missing a required field", () => { + const { createdAt: _createdAt, ...withoutCreatedAt } = validRow; + expect(() => selectUserSchema.parse(withoutCreatedAt)).toThrow(); + }); + + it("rejects a role outside the enum", () => { + expect(() => selectUserSchema.parse({ ...validRow, role: "root" })) + .toThrow(); + }); +}); diff --git a/packages/db/src/schema/sessions.ts b/packages/db/src/schema/sessions.ts index 7e9a989..a9360d8 100644 --- a/packages/db/src/schema/sessions.ts +++ b/packages/db/src/schema/sessions.ts @@ -2,58 +2,58 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { users } from "./users.ts"; export const sessions = pgTable("sessions", { - id: text("id").primaryKey(), - userId: text("user_id") - .notNull() - .references(() => users.id, { onDelete: "cascade" }), - token: text("token").notNull().unique(), - expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), - ipAddress: text("ip_address"), - userAgent: text("user_agent"), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true }) - .defaultNow() - .notNull(), + id: text("id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + token: text("token").notNull().unique(), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .defaultNow() + .notNull(), }); export const accounts = pgTable("accounts", { - id: text("id").primaryKey(), - userId: text("user_id") - .notNull() - .references(() => users.id, { onDelete: "cascade" }), - accountId: text("account_id").notNull(), - providerId: text("provider_id").notNull(), - accessToken: text("access_token"), - refreshToken: text("refresh_token"), - accessTokenExpiresAt: timestamp("access_token_expires_at", { - withTimezone: true, - }), - refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { - withTimezone: true, - }), - scope: text("scope"), - password: text("password"), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true }) - .defaultNow() - .notNull(), + id: text("id").primaryKey(), + userId: text("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at", { + withTimezone: true, + }), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { + withTimezone: true, + }), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .defaultNow() + .notNull(), }); export const verifications = pgTable("verifications", { - id: text("id").primaryKey(), - identifier: text("identifier").notNull(), - value: text("value").notNull(), - expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true }) - .defaultNow() - .notNull(), + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .defaultNow() + .notNull(), }); export type Session = typeof sessions.$inferSelect; diff --git a/packages/db/src/schema/users.ts b/packages/db/src/schema/users.ts index c77dbd8..9fcce4d 100644 --- a/packages/db/src/schema/users.ts +++ b/packages/db/src/schema/users.ts @@ -1,21 +1,25 @@ import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; -export const userRoleEnum = pgEnum("user_role", ["user", "admin"]); +export const userRoleEnum = pgEnum("user_role", ["buyer", "seller", "admin"]); +export type UserRole = (typeof userRoleEnum.enumValues)[number]; export const users = pgTable("users", { - id: text("id").primaryKey(), - email: text("email").notNull().unique(), - name: text("name").notNull(), - image: text("image"), - role: userRoleEnum("role").default("user").notNull(), - emailVerified: boolean("email_verified").default(false).notNull(), - createdAt: timestamp("created_at", { withTimezone: true }) - .defaultNow() - .notNull(), - updatedAt: timestamp("updated_at", { withTimezone: true }) - .defaultNow() - .notNull(), + id: text("id").primaryKey(), + email: text("email").notNull().unique(), + name: text("name").notNull(), + image: text("image"), + role: userRoleEnum("role").default("buyer").notNull(), + emailVerified: boolean("email_verified").default(false).notNull(), + twoFactorEnabled: boolean("two_factor_enabled").default(false).notNull(), + twoFactorSecret: text("two_factor_secret"), + backupCodes: text("backup_codes"), + createdAt: timestamp("created_at", { withTimezone: true }) + .defaultNow() + .notNull(), + updatedAt: timestamp("updated_at", { withTimezone: true }) + .defaultNow() + .notNull(), }); // Zod schemas for validation diff --git a/packages/db/src/types.ts b/packages/db/src/types.ts index eacc29f..15f8402 100644 --- a/packages/db/src/types.ts +++ b/packages/db/src/types.ts @@ -1,8 +1,5 @@ -import type { Account, Session } from "./schema/sessions.js"; -import type { InsertUser, User } from "./schema/users.js"; +import type { Account, Session } from "./schema/sessions.ts"; +import type { InsertUser, User, UserRole } from "./schema/users.ts"; // Re-export schema types -export type { User, InsertUser, Session, Account }; - -// Database-specific types -export type UserRole = "user" | "admin"; +export type { Account, InsertUser, Session, User, UserRole }; diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json deleted file mode 100644 index e1890a0..0000000 --- a/packages/db/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "baseUrl": ".", - "paths": { - "@repo/db": ["./src/index.ts"], - "@repo/db/schema": ["./src/schema/index.ts"], - "@repo/db/types": ["./src/types.ts"], - "@repo/db/dtos": ["./src/dtos/index.ts"] - }, - "composite": true, - "declaration": true, - "declarationMap": true, - "module": "ESNext", - "moduleResolution": "node", - "target": "ES2022", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - "allowImportingTsExtensions": true, - "rewriteRelativeImportExtensions": true, - "noEmit": false, - "verbatimModuleSyntax": true - }, - "include": ["src"], - "exclude": ["node_modules", "dist", "migrations"] -} diff --git a/packages/email-templates/deno.json b/packages/email-templates/deno.json new file mode 100644 index 0000000..e371c36 --- /dev/null +++ b/packages/email-templates/deno.json @@ -0,0 +1,9 @@ +{ + "name": "@repo/email-templates", + "version": "0.1.0", + "exports": "./src/index.ts", + "exclude": ["node_modules"], + "test": { + "include": ["src/**/*.test.ts"] + } +} diff --git a/packages/email-templates/package.json b/packages/email-templates/package.json index 2e015f0..5ef76e7 100644 --- a/packages/email-templates/package.json +++ b/packages/email-templates/package.json @@ -1,30 +1,5 @@ { - "name": "@repo/email-templates", - "version": "1.0.0", - "private": true, - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "scripts": { - "prebuild": "rimraf dist tsconfig.tsbuildinfo", - "build": "tsc --build", - "dev": "tsc --build --watch", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "test": "vitest run" - }, - "devDependencies": { - "@types/node": "^22.19.3", - "typescript": "^5.9.3", - "vitest": "^4.0.16" - }, - "dependencies": { - "rimraf": "^6.1.2" - } + "name": "@repo/email-templates", + "private": true, + "type": "module" } diff --git a/packages/email-templates/src/__tests__/index.test.ts b/packages/email-templates/src/__tests__/index.test.ts index faeaaaa..cd3db46 100644 --- a/packages/email-templates/src/__tests__/index.test.ts +++ b/packages/email-templates/src/__tests__/index.test.ts @@ -1,117 +1,118 @@ -import { describe, expect, it } from "vitest"; -import { passwordResetEmail, welcomeEmail } from "../index.js"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; +import { passwordResetEmail, welcomeEmail } from "../index.ts"; // ─── welcomeEmail ───────────────────────────────────────────────────────────── describe("welcomeEmail", () => { - it("uses the user's name in the subject", () => { - const { subject } = welcomeEmail({ name: "Alice" }); - expect(subject).toBe("Welcome, Alice!"); - }); - - it("includes the user's name in the plain text body", () => { - const { text } = welcomeEmail({ name: "Alice" }); - expect(text).toContain("Alice"); - }); - - it("includes the standard sign-off in the plain text body", () => { - const { text } = welcomeEmail({ name: "Alice" }); - expect(text).toContain("— The Team"); - }); - - it("includes the action URL in the plain text body when provided", () => { - const { text } = welcomeEmail({ - name: "Bob", - actionUrl: "https://example.com/verify", - }); - expect(text).toContain("https://example.com/verify"); - }); - - it("omits the action URL from the plain text body when not provided", () => { - const { text } = welcomeEmail({ name: "Bob" }); - expect(text).not.toContain("http"); - }); - - it("includes a verify email button in the HTML when actionUrl is provided", () => { - const { html } = welcomeEmail({ - name: "Bob", - actionUrl: "https://example.com/verify", - }); - expect(html).toContain("Verify Email"); - expect(html).toContain("https://example.com/verify"); - }); - - it("omits the verify button from the HTML when actionUrl is not provided", () => { - const { html } = welcomeEmail({ name: "Bob" }); - expect(html).not.toContain("Verify Email"); - }); - - it("returns valid HTML structure", () => { - const { html } = welcomeEmail({ name: "Charlie" }); - expect(html).toContain(""); - expect(html).toContain(" { + const { subject } = welcomeEmail({ name: "Alice" }); + expect(subject).toBe("Welcome, Alice!"); + }); + + it("includes the user's name in the plain text body", () => { + const { text } = welcomeEmail({ name: "Alice" }); + expect(text).toContain("Alice"); + }); + + it("includes the standard sign-off in the plain text body", () => { + const { text } = welcomeEmail({ name: "Alice" }); + expect(text).toContain("— The Team"); + }); + + it("includes the action URL in the plain text body when provided", () => { + const { text } = welcomeEmail({ + name: "Bob", + actionUrl: "https://example.com/verify", + }); + expect(text).toContain("https://example.com/verify"); + }); + + it("omits the action URL from the plain text body when not provided", () => { + const { text } = welcomeEmail({ name: "Bob" }); + expect(text).not.toContain("http"); + }); + + it("includes a verify email button in the HTML when actionUrl is provided", () => { + const { html } = welcomeEmail({ + name: "Bob", + actionUrl: "https://example.com/verify", + }); + expect(html).toContain("Verify Email"); + expect(html).toContain("https://example.com/verify"); + }); + + it("omits the verify button from the HTML when actionUrl is not provided", () => { + const { html } = welcomeEmail({ name: "Bob" }); + expect(html).not.toContain("Verify Email"); + }); + + it("returns valid HTML structure", () => { + const { html } = welcomeEmail({ name: "Charlie" }); + expect(html).toContain(""); + expect(html).toContain(" { - it("has the correct subject line", () => { - const { subject } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(subject).toBe("Reset your password"); - }); - - it("includes the reset URL in the plain text body", () => { - const { text } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(text).toContain("https://example.com/reset"); - }); - - it("mentions the expiry period in the plain text body", () => { - const { text } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(text).toContain("1 hour"); - }); - - it("includes the user's name in the plain text body", () => { - const { text } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(text).toContain("Alice"); - }); - - it("includes the standard sign-off in the plain text body", () => { - const { text } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(text).toContain("— The Team"); - }); - - it("includes the reset URL in the HTML", () => { - const { html } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(html).toContain("https://example.com/reset"); - expect(html).toContain("Reset Password"); - }); - - it("returns valid HTML structure", () => { - const { html } = passwordResetEmail({ - name: "Alice", - actionUrl: "https://example.com/reset", - }); - expect(html).toContain(""); - expect(html).toContain(" { + const { subject } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(subject).toBe("Reset your password"); + }); + + it("includes the reset URL in the plain text body", () => { + const { text } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(text).toContain("https://example.com/reset"); + }); + + it("mentions the expiry period in the plain text body", () => { + const { text } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(text).toContain("1 hour"); + }); + + it("includes the user's name in the plain text body", () => { + const { text } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(text).toContain("Alice"); + }); + + it("includes the standard sign-off in the plain text body", () => { + const { text } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(text).toContain("— The Team"); + }); + + it("includes the reset URL in the HTML", () => { + const { html } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(html).toContain("https://example.com/reset"); + expect(html).toContain("Reset Password"); + }); + + it("returns valid HTML structure", () => { + const { html } = passwordResetEmail({ + name: "Alice", + actionUrl: "https://example.com/reset", + }); + expect(html).toContain(""); + expect(html).toContain(" + return `

${title}

@@ -20,33 +20,36 @@ ${body} } export function welcomeEmail({ name, actionUrl }: EmailProps): EmailTemplate { - const button = actionUrl - ? `

Verify Email

` - : ""; + const button = actionUrl + ? `

Verify Email

` + : ""; - return { - subject: `Welcome, ${name}!`, - html: baseTemplate( - `Welcome, ${name}!`, - `

Thanks for signing up.

${button}`, - ), - text: `Welcome, ${name}!\n\nThanks for signing up.${actionUrl ? `\n\nVerify: ${actionUrl}` : ""}\n\n— The Team`, - }; + return { + subject: `Welcome, ${name}!`, + html: baseTemplate( + `Welcome, ${name}!`, + `

Thanks for signing up.

${button}`, + ), + text: `Welcome, ${name}!\n\nThanks for signing up.${ + actionUrl ? `\n\nVerify: ${actionUrl}` : "" + }\n\n— The Team`, + }; } export function passwordResetEmail({ - name, - actionUrl, + name, + actionUrl, }: EmailProps): EmailTemplate { - return { - subject: "Reset your password", - html: baseTemplate( - "Reset your password", - ` + return { + subject: "Reset your password", + html: baseTemplate( + "Reset your password", + `

Hi ${name}, click below to reset your password. Link expires in 1 hour.

Reset Password

`, - ), - text: `Hi ${name},\n\nReset your password: ${actionUrl}\n\nLink expires in 1 hour.\n\n— The Team`, - }; + ), + text: + `Hi ${name},\n\nReset your password: ${actionUrl}\n\nLink expires in 1 hour.\n\n— The Team`, + }; } diff --git a/packages/email-templates/tsconfig.json b/packages/email-templates/tsconfig.json deleted file mode 100644 index 6fa276e..0000000 --- a/packages/email-templates/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "node16", - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"] -} diff --git a/packages/email-templates/vitest.config.ts b/packages/email-templates/vitest.config.ts deleted file mode 100644 index dab44ae..0000000 --- a/packages/email-templates/vitest.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - globals: true, - environment: "node", - include: ["src/**/*.test.ts"], - coverage: { - reporter: ["text", "json", "html"], - }, - }, -}); diff --git a/packages/shared/deno.json b/packages/shared/deno.json new file mode 100644 index 0000000..f5accc3 --- /dev/null +++ b/packages/shared/deno.json @@ -0,0 +1,14 @@ +{ + "name": "@repo/shared", + "version": "0.1.0", + "exports": "./src/index.ts", + "imports": { + "zod": "npm:zod" + }, + "exclude": ["node_modules"], + "lint": { + "rules": { + "exclude": ["no-slow-types"] + } + } +} diff --git a/packages/shared/package.json b/packages/shared/package.json index 8e51a18..4d5f092 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,38 +1,5 @@ { - "name": "@repo/shared", - "version": "0.0.0", - "private": true, - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./types": { - "types": "./dist/types.d.ts", - "import": "./dist/types.js" - }, - "./schemas": { - "types": "./dist/schemas.d.ts", - "import": "./dist/schemas.js" - } - }, - "scripts": { - "prebuild": "rimraf dist tsconfig.tsbuildinfo", - "build": "tsc --build", - "dev": "tsc --build --watch", - "typecheck": "tsc --noEmit", - "clean": "rimraf dist", - "test": "vitest run" - }, - "dependencies": { - "zod": "^4.2.1" - }, - "devDependencies": { - "rimraf": "^6.1.2", - "typescript": "^5.9.3", - "vitest": "^4.0.16" - } + "name": "@repo/shared", + "private": true, + "type": "module" } diff --git a/packages/shared/src/__tests__/result.test.ts b/packages/shared/src/__tests__/result.test.ts index 102423a..7977c30 100644 --- a/packages/shared/src/__tests__/result.test.ts +++ b/packages/shared/src/__tests__/result.test.ts @@ -1,200 +1,222 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import { - andThen, - andThenAsync, - err, - isErr, - isOk, - map, - match, - ok, - unwrap, -} from "../result.js"; + andThen, + andThenAsync, + err, + isErr, + isOk, + map, + match, + ok, + unwrap, +} from "../result.ts"; // ─── ok / err constructors ──────────────────────────────────────────────────── describe("ok", () => { - it("creates an Ok result with the provided value", () => { - const result = ok(42); - expect(result).toEqual({ ok: true, value: 42 }); - }); - - it("works with object values", () => { - const result = ok({ id: "1", name: "Alice" }); - expect(result.ok).toBe(true); - if (result.ok) expect(result.value.name).toBe("Alice"); - }); + it("creates an Ok result with the provided value", () => { + const result = ok(42); + expect(result).toEqual({ ok: true, value: 42 }); + }); + + it("works with object values", () => { + const result = ok({ id: "1", name: "Alice" }); + expect(result.ok).toBe(true); + if (result.ok) expect(result.value.name).toBe("Alice"); + }); }); describe("err", () => { - it("creates an Err result with the provided error", () => { - const result = err({ type: "NOT_FOUND" }); - expect(result).toEqual({ ok: false, error: { type: "NOT_FOUND" } }); - }); - - it("ok property is false", () => { - expect(err("oops").ok).toBe(false); - }); + it("creates an Err result with the provided error", () => { + const result = err({ type: "NOT_FOUND" }); + expect(result).toEqual({ ok: false, error: { type: "NOT_FOUND" } }); + }); + + it("ok property is false", () => { + expect(err("oops").ok).toBe(false); + }); }); // ─── Type guards ───────────────────────────────────────────────────────────── describe("isOk", () => { - it("returns true for Ok results", () => { - expect(isOk(ok("yes"))).toBe(true); - }); + it("returns true for Ok results", () => { + expect(isOk(ok("yes"))).toBe(true); + }); - it("returns false for Err results", () => { - expect(isOk(err("no"))).toBe(false); - }); + it("returns false for Err results", () => { + expect(isOk(err("no"))).toBe(false); + }); }); describe("isErr", () => { - it("returns true for Err results", () => { - expect(isErr(err("bad"))).toBe(true); - }); + it("returns true for Err results", () => { + expect(isErr(err("bad"))).toBe(true); + }); - it("returns false for Ok results", () => { - expect(isErr(ok("good"))).toBe(false); - }); + it("returns false for Ok results", () => { + expect(isErr(ok("good"))).toBe(false); + }); }); // ─── unwrap ────────────────────────────────────────────────────────────────── describe("unwrap", () => { - it("returns the value from an Ok result", () => { - expect(unwrap(ok("hello"))).toBe("hello"); - }); - - it("throws when called on an Err result", () => { - expect(() => unwrap(err("oh no"))).toThrow( - "Called unwrap on an Err result", - ); - }); + it("returns the value from an Ok result", () => { + expect(unwrap(ok("hello"))).toBe("hello"); + }); + + it("throws when called on an Err result", () => { + expect(() => unwrap(err("oh no"))).toThrow( + "Called unwrap on an Err result", + ); + }); }); // ─── map ───────────────────────────────────────────────────────────────────── describe("map", () => { - it("transforms the Ok value with the provided function", () => { - const result = map(ok(2), (n) => n * 3); - expect(result).toEqual(ok(6)); - }); - - it("passes Err through without calling the function", () => { - const fn = vi.fn(); - const result = map(err("fail"), fn); - expect(result).toEqual(err("fail")); - expect(fn).not.toHaveBeenCalled(); - }); - - it("allows changing the value type", () => { - const result = map(ok(42), (n) => String(n)); - expect(result).toEqual(ok("42")); - }); + it("transforms the Ok value with the provided function", () => { + const result = map(ok(2), (n) => n * 3); + expect(result).toEqual(ok(6)); + }); + + it("passes Err through without calling the function", () => { + let called = false; + const fn = () => { + called = true; + }; + const result = map(err("fail"), fn); + expect(result).toEqual(err("fail")); + expect(called).toBe(false); + }); + + it("allows changing the value type", () => { + const result = map(ok(42), (n) => String(n)); + expect(result).toEqual(ok("42")); + }); }); // ─── andThen ───────────────────────────────────────────────────────────────── describe("andThen", () => { - it("chains the function on an Ok result", () => { - const result = andThen(ok(4), (n) => ok(n * 2)); - expect(result).toEqual(ok(8)); - }); - - it("short-circuits on an Err result without calling the function", () => { - const fn = vi.fn(); - const result = andThen(err("first failure"), fn); - expect(result).toEqual(err("first failure")); - expect(fn).not.toHaveBeenCalled(); - }); - - it("propagates an Err returned from the chained function", () => { - const result = andThen(ok(0), (n) => - n === 0 ? err({ type: "DIVISION_BY_ZERO" }) : ok(1 / n), - ); - expect(result).toEqual(err({ type: "DIVISION_BY_ZERO" })); - }); - - it("chains multiple operations together", () => { - const double = (n: number) => ok(n * 2); - const addOne = (n: number) => ok(n + 1); - - const result = andThen(andThen(ok(3), double), addOne); - expect(result).toEqual(ok(7)); - }); + it("chains the function on an Ok result", () => { + const result = andThen(ok(4), (n) => ok(n * 2)); + expect(result).toEqual(ok(8)); + }); + + it("short-circuits on an Err result without calling the function", () => { + let called = false; + const fn = () => { + called = true; + return ok(0); + }; + const result = andThen(err("first failure"), fn); + expect(result).toEqual(err("first failure")); + expect(called).toBe(false); + }); + + it("propagates an Err returned from the chained function", () => { + const result = andThen( + ok(0), + (n) => n === 0 ? err({ type: "DIVISION_BY_ZERO" }) : ok(1 / n), + ); + expect(result).toEqual(err({ type: "DIVISION_BY_ZERO" })); + }); + + it("chains multiple operations together", () => { + const double = (n: number) => ok(n * 2); + const addOne = (n: number) => ok(n + 1); + + const result = andThen(andThen(ok(3), double), addOne); + expect(result).toEqual(ok(7)); + }); }); // ─── andThenAsync ───────────────────────────────────────────────────────────── describe("andThenAsync", () => { - it("chains an async function on an Ok result", async () => { - const result = await andThenAsync(ok(5), async (n) => ok(n * 2)); - expect(result).toEqual(ok(10)); - }); - - it("short-circuits on an Err result without calling the function", async () => { - const fn = vi.fn(); - const result = await andThenAsync(err("already failed"), fn); - expect(result).toEqual(err("already failed")); - expect(fn).not.toHaveBeenCalled(); - }); - - it("propagates an async Err from the chained function", async () => { - const result = await andThenAsync(ok("user"), async (_) => - err({ type: "CONFLICT", detail: "duplicate" }), - ); - expect(result).toEqual(err({ type: "CONFLICT", detail: "duplicate" })); - }); + it("chains an async function on an Ok result", async () => { + const result = await andThenAsync(ok(5), (n) => Promise.resolve(ok(n * 2))); + expect(result).toEqual(ok(10)); + }); + + it("short-circuits on an Err result without calling the function", async () => { + let called = false; + const fn = () => { + called = true; + return Promise.resolve(ok(0)); + }; + const result = await andThenAsync(err("already failed"), fn); + expect(result).toEqual(err("already failed")); + expect(called).toBe(false); + }); + + it("propagates an async Err from the chained function", async () => { + const result = await andThenAsync( + ok("user"), + (_) => Promise.resolve(err({ type: "CONFLICT", detail: "duplicate" })), + ); + expect(result).toEqual(err({ type: "CONFLICT", detail: "duplicate" })); + }); }); // ─── match ─────────────────────────────────────────────────────────────────── describe("match", () => { - it("calls ok handler on an Ok result", () => { - const output = match(ok(10), { - ok: (n) => `value is ${n}`, - err: (_) => "error", - }); - expect(output).toBe("value is 10"); - }); - - it("calls err handler on an Err result", () => { - const output = match(err({ type: "NOT_FOUND" }), { - ok: (_) => "found", - err: (e) => `missing: ${e.type}`, - }); - expect(output).toBe("missing: NOT_FOUND"); - }); - - it("allows ok and err handlers to return different types", () => { - // This tests the R1 | R2 type signature — the compiler allows distinct types. - const result = match( - ok(1) as ReturnType> | ReturnType>, - { - ok: (n) => n + 1, - err: (e) => e.toUpperCase(), - }, - ); - // result is number | string - expect(result).toBe(2); - }); - - it("does not call the err handler when result is Ok", () => { - const errHandler = vi.fn(() => "should not run"); - match(ok("success"), { - ok: (v) => v, - err: errHandler, - }); - expect(errHandler).not.toHaveBeenCalled(); - }); - - it("does not call the ok handler when result is Err", () => { - const okHandler = vi.fn(() => "should not run"); - match(err("oops"), { - ok: okHandler, - err: (e) => e, - }); - expect(okHandler).not.toHaveBeenCalled(); - }); + it("calls ok handler on an Ok result", () => { + const output = match(ok(10), { + ok: (n) => `value is ${n}`, + err: (_) => "error", + }); + expect(output).toBe("value is 10"); + }); + + it("calls err handler on an Err result", () => { + const output = match(err({ type: "NOT_FOUND" }), { + ok: (_) => "found", + err: (e) => `missing: ${e.type}`, + }); + expect(output).toBe("missing: NOT_FOUND"); + }); + + it("allows ok and err handlers to return different types", () => { + // This tests the R1 | R2 type signature — the compiler allows distinct types. + const result = match( + ok(1) as ReturnType> | ReturnType>, + { + ok: (n) => n + 1, + err: (e) => e.toUpperCase(), + }, + ); + // result is number | string + expect(result).toBe(2); + }); + + it("does not call the err handler when result is Ok", () => { + let called = false; + const errHandler = () => { + called = true; + return "should not run"; + }; + match(ok("success"), { + ok: (v) => v, + err: errHandler, + }); + expect(called).toBe(false); + }); + + it("does not call the ok handler when result is Err", () => { + let called = false; + const okHandler = () => { + called = true; + return "should not run"; + }; + match(err("oops"), { + ok: okHandler, + err: (e) => e, + }); + expect(called).toBe(false); + }); }); diff --git a/packages/shared/src/__tests__/schemas.test.ts b/packages/shared/src/__tests__/schemas.test.ts index 6cbf7b3..74e52c0 100644 --- a/packages/shared/src/__tests__/schemas.test.ts +++ b/packages/shared/src/__tests__/schemas.test.ts @@ -1,137 +1,137 @@ -import { describe, expect, it } from "vitest"; +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; import { z } from "zod"; import { - apiErrorSchema, - apiSuccessSchema, - idParamSchema, - paginationSchema, -} from "../schemas.js"; + apiErrorSchema, + apiSuccessSchema, + idParamSchema, + paginationSchema, +} from "../schemas.ts"; // ─── paginationSchema ───────────────────────────────────────────────────────── describe("paginationSchema", () => { - it("defaults page to 1 and limit to 20 when not provided", () => { - const result = paginationSchema.parse({}); - expect(result).toEqual({ page: 1, limit: 20 }); - }); - - it("coerces string numbers to integers", () => { - const result = paginationSchema.parse({ page: "3", limit: "50" }); - expect(result).toEqual({ page: 3, limit: 50 }); - }); - - it("rejects a limit greater than 100", () => { - expect(() => paginationSchema.parse({ limit: 101 })).toThrow(); - }); - - it("rejects non-positive page numbers", () => { - expect(() => paginationSchema.parse({ page: 0 })).toThrow(); - expect(() => paginationSchema.parse({ page: -1 })).toThrow(); - }); - - it("accepts limit of exactly 100", () => { - const result = paginationSchema.parse({ limit: 100 }); - expect(result.limit).toBe(100); - }); + it("defaults page to 1 and limit to 20 when not provided", () => { + const result = paginationSchema.parse({}); + expect(result).toEqual({ page: 1, limit: 20 }); + }); + + it("coerces string numbers to integers", () => { + const result = paginationSchema.parse({ page: "3", limit: "50" }); + expect(result).toEqual({ page: 3, limit: 50 }); + }); + + it("rejects a limit greater than 100", () => { + expect(() => paginationSchema.parse({ limit: 101 })).toThrow(); + }); + + it("rejects non-positive page numbers", () => { + expect(() => paginationSchema.parse({ page: 0 })).toThrow(); + expect(() => paginationSchema.parse({ page: -1 })).toThrow(); + }); + + it("accepts limit of exactly 100", () => { + const result = paginationSchema.parse({ limit: 100 }); + expect(result.limit).toBe(100); + }); }); // ─── idParamSchema ──────────────────────────────────────────────────────────── describe("idParamSchema", () => { - it("accepts a valid id string", () => { - const result = idParamSchema.parse({ id: "abc-123" }); - expect(result.id).toBe("abc-123"); - }); - - it("rejects an empty string id", () => { - expect(() => idParamSchema.parse({ id: "" })).toThrow(); - }); - - it("rejects a missing id field", () => { - expect(() => idParamSchema.parse({})).toThrow(); - }); + it("accepts a valid id string", () => { + const result = idParamSchema.parse({ id: "abc-123" }); + expect(result.id).toBe("abc-123"); + }); + + it("rejects an empty string id", () => { + expect(() => idParamSchema.parse({ id: "" })).toThrow(); + }); + + it("rejects a missing id field", () => { + expect(() => idParamSchema.parse({})).toThrow(); + }); }); // ─── apiSuccessSchema ───────────────────────────────────────────────────────── describe("apiSuccessSchema", () => { - it("validates a well-formed success response", () => { - const schema = apiSuccessSchema(z.string()); - const result = schema.parse({ success: true, data: "hello" }); - expect(result).toEqual({ success: true, data: "hello" }); - }); - - it("validates with an object data schema", () => { - const schema = apiSuccessSchema( - z.object({ id: z.string(), name: z.string() }), - ); - const result = schema.parse({ - success: true, - data: { id: "1", name: "Alice" }, - }); - expect(result.data.name).toBe("Alice"); - }); - - it("rejects when success is false", () => { - const schema = apiSuccessSchema(z.string()); - expect(() => schema.parse({ success: false, data: "hello" })).toThrow(); - }); - - it("rejects when data does not match the inner schema", () => { - const schema = apiSuccessSchema(z.number()); - expect(() => - schema.parse({ success: true, data: "not a number" }), - ).toThrow(); - }); - - it("rejects when data is missing", () => { - const schema = apiSuccessSchema(z.string()); - expect(() => schema.parse({ success: true })).toThrow(); - }); + it("validates a well-formed success response", () => { + const schema = apiSuccessSchema(z.string()); + const result = schema.parse({ success: true, data: "hello" }); + expect(result).toEqual({ success: true, data: "hello" }); + }); + + it("validates with an object data schema", () => { + const schema = apiSuccessSchema( + z.object({ id: z.string(), name: z.string() }), + ); + const result = schema.parse({ + success: true, + data: { id: "1", name: "Alice" }, + }); + expect(result.data.name).toBe("Alice"); + }); + + it("rejects when success is false", () => { + const schema = apiSuccessSchema(z.string()); + expect(() => schema.parse({ success: false, data: "hello" })).toThrow(); + }); + + it("rejects when data does not match the inner schema", () => { + const schema = apiSuccessSchema(z.number()); + expect(() => schema.parse({ success: true, data: "not a number" })) + .toThrow(); + }); + + it("rejects when data is missing", () => { + const schema = apiSuccessSchema(z.string()); + expect(() => schema.parse({ success: true })).toThrow(); + }); }); // ─── apiErrorSchema ─────────────────────────────────────────────────────────── describe("apiErrorSchema", () => { - it("validates a well-formed error response", () => { - const result = apiErrorSchema.parse({ - success: false, - error: { code: "NOT_FOUND", message: "Resource not found" }, - }); - expect(result.success).toBe(false); - expect(result.error.code).toBe("NOT_FOUND"); - }); - - it("accepts an optional details field", () => { - const result = apiErrorSchema.parse({ - success: false, - error: { - code: "VALIDATION_ERROR", - message: "Invalid input", - details: { field: "email", issue: "required" }, - }, - }); - expect(result.error.details).toEqual({ field: "email", issue: "required" }); - }); - - it("rejects when success is true", () => { - expect(() => - apiErrorSchema.parse({ - success: true, - error: { code: "OOPS", message: "bad" }, - }), - ).toThrow(); - }); - - it("rejects when code is missing", () => { - expect(() => - apiErrorSchema.parse({ success: false, error: { message: "oops" } }), - ).toThrow(); - }); - - it("rejects when message is missing", () => { - expect(() => - apiErrorSchema.parse({ success: false, error: { code: "ERR" } }), - ).toThrow(); - }); + it("validates a well-formed error response", () => { + const result = apiErrorSchema.parse({ + success: false, + error: { code: "NOT_FOUND", message: "Resource not found" }, + }); + expect(result.success).toBe(false); + expect(result.error.code).toBe("NOT_FOUND"); + }); + + it("accepts an optional details field", () => { + const result = apiErrorSchema.parse({ + success: false, + error: { + code: "VALIDATION_ERROR", + message: "Invalid input", + details: { field: "email", issue: "required" }, + }, + }); + expect(result.error.details).toEqual({ field: "email", issue: "required" }); + }); + + it("rejects when success is true", () => { + expect(() => + apiErrorSchema.parse({ + success: true, + error: { code: "OOPS", message: "bad" }, + }) + ).toThrow(); + }); + + it("rejects when code is missing", () => { + expect(() => + apiErrorSchema.parse({ success: false, error: { message: "oops" } }) + ).toThrow(); + }); + + it("rejects when message is missing", () => { + expect(() => + apiErrorSchema.parse({ success: false, error: { code: "ERR" } }) + ).toThrow(); + }); }); diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 83883cc..22001d3 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,3 +1,3 @@ -export * from "./result.js"; -export * from "./schemas.js"; -export * from "./types.js"; +export * from "./result.ts"; +export * from "./schemas.ts"; +export * from "./types.ts"; diff --git a/packages/shared/src/result.ts b/packages/shared/src/result.ts index 7038c53..6e8b81d 100644 --- a/packages/shared/src/result.ts +++ b/packages/shared/src/result.ts @@ -136,7 +136,7 @@ export const andThen = ( export const andThenAsync = async ( result: Result, fn: (value: T) => Promise>, -): Promise> => (result.ok ? fn(result.value) : result); +): Promise> => (result.ok ? await fn(result.value) : result); /** * Exhaustively handle both branches in a single expression. diff --git a/packages/shared/src/schemas.ts b/packages/shared/src/schemas.ts index 703f02a..bf18e88 100644 --- a/packages/shared/src/schemas.ts +++ b/packages/shared/src/schemas.ts @@ -3,8 +3,8 @@ import { z } from "zod"; // ─── Pagination ────────────────────────────────────────────────────────────── export const paginationSchema = z.object({ - page: z.coerce.number().int().positive().default(1), - limit: z.coerce.number().int().positive().max(100).default(20), + page: z.coerce.number().int().positive().default(1), + limit: z.coerce.number().int().positive().max(100).default(20), }); export type PaginationInput = z.infer; @@ -17,10 +17,10 @@ export type PaginationInput = z.infer; * const rows = await db.query.posts.findMany({ limit, offset }); */ export function paginationQuery(input: PaginationInput) { - return { - limit: input.limit, - offset: (input.page - 1) * input.limit, - }; + return { + limit: input.limit, + offset: (input.page - 1) * input.limit, + }; } /** @@ -35,21 +35,21 @@ export function paginationQuery(input: PaginationInput) { * return c.json(success(paginate(rows, Number(count), input)), OK); */ export function paginate(items: T[], total: number, input: PaginationInput) { - return { - items, - meta: { - page: input.page, - limit: input.limit, - total, - totalPages: Math.ceil(total / input.limit), - hasMore: input.page * input.limit < total, - }, - }; + return { + items, + meta: { + page: input.page, + limit: input.limit, + total, + totalPages: Math.ceil(total / input.limit), + hasMore: input.page * input.limit < total, + }, + }; } // Common params export const idParamSchema = z.object({ - id: z.string().min(1), + id: z.string().min(1), }); // Type exports @@ -70,19 +70,19 @@ export type IdParam = z.infer; // Wraps any data schema in { success: true, data: T }. export const apiSuccessSchema = (dataSchema: T) => - z.object({ - success: z.literal(true), - data: dataSchema, - }); + z.object({ + success: z.literal(true), + data: dataSchema, + }); // Fixed shape for all error responses: { success: false, error: { code, message, details? } }. export const apiErrorSchema = z.object({ - success: z.literal(false), - error: z.object({ - code: z.string(), - message: z.string(), - details: z.record(z.string(), z.unknown()).optional(), - }), + success: z.literal(false), + error: z.object({ + code: z.string(), + message: z.string(), + details: z.record(z.string(), z.unknown()).optional(), + }), }); /** @@ -95,15 +95,15 @@ export const apiErrorSchema = z.object({ * } */ export const paginatedSuccessSchema = (itemSchema: T) => - apiSuccessSchema( - z.object({ - items: z.array(itemSchema), - meta: z.object({ - page: z.number(), - limit: z.number(), - total: z.number(), - totalPages: z.number(), - hasMore: z.boolean(), - }), - }), - ); + apiSuccessSchema( + z.object({ + items: z.array(itemSchema), + meta: z.object({ + page: z.number(), + limit: z.number(), + total: z.number(), + totalPages: z.number(), + hasMore: z.boolean(), + }), + }), + ); diff --git a/packages/shared/src/types.ts b/packages/shared/src/types.ts index 91f8eb6..9561577 100644 --- a/packages/shared/src/types.ts +++ b/packages/shared/src/types.ts @@ -1,16 +1,16 @@ // API Response Types export interface ApiSuccess { - success: true; - data: T; + success: true; + data: T; } export interface ApiError { - success: false; - error: { - code: string; - message: string; - details?: Record; - }; + success: false; + error: { + code: string; + message: string; + details?: Record; + }; } export type ApiResponse = ApiSuccess | ApiError; diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json deleted file mode 100644 index 6fa276e..0000000 --- a/packages/shared/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "node16", - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"] -} diff --git a/packages/shared/vitest.config.ts b/packages/shared/vitest.config.ts deleted file mode 100644 index dab44ae..0000000 --- a/packages/shared/vitest.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - globals: true, - environment: "node", - include: ["src/**/*.test.ts"], - coverage: { - reporter: ["text", "json", "html"], - }, - }, -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index e1a1cd0..0000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,7279 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - zod: - specifier: 4.2.1 - version: 4.2.1 - devDependencies: - '@biomejs/biome': - specifier: 2.3.7 - version: 2.3.7 - '@types/node': - specifier: ^25.0.3 - version: 25.3.0 - tsx: - specifier: ^4.21.0 - version: 4.21.0 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - - apps/backend: - dependencies: - '@asteasolutions/zod-to-openapi': - specifier: 8.0.0 - version: 8.0.0(zod@4.2.1) - '@aws-sdk/client-s3': - specifier: ^3.750.0 - version: 3.994.0 - '@aws-sdk/s3-request-presigner': - specifier: ^3.750.0 - version: 3.994.0 - '@axiomhq/pino': - specifier: ^1.4.0 - version: 1.4.0 - '@hono/node-server': - specifier: ^1.19.7 - version: 1.19.9(hono@4.12.0) - '@hono/node-ws': - specifier: ^1.2.0 - version: 1.3.0(@hono/node-server@1.19.9(hono@4.12.0))(hono@4.12.0) - '@hono/swagger-ui': - specifier: ^0.5.3 - version: 0.5.3(hono@4.12.0) - '@hono/zod-openapi': - specifier: ^1.2.0 - version: 1.2.2(hono@4.12.0)(zod@4.2.1) - '@hono/zod-validator': - specifier: ^0.7.6 - version: 0.7.6(hono@4.12.0)(zod@4.2.1) - '@repo/db': - specifier: workspace:* - version: link:../../packages/db - '@repo/email-templates': - specifier: workspace:* - version: link:../../packages/email-templates - '@repo/shared': - specifier: workspace:* - version: link:../../packages/shared - '@scalar/hono-api-reference': - specifier: ^0.9.30 - version: 0.9.44(hono@4.12.0) - better-auth: - specifier: ^1.4.9 - version: 1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18) - bullmq: - specifier: ^5.34.0 - version: 5.69.3 - dotenv: - specifier: ^16.6.1 - version: 16.6.1 - dotenv-expand: - specifier: ^12.0.3 - version: 12.0.3 - drizzle-orm: - specifier: ^0.44.7 - version: 0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8) - drizzle-zod: - specifier: 0.8.3 - version: 0.8.3(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(zod@4.2.1) - hono: - specifier: ^4.11.3 - version: 4.12.0 - hono-pino: - specifier: ^0.7.2 - version: 0.7.2(hono@4.12.0)(pino@9.14.0) - ioredis: - specifier: ^5.4.1 - version: 5.9.3 - neverthrow: - specifier: ^8.2.0 - version: 8.2.0 - pg: - specifier: ^8.16.3 - version: 8.18.0 - pino: - specifier: ^9.14.0 - version: 9.14.0 - pino-pretty: - specifier: ^13.1.3 - version: 13.1.3 - postgres: - specifier: ^3.4.7 - version: 3.4.8 - resend: - specifier: ^6.6.0 - version: 6.9.2 - stoker: - specifier: 2.0.1 - version: 2.0.1(@asteasolutions/zod-to-openapi@8.0.0(zod@4.2.1))(@hono/zod-openapi@1.2.2(hono@4.12.0)(zod@4.2.1))(hono@4.12.0)(openapi3-ts@4.5.0) - zod: - specifier: ^4.2.1 - version: 4.2.1 - devDependencies: - '@biomejs/biome': - specifier: 2.3.7 - version: 2.3.7 - '@swc/cli': - specifier: ^0.7.9 - version: 0.7.10(@swc/core@1.15.11) - '@swc/core': - specifier: ^1.15.8 - version: 1.15.11 - '@types/node': - specifier: ^22.19.3 - version: 22.19.11 - '@types/pg': - specifier: ^8.16.0 - version: 8.16.0 - '@vitest/ui': - specifier: ^4.0.16 - version: 4.0.18(vitest@4.0.18) - cross-env: - specifier: ^7.0.3 - version: 7.0.3 - drizzle-kit: - specifier: ^0.31.8 - version: 0.31.9 - tsc-alias: - specifier: ^1.8.16 - version: 1.8.16 - tsx: - specifier: ^4.21.0 - version: 4.21.0 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - vitest: - specifier: ^4.0.16 - version: 4.0.18(@types/node@22.19.11)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - apps/frontend: - dependencies: - '@base-ui/react': - specifier: ^1.2.0 - version: 1.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@better-fetch/fetch': - specifier: ^1.1.18 - version: 1.1.21 - '@repo/db': - specifier: workspace:* - version: link:../../packages/db - '@repo/shared': - specifier: workspace:* - version: link:../../packages/shared - '@tailwindcss/vite': - specifier: ^4.1.17 - version: 4.2.0(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@tanstack/react-form': - specifier: ^1.0.0 - version: 1.28.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/react-query': - specifier: ^5.90.7 - version: 5.90.21(react@19.2.4) - '@tanstack/react-router': - specifier: ^1.134.13 - version: 1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/react-router-devtools': - specifier: ^1.134.13 - version: 1.161.1(@tanstack/react-router@1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.161.1)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - better-auth: - specifier: ^1.3.34 - version: 1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.553.0 - version: 0.553.0(react@19.2.4) - react: - specifier: ^19.1.1 - version: 19.2.4 - react-dom: - specifier: ^19.1.1 - version: 19.2.4(react@19.2.4) - sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - tailwind-merge: - specifier: ^3.3.1 - version: 3.5.0 - tailwindcss: - specifier: ^4.1.17 - version: 4.2.0 - vite-tsconfig-paths: - specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - zod: - specifier: ^4.2.1 - version: 4.2.1 - zustand: - specifier: ^5.0.8 - version: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) - devDependencies: - '@tanstack/router-plugin': - specifier: ^1.134.14 - version: 1.161.1(@tanstack/react-router@1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@types/react': - specifier: ^19.1.16 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.1.9 - version: 19.2.3(@types/react@19.2.14) - '@vitejs/plugin-react': - specifier: ^5.0.4 - version: 5.1.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - typescript: - specifier: ~5.9.3 - version: 5.9.3 - vite: - specifier: ^7.1.7 - version: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - packages/db: - dependencies: - dotenv: - specifier: ^16.6.1 - version: 16.6.1 - drizzle-orm: - specifier: ^0.44.7 - version: 0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8) - drizzle-zod: - specifier: 0.8.3 - version: 0.8.3(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(zod@4.2.1) - zod: - specifier: ^4.2.1 - version: 4.2.1 - devDependencies: - '@types/node': - specifier: ^22.19.3 - version: 22.19.11 - drizzle-kit: - specifier: ^0.31.8 - version: 0.31.9 - postgres: - specifier: ^3.4.7 - version: 3.4.8 - rimraf: - specifier: ^6.1.2 - version: 6.1.3 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - - packages/email-templates: - dependencies: - rimraf: - specifier: ^6.1.2 - version: 6.1.3 - devDependencies: - '@types/node': - specifier: ^22.19.3 - version: 22.19.11 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - vitest: - specifier: ^4.0.16 - version: 4.0.18(@types/node@22.19.11)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - packages/shared: - dependencies: - zod: - specifier: ^4.2.1 - version: 4.2.1 - devDependencies: - rimraf: - specifier: ^6.1.2 - version: 6.1.3 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - vitest: - specifier: ^4.0.16 - version: 4.0.18(@types/node@25.3.0)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - -packages: - - '@asteasolutions/zod-to-openapi@8.0.0': - resolution: {integrity: sha512-C56hBPiraeSWUNLz8mB5Z0/0LdfaFD5d6WB/+hdUg0MiC7egTgvWRGh3M3jZ3CRl03l/NJWnmv5D3OUAz+JGeg==} - peerDependencies: - zod: ^4.0.0 - - '@asteasolutions/zod-to-openapi@8.4.1': - resolution: {integrity: sha512-WmJUsFINbnWxGvHSd16aOjgKf+5GsfdxruO2YDLcgplsidakCauik1lhlk83YDH06265Yd1XtUyF24o09uygpw==} - peerDependencies: - zod: ^4.0.0 - - '@aws-crypto/crc32@5.2.0': - resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} - engines: {node: '>=16.0.0'} - - '@aws-crypto/crc32c@5.2.0': - resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - - '@aws-crypto/sha1-browser@5.2.0': - resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - - '@aws-crypto/sha256-browser@5.2.0': - resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - - '@aws-crypto/sha256-js@5.2.0': - resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} - engines: {node: '>=16.0.0'} - - '@aws-crypto/supports-web-crypto@5.2.0': - resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - - '@aws-crypto/util@5.2.0': - resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - - '@aws-sdk/client-s3@3.994.0': - resolution: {integrity: sha512-zIVQt/XfE2zTFrcPEf8R+KRaRD1++XHMPRhxXM2kVA6NA6Aq/cFCUyYOYYwSbWLF/XeToaX1auYGn3IoZKruPQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/client-sso@3.993.0': - resolution: {integrity: sha512-VLUN+wIeNX24fg12SCbzTUBnBENlL014yMKZvRhPkcn4wHR6LKgNrjsG3fZ03Xs0XoKaGtNFi1VVrq666sGBoQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/core@3.973.11': - resolution: {integrity: sha512-wdQ8vrvHkKIV7yNUKXyjPWKCdYEUrZTHJ8Ojd5uJxXp9vqPCkUR1dpi1NtOLcrDgueJH7MUH5lQZxshjFPSbDA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/crc64-nvme@3.972.0': - resolution: {integrity: sha512-ThlLhTqX68jvoIVv+pryOdb5coP1cX1/MaTbB9xkGDCbWbsqQcLqzPxuSoW1DCnAAIacmXCWpzUNOB9pv+xXQw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-env@3.972.9': - resolution: {integrity: sha512-ZptrOwQynfupubvcngLkbdIq/aXvl/czdpEG8XJ8mN8Nb19BR0jaK0bR+tfuMU36Ez9q4xv7GGkHFqEEP2hUUQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-http@3.972.11': - resolution: {integrity: sha512-hECWoOoH386bGr89NQc9vA/abkGf5TJrMREt+lhNcnSNmoBS04fK7vc3LrJBSQAUGGVj0Tz3f4dHB3w5veovig==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-ini@3.972.9': - resolution: {integrity: sha512-zr1csEu9n4eDiHMTYJabX1mDGuGLgjgUnNckIivvk43DocJC9/f6DefFrnUPZXE+GHtbW50YuXb+JIxKykU74A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-login@3.972.9': - resolution: {integrity: sha512-m4RIpVgZChv0vWS/HKChg1xLgZPpx8Z+ly9Fv7FwA8SOfuC6I3htcSaBz2Ch4bneRIiBUhwP4ziUo0UZgtJStQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-node@3.972.10': - resolution: {integrity: sha512-70nCESlvnzjo4LjJ8By8MYIiBogkYPSXl3WmMZfH9RZcB/Nt9qVWbFpYj6Fk1vLa4Vk8qagFVeXgxdieMxG1QA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-process@3.972.9': - resolution: {integrity: sha512-gOWl0Fe2gETj5Bk151+LYKpeGi2lBDLNu+NMNpHRlIrKHdBmVun8/AalwMK8ci4uRfG5a3/+zvZBMpuen1SZ0A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-sso@3.972.9': - resolution: {integrity: sha512-ey7S686foGTArvFhi3ifQXmgptKYvLSGE2250BAQceMSXZddz7sUSNERGJT2S7u5KIe/kgugxrt01hntXVln6w==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-web-identity@3.972.9': - resolution: {integrity: sha512-8LnfS76nHXoEc9aRRiMMpxZxJeDG0yusdyo3NvPhCgESmBUgpMa4luhGbClW5NoX/qRcGxxM6Z/esqANSNMTow==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-bucket-endpoint@3.972.3': - resolution: {integrity: sha512-fmbgWYirF67YF1GfD7cg5N6HHQ96EyRNx/rDIrTF277/zTWVuPI2qS/ZHgofwR1NZPe/NWvoppflQY01LrbVLg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-expect-continue@3.972.3': - resolution: {integrity: sha512-4msC33RZsXQpUKR5QR4HnvBSNCPLGHmB55oDiROqqgyOc+TOfVu2xgi5goA7ms6MdZLeEh2905UfWMnMMF4mRg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-flexible-checksums@3.972.9': - resolution: {integrity: sha512-E663+r/UQpvF3aJkD40p5ZANVQFsUcbE39jifMtN7wc0t1M0+2gJJp3i75R49aY9OiSX5lfVyPUNjN/BNRCCZA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-host-header@3.972.3': - resolution: {integrity: sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-location-constraint@3.972.3': - resolution: {integrity: sha512-nIg64CVrsXp67vbK0U1/Is8rik3huS3QkRHn2DRDx4NldrEFMgdkZGI/+cZMKD9k4YOS110Dfu21KZLHrFA/1g==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-logger@3.972.3': - resolution: {integrity: sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-recursion-detection@3.972.3': - resolution: {integrity: sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.972.11': - resolution: {integrity: sha512-Qr0T7ZQTRMOuR6ahxEoJR1thPVovfWrKB2a6KBGR+a8/ELrFodrgHwhq50n+5VMaGuLtGhHiISU3XGsZmtmVXQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-ssec@3.972.3': - resolution: {integrity: sha512-dU6kDuULN3o3jEHcjm0c4zWJlY1zWVkjG9NPe9qxYLLpcbdj5kRYBS2DdWYD+1B9f910DezRuws7xDEqKkHQIg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-user-agent@3.972.11': - resolution: {integrity: sha512-R8CvPsPHXwzIHCAza+bllY6PrctEk4lYq/SkHJz9NLoBHCcKQrbOcsfXxO6xmipSbUNIbNIUhH0lBsJGgsRdiw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/nested-clients@3.993.0': - resolution: {integrity: sha512-iOq86f2H67924kQUIPOAvlmMaOAvOLoDOIb66I2YqSUpMYB6ufiuJW3RlREgskxv86S5qKzMnfy/X6CqMjK6XQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/region-config-resolver@3.972.3': - resolution: {integrity: sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/s3-request-presigner@3.994.0': - resolution: {integrity: sha512-g/jYc++IunLJZpeyLJrbC39XAf57BYjWjKkxRQlwj5fC90Scg4t/2FS0BV2u/U9UZX5HqpQQahI60tPEN84JqQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.994.0': - resolution: {integrity: sha512-8y04Lv497KKd7f2TVlm2RaKQaNfnY17ZH8d3m+7sW/3R3BhZvHgWQZyqTb/vcN2ERz1YAnWx6woJyB3ZNFvakw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.993.0': - resolution: {integrity: sha512-+35g4c+8r7sB9Sjp1KPdM8qxGn6B/shBjJtEUN4e+Edw9UEQlZKIzioOGu3UAbyE0a/s450LdLZr4wbJChtmww==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/types@3.973.1': - resolution: {integrity: sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-arn-parser@3.972.2': - resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-endpoints@3.993.0': - resolution: {integrity: sha512-j6vioBeRZ4eHX4SWGvGPpwGg/xSOcK7f1GL0VM+rdf3ZFTIsUEhCFmD78B+5r2PgztcECSzEfvHQX01k8dPQPw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-endpoints@3.994.0': - resolution: {integrity: sha512-L2obUBw4ACMMd1F/SG5LdfPyZ0xJNs9Maifwr3w0uWO+4YvHmk9FfRskfSfE/SLZ9S387oSZ+1xiP7BfVCP/Og==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-format-url@3.972.3': - resolution: {integrity: sha512-n7F2ycckcKFXa01vAsT/SJdjFHfKH9s96QHcs5gn8AaaigASICeME8WdUL9uBp8XV/OVwEt8+6gzn6KFUgQa8g==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-locate-window@3.965.4': - resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/util-user-agent-browser@3.972.3': - resolution: {integrity: sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==} - - '@aws-sdk/util-user-agent-node@3.972.9': - resolution: {integrity: sha512-JNswdsLdQemxqaSIBL2HRhsHPUBBziAgoi5RQv6/9avmE5g5RSdt1hWr3mHJ7OxqRYf+KeB11ExWbiqfrnoeaA==} - engines: {node: '>=20.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - - '@aws-sdk/xml-builder@3.972.5': - resolution: {integrity: sha512-mCae5Ys6Qm1LDu0qdGwx2UQ63ONUe+FHw908fJzLDqFKTDBK4LDZUqKWm4OkTCNFq19bftjsBSESIGLD/s3/rA==} - engines: {node: '>=20.0.0'} - - '@aws/lambda-invoke-store@0.2.3': - resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} - engines: {node: '>=18.0.0'} - - '@axiomhq/js@1.4.0': - resolution: {integrity: sha512-wC5x1ud/QJMstrjpicATkyY8+ZVWEl4WlXMtA5EZf7hkj0+b191yv4yynLxLEfr/MveXora9m6CWdJq4DsbcAg==} - engines: {node: '>=20'} - - '@axiomhq/pino@1.4.0': - resolution: {integrity: sha512-7ujZM1kqbA98BWl8ltWdTLSDE7+67nOvR30/hm8P6omISjaQV0xgtdtZtDGe/BH2VmZeQWPGfAI1FnUNYC3CtQ==} - engines: {node: '>=20'} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - - '@base-ui/react@1.2.0': - resolution: {integrity: sha512-O6aEQHcm+QyGTFY28xuwRD3SEJGZOBDpyjN2WvpfWYFVhg+3zfXPysAILqtM0C1kWC82MccOE/v1j+GHXE4qIw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - - '@base-ui/utils@0.2.5': - resolution: {integrity: sha512-oYC7w0gp76RI5MxprlGLV0wze0SErZaRl3AAkeP3OnNB/UBMb6RqNf6ZSIlxOc9Qp68Ab3C2VOcJQyRs7Xc7Vw==} - peerDependencies: - '@types/react': ^17 || ^18 || ^19 - react: ^17 || ^18 || ^19 - react-dom: ^17 || ^18 || ^19 - peerDependenciesMeta: - '@types/react': - optional: true - - '@better-auth/core@1.4.18': - resolution: {integrity: sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg==} - peerDependencies: - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - better-call: 1.1.8 - jose: ^6.1.0 - kysely: ^0.28.5 - nanostores: ^1.0.1 - - '@better-auth/telemetry@1.4.18': - resolution: {integrity: sha512-e5rDF8S4j3Um/0LIVATL2in9dL4lfO2fr2v1Wio4qTMRbfxqnUDTa+6SZtwdeJrbc4O+a3c+IyIpjG9Q/6GpfQ==} - peerDependencies: - '@better-auth/core': 1.4.18 - - '@better-auth/utils@0.3.0': - resolution: {integrity: sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==} - - '@better-fetch/fetch@1.1.21': - resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} - - '@biomejs/biome@2.3.7': - resolution: {integrity: sha512-CTbAS/jNAiUc6rcq94BrTB8z83O9+BsgWj2sBCQg9rD6Wkh2gjfR87usjx0Ncx0zGXP1NKgT7JNglay5Zfs9jw==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.3.7': - resolution: {integrity: sha512-LirkamEwzIUULhXcf2D5b+NatXKeqhOwilM+5eRkbrnr6daKz9rsBL0kNZ16Hcy4b8RFq22SG4tcLwM+yx/wFA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.3.7': - resolution: {integrity: sha512-Q4TO633kvrMQkKIV7wmf8HXwF0dhdTD9S458LGE24TYgBjSRbuhvio4D5eOQzirEYg6eqxfs53ga/rbdd8nBKg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.3.7': - resolution: {integrity: sha512-/afy8lto4CB8scWfMdt+NoCZtatBUF62Tk3ilWH2w8ENd5spLhM77zKlFZEvsKJv9AFNHknMl03zO67CiklL2Q==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-arm64@2.3.7': - resolution: {integrity: sha512-inHOTdlstUBzgjDcx0ge71U4SVTbwAljmkfi3MC5WzsYCRhancqfeL+sa4Ke6v2ND53WIwCFD5hGsYExoI3EZQ==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-x64-musl@2.3.7': - resolution: {integrity: sha512-CQUtgH1tIN6e5wiYSJqzSwJumHYolNtaj1dwZGCnZXm2PZU1jOJof9TsyiP3bXNDb+VOR7oo7ZvY01If0W3iFQ==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-linux-x64@2.3.7': - resolution: {integrity: sha512-fJMc3ZEuo/NaMYo5rvoWjdSS5/uVSW+HPRQujucpZqm2ZCq71b8MKJ9U4th9yrv2L5+5NjPF0nqqILCl8HY/fg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-win32-arm64@2.3.7': - resolution: {integrity: sha512-aJAE8eCNyRpcfx2JJAtsPtISnELJ0H4xVVSwnxm13bzI8RwbXMyVtxy2r5DV1xT3WiSP+7LxORcApWw0LM8HiA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.3.7': - resolution: {integrity: sha512-pulzUshqv9Ed//MiE8MOUeeEkbkSHVDVY5Cz5wVAnH1DUqliCQG3j6s1POaITTFqFfo7AVIx2sWdKpx/GS+Nqw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} - - '@drizzle-team/brocli@0.10.2': - resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - - '@esbuild-kit/core-utils@3.3.2': - resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild-kit/esm-loader@2.6.5': - resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@floating-ui/core@1.7.4': - resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} - - '@floating-ui/dom@1.7.5': - resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} - - '@floating-ui/react-dom@2.1.7': - resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - - '@hono/node-ws@1.3.0': - resolution: {integrity: sha512-ju25YbbvLuXdqBCmLZLqnNYu1nbHIQjoyUqA8ApZOeL1k4skuiTcw5SW77/5SUYo2Xi2NVBJoVlfQurnKEp03Q==} - engines: {node: '>=18.14.1'} - peerDependencies: - '@hono/node-server': ^1.19.2 - hono: ^4.6.0 - - '@hono/swagger-ui@0.5.3': - resolution: {integrity: sha512-Hn90DOOJ62ICJQplQvCDVpi9Jcn6EhtRaiffyJIS53wA5RmRLtMCDQGVc0bor8vQD7JIwpkweWjs+3cycp+IvA==} - peerDependencies: - hono: '>=4.0.0' - - '@hono/zod-openapi@1.2.2': - resolution: {integrity: sha512-va6vsL23wCJ1d0Vd+vGL1XOt+wPwItxirYafuhlW9iC2MstYr2FvsI7mctb45eBTjZfkqB/3LYDJEppPjOEiHw==} - engines: {node: '>=16.0.0'} - peerDependencies: - hono: '>=4.3.6' - zod: ^4.0.0 - - '@hono/zod-validator@0.7.6': - resolution: {integrity: sha512-Io1B6d011Gj1KknV4rXYz4le5+5EubcWEU/speUjuw9XMMIaP3n78yXLhjd2A3PXaXaUwEAluOiAyLqhBEJgsw==} - peerDependencies: - hono: '>=3.9.0' - zod: ^3.25.0 || ^4.0.0 - - '@ioredis/commands@1.5.0': - resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} - cpu: [x64] - os: [win32] - - '@napi-rs/nice-android-arm-eabi@1.1.1': - resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} - engines: {node: '>= 10'} - cpu: [arm] - os: [android] - - '@napi-rs/nice-android-arm64@1.1.1': - resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - - '@napi-rs/nice-darwin-arm64@1.1.1': - resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@napi-rs/nice-darwin-x64@1.1.1': - resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@napi-rs/nice-freebsd-x64@1.1.1': - resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - - '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': - resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@napi-rs/nice-linux-arm64-gnu@1.1.1': - resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/nice-linux-arm64-musl@1.1.1': - resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@napi-rs/nice-linux-ppc64-gnu@1.1.1': - resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} - engines: {node: '>= 10'} - cpu: [ppc64] - os: [linux] - - '@napi-rs/nice-linux-riscv64-gnu@1.1.1': - resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} - engines: {node: '>= 10'} - cpu: [riscv64] - os: [linux] - - '@napi-rs/nice-linux-s390x-gnu@1.1.1': - resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} - engines: {node: '>= 10'} - cpu: [s390x] - os: [linux] - - '@napi-rs/nice-linux-x64-gnu@1.1.1': - resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/nice-linux-x64-musl@1.1.1': - resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@napi-rs/nice-openharmony-arm64@1.1.1': - resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [openharmony] - - '@napi-rs/nice-win32-arm64-msvc@1.1.1': - resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@napi-rs/nice-win32-ia32-msvc@1.1.1': - resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@napi-rs/nice-win32-x64-msvc@1.1.1': - resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@napi-rs/nice@1.1.1': - resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} - engines: {node: '>= 10'} - - '@noble/ciphers@2.1.1': - resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} - engines: {node: '>= 20.19.0'} - - '@noble/hashes@2.0.1': - resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} - engines: {node: '>= 20.19.0'} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - - '@rolldown/pluginutils@1.0.0-rc.3': - resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} - - '@rollup/rollup-android-arm-eabi@4.57.1': - resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.57.1': - resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.57.1': - resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.57.1': - resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.57.1': - resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.57.1': - resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': - resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.57.1': - resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.57.1': - resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.57.1': - resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loong64-gnu@4.57.1': - resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-loong64-musl@4.57.1': - resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.57.1': - resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-ppc64-musl@4.57.1': - resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.57.1': - resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.57.1': - resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.57.1': - resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.57.1': - resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.57.1': - resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-openbsd-x64@4.57.1': - resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.57.1': - resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.57.1': - resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.57.1': - resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.57.1': - resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.57.1': - resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} - cpu: [x64] - os: [win32] - - '@scalar/core@0.3.41': - resolution: {integrity: sha512-IPgiHOSGBDfBcJELbev0lo+ZHc8Q/vA82neX0Ax1iHNGtf/munH+qN5I+p9rGRS60IRQAwABlUo31Y3Gw1c0EA==} - engines: {node: '>=20'} - - '@scalar/helpers@0.2.15': - resolution: {integrity: sha512-hMHXejGFVOS4HwCo7C2qddChuvMJs3sEOALo7gNOvwLS4dGLrW8flbSglDki4ttyremlKQstP5WJuPxmHQU3sA==} - engines: {node: '>=20'} - - '@scalar/hono-api-reference@0.9.44': - resolution: {integrity: sha512-NusQ3S/LYKmEMOwc5kbKi6Can1b0iHTL/145CxfT5klEXtazhzdiXtYNiPNS99vGYKjub7+oaFR/HXJz4y/w2w==} - engines: {node: '>=20'} - peerDependencies: - hono: ^4.11.5 - - '@scalar/types@0.6.6': - resolution: {integrity: sha512-nr3m23p5MnGy4Wb4JFT7aA+jzvYSs/AS40NUEoQMBE1IwtuvG5gtLL0uu6kWpDq4UAfrWGntlAQNX7G8X9D4sg==} - engines: {node: '>=20'} - - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - - '@smithy/abort-controller@4.2.8': - resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} - engines: {node: '>=18.0.0'} - - '@smithy/chunked-blob-reader-native@4.2.1': - resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} - engines: {node: '>=18.0.0'} - - '@smithy/chunked-blob-reader@5.2.0': - resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} - engines: {node: '>=18.0.0'} - - '@smithy/config-resolver@4.4.6': - resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} - engines: {node: '>=18.0.0'} - - '@smithy/core@3.23.2': - resolution: {integrity: sha512-HaaH4VbGie4t0+9nY3tNBRSxVTr96wzIqexUa6C2qx3MPePAuz7lIxPxYtt1Wc//SPfJLNoZJzfdt0B6ksj2jA==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.2.8': - resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-codec@4.2.8': - resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-browser@4.2.8': - resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-config-resolver@4.3.8': - resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-node@4.2.8': - resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-universal@4.2.8': - resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.3.9': - resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-blob-browser@4.2.9': - resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-node@4.2.8': - resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-stream-node@4.2.8': - resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} - engines: {node: '>=18.0.0'} - - '@smithy/invalid-dependency@4.2.8': - resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==} - engines: {node: '>=18.0.0'} - - '@smithy/is-array-buffer@2.2.0': - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} - - '@smithy/is-array-buffer@4.2.0': - resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} - engines: {node: '>=18.0.0'} - - '@smithy/md5-js@4.2.8': - resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-content-length@4.2.8': - resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-endpoint@4.4.16': - resolution: {integrity: sha512-L5GICFCSsNhbJ5JSKeWFGFy16Q2OhoBizb3X2DrxaJwXSEujVvjG9Jt386dpQn2t7jINglQl0b4K/Su69BdbMA==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-retry@4.4.33': - resolution: {integrity: sha512-jLqZOdJhtIL4lnA9hXnAG6GgnJlo1sD3FqsTxm9wSfjviqgWesY/TMBVnT84yr4O0Vfe0jWoXlfFbzsBVph3WA==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-serde@4.2.9': - resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-stack@4.2.8': - resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} - engines: {node: '>=18.0.0'} - - '@smithy/node-config-provider@4.3.8': - resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.4.10': - resolution: {integrity: sha512-u4YeUwOWRZaHbWaebvrs3UhwQwj+2VNmcVCwXcYTvPIuVyM7Ex1ftAj+fdbG/P4AkBwLq/+SKn+ydOI4ZJE9PA==} - engines: {node: '>=18.0.0'} - - '@smithy/property-provider@4.2.8': - resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} - engines: {node: '>=18.0.0'} - - '@smithy/protocol-http@5.3.8': - resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-builder@4.2.8': - resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-parser@4.2.8': - resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} - engines: {node: '>=18.0.0'} - - '@smithy/service-error-classification@4.2.8': - resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} - engines: {node: '>=18.0.0'} - - '@smithy/shared-ini-file-loader@4.4.3': - resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.3.8': - resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} - engines: {node: '>=18.0.0'} - - '@smithy/smithy-client@4.11.5': - resolution: {integrity: sha512-xixwBRqoeP2IUgcAl3U9dvJXc+qJum4lzo3maaJxifsZxKUYLfVfCXvhT4/jD01sRrHg5zjd1cw2Zmjr4/SuKQ==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.12.0': - resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} - engines: {node: '>=18.0.0'} - - '@smithy/url-parser@4.2.8': - resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-base64@4.3.0': - resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-browser@4.2.0': - resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-node@4.2.1': - resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-buffer-from@2.2.0': - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} - - '@smithy/util-buffer-from@4.2.0': - resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} - engines: {node: '>=18.0.0'} - - '@smithy/util-config-provider@4.2.0': - resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-browser@4.3.32': - resolution: {integrity: sha512-092sjYfFMQ/iaPH798LY/OJFBcYu0sSK34Oy9vdixhsU36zlZu8OcYjF3TD4e2ARupyK7xaxPXl+T0VIJTEkkg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-node@4.2.35': - resolution: {integrity: sha512-miz/ggz87M8VuM29y7jJZMYkn7+IErM5p5UgKIf8OtqVs/h2bXr1Bt3uTsREsI/4nK8a0PQERbAPsVPVNIsG7Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-endpoints@3.2.8': - resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-hex-encoding@4.2.0': - resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-middleware@4.2.8': - resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} - engines: {node: '>=18.0.0'} - - '@smithy/util-retry@4.2.8': - resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-stream@4.5.12': - resolution: {integrity: sha512-D8tgkrmhAX/UNeCZbqbEO3uqyghUnEmmoO9YEvRuwxjlkKKUE7FOgCJnqpTlQPe9MApdWPky58mNQQHbnCzoNg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-uri-escape@4.2.0': - resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-utf8@2.3.0': - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} - - '@smithy/util-utf8@4.2.0': - resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-waiter@4.2.8': - resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==} - engines: {node: '>=18.0.0'} - - '@smithy/uuid@1.1.0': - resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} - engines: {node: '>=18.0.0'} - - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@swc/cli@0.7.10': - resolution: {integrity: sha512-QQ36Q1VwGTT2YzvMeNe/j1x4DKS277DscNhWc57dIwQn//C+zAgvuSupMB/XkmYqPKQX+8hjn5/cHRJrMvWy0Q==} - engines: {node: '>= 16.14.0'} - hasBin: true - peerDependencies: - '@swc/core': ^1.2.66 - chokidar: ^4.0.1 - peerDependenciesMeta: - chokidar: - optional: true - - '@swc/core-darwin-arm64@1.15.11': - resolution: {integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - - '@swc/core-darwin-x64@1.15.11': - resolution: {integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - - '@swc/core-linux-arm-gnueabihf@1.15.11': - resolution: {integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - - '@swc/core-linux-arm64-gnu@1.15.11': - resolution: {integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - - '@swc/core-linux-arm64-musl@1.15.11': - resolution: {integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - - '@swc/core-linux-x64-gnu@1.15.11': - resolution: {integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - - '@swc/core-linux-x64-musl@1.15.11': - resolution: {integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - - '@swc/core-win32-arm64-msvc@1.15.11': - resolution: {integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - - '@swc/core-win32-ia32-msvc@1.15.11': - resolution: {integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - - '@swc/core-win32-x64-msvc@1.15.11': - resolution: {integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - - '@swc/core@1.15.11': - resolution: {integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==} - engines: {node: '>=10'} - peerDependencies: - '@swc/helpers': '>=0.5.17' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - - '@swc/types@0.1.25': - resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} - - '@tailwindcss/node@4.2.0': - resolution: {integrity: sha512-Yv+fn/o2OmL5fh/Ir62VXItdShnUxfpkMA4Y7jdeC8O81WPB8Kf6TT6GSHvnqgSwDzlB5iT7kDpeXxLsUS0T6Q==} - - '@tailwindcss/oxide-android-arm64@4.2.0': - resolution: {integrity: sha512-F0QkHAVaW/JNBWl4CEKWdZ9PMb0khw5DCELAOnu+RtjAfx5Zgw+gqCHFvqg3AirU1IAd181fwOtJQ5I8Yx5wtw==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [android] - - '@tailwindcss/oxide-darwin-arm64@4.2.0': - resolution: {integrity: sha512-I0QylkXsBsJMZ4nkUNSR04p6+UptjcwhcVo3Zu828ikiEqHjVmQL9RuQ6uT/cVIiKpvtVA25msu/eRV97JeNSA==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [darwin] - - '@tailwindcss/oxide-darwin-x64@4.2.0': - resolution: {integrity: sha512-6TmQIn4p09PBrmnkvbYQ0wbZhLtbaksCDx7Y7R3FYYx0yxNA7xg5KP7dowmQ3d2JVdabIHvs3Hx4K3d5uCf8xg==} - engines: {node: '>= 20'} - cpu: [x64] - os: [darwin] - - '@tailwindcss/oxide-freebsd-x64@4.2.0': - resolution: {integrity: sha512-qBudxDvAa2QwGlq9y7VIzhTvp2mLJ6nD/G8/tI70DCDoneaUeLWBJaPcbfzqRIWraj+o969aDQKvKW9dvkUizw==} - engines: {node: '>= 20'} - cpu: [x64] - os: [freebsd] - - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0': - resolution: {integrity: sha512-7XKkitpy5NIjFZNUQPeUyNJNJn1CJeV7rmMR+exHfTuOsg8rxIO9eNV5TSEnqRcaOK77zQpsyUkBWmPy8FgdSg==} - engines: {node: '>= 20'} - cpu: [arm] - os: [linux] - - '@tailwindcss/oxide-linux-arm64-gnu@4.2.0': - resolution: {integrity: sha512-Mff5a5Q3WoQR01pGU1gr29hHM1N93xYrKkGXfPw/aRtK4bOc331Ho4Tgfsm5WDGvpevqMpdlkCojT3qlCQbCpA==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - - '@tailwindcss/oxide-linux-arm64-musl@4.2.0': - resolution: {integrity: sha512-XKcSStleEVnbH6W/9DHzZv1YhjE4eSS6zOu2eRtYAIh7aV4o3vIBs+t/B15xlqoxt6ef/0uiqJVB6hkHjWD/0A==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - - '@tailwindcss/oxide-linux-x64-gnu@4.2.0': - resolution: {integrity: sha512-/hlXCBqn9K6fi7eAM0RsobHwJYa5V/xzWspVTzxnX+Ft9v6n+30Pz8+RxCn7sQL/vRHHLS30iQPrHQunu6/vJA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - - '@tailwindcss/oxide-linux-x64-musl@4.2.0': - resolution: {integrity: sha512-lKUaygq4G7sWkhQbfdRRBkaq4LY39IriqBQ+Gk6l5nKq6Ay2M2ZZb1tlIyRNgZKS8cbErTwuYSor0IIULC0SHw==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - - '@tailwindcss/oxide-wasm32-wasi@4.2.0': - resolution: {integrity: sha512-xuDjhAsFdUuFP5W9Ze4k/o4AskUtI8bcAGU4puTYprr89QaYFmhYOPfP+d1pH+k9ets6RoE23BXZM1X1jJqoyw==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - bundledDependencies: - - '@napi-rs/wasm-runtime' - - '@emnapi/core' - - '@emnapi/runtime' - - '@tybys/wasm-util' - - '@emnapi/wasi-threads' - - tslib - - '@tailwindcss/oxide-win32-arm64-msvc@4.2.0': - resolution: {integrity: sha512-2UU/15y1sWDEDNJXxEIrfWKC2Yb4YgIW5Xz2fKFqGzFWfoMHWFlfa1EJlGO2Xzjkq/tvSarh9ZTjvbxqWvLLXA==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [win32] - - '@tailwindcss/oxide-win32-x64-msvc@4.2.0': - resolution: {integrity: sha512-CrFadmFoc+z76EV6LPG1jx6XceDsaCG3lFhyLNo/bV9ByPrE+FnBPckXQVP4XRkN76h3Fjt/a+5Er/oA/nCBvQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [win32] - - '@tailwindcss/oxide@4.2.0': - resolution: {integrity: sha512-AZqQzADaj742oqn2xjl5JbIOzZB/DGCYF/7bpvhA8KvjUj9HJkag6bBuwZvH1ps6dfgxNHyuJVlzSr2VpMgdTQ==} - engines: {node: '>= 20'} - - '@tailwindcss/vite@4.2.0': - resolution: {integrity: sha512-da9mFCaHpoOgtQiWtDGIikTrSpUFBtIZCG3jy/u2BGV+l/X1/pbxzmIUxNt6JWm19N3WtGi4KlJdSH/Si83WOA==} - peerDependencies: - vite: ^5.2.0 || ^6 || ^7 - - '@tanstack/devtools-event-client@0.4.0': - resolution: {integrity: sha512-RPfGuk2bDZgcu9bAJodvO2lnZeHuz4/71HjZ0bGb/SPg8+lyTA+RLSKQvo7fSmPSi8/vcH3aKQ8EM9ywf1olaw==} - engines: {node: '>=18'} - - '@tanstack/form-core@1.28.3': - resolution: {integrity: sha512-DBhnu1d5VfACAYOAZJO8tsEUHjWczZMJY8v/YrtAJNWpwvL/3ogDuz8e6yUB2m/iVTNq6K8yrnVN2nrX0/BX/w==} - - '@tanstack/history@1.154.14': - resolution: {integrity: sha512-xyIfof8eHBuub1CkBnbKNKQXeRZC4dClhmzePHVOEel4G7lk/dW+TQ16da7CFdeNLv6u6Owf5VoBQxoo6DFTSA==} - engines: {node: '>=12'} - - '@tanstack/pacer-lite@0.1.1': - resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==} - engines: {node: '>=18'} - - '@tanstack/query-core@5.90.20': - resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} - - '@tanstack/react-form@1.28.3': - resolution: {integrity: sha512-84yd0swZRcyC3Q46dYBH6bHf1tlIY1flchbdG3VwArg/wLVW5RdBenIrJhleHjk2OxXuF+9HoKQbHglJyWIXQA==} - peerDependencies: - '@tanstack/react-start': '*' - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@tanstack/react-start': - optional: true - - '@tanstack/react-query@5.90.21': - resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} - peerDependencies: - react: ^18 || ^19 - - '@tanstack/react-router-devtools@1.161.1': - resolution: {integrity: sha512-fl+o760gCHbd4Nb64SpVJQjpe77xDh2Mx6NqZy0aKACXvWRd8CDcFPzSvDZu4s7tHqFKMfzXqhNzL/jT+A8Prg==} - engines: {node: '>=12'} - peerDependencies: - '@tanstack/react-router': ^1.161.1 - '@tanstack/router-core': ^1.161.1 - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - peerDependenciesMeta: - '@tanstack/router-core': - optional: true - - '@tanstack/react-router@1.161.1': - resolution: {integrity: sha512-RQlCaunj+sleC8/JLxd22sWNpwqTHftcRdwGwNF27tjEzTnj06C6azWmA5sGclTdxGVclEOc/eaW7bUv5klsNw==} - engines: {node: '>=12'} - peerDependencies: - react: '>=18.0.0 || >=19.0.0' - react-dom: '>=18.0.0 || >=19.0.0' - - '@tanstack/react-store@0.8.1': - resolution: {integrity: sha512-XItJt+rG8c5Wn/2L/bnxys85rBpm0BfMbhb4zmPVLXAKY9POrp1xd6IbU4PKoOI+jSEGc3vntPRfLGSgXfE2Ig==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@tanstack/router-core@1.161.1': - resolution: {integrity: sha512-Ika9RBvxB5cE+ziLxq90rqwhl9sb+j6mlGkRDwuDaGSDODenFeCDzjE0YQlgQ/kBUdSK2K1fFBiQPy5cnl54Og==} - engines: {node: '>=12'} - - '@tanstack/router-devtools-core@1.161.1': - resolution: {integrity: sha512-I3BcTUD2D8l1sKkab4JJM5LHwwWX5sDCbbhD+MGWplycIujzaW7xADbOnwLpeDjtJarc8kY20cUQ2NJ2eaX9kw==} - engines: {node: '>=12'} - peerDependencies: - '@tanstack/router-core': ^1.161.1 - csstype: ^3.0.10 - peerDependenciesMeta: - csstype: - optional: true - - '@tanstack/router-generator@1.161.1': - resolution: {integrity: sha512-IvkjrSaqr3WzYDUjdXOug1x5MhJT5Pw+hKkAi+GDA4isaBjyXS71QmY3jhsZZ2Rz08Xjw2JkAoIJCxfqw6AQKw==} - engines: {node: '>=12'} - - '@tanstack/router-plugin@1.161.1': - resolution: {integrity: sha512-1veqinPZRJMWJSgKljk3XF6l9PaDRRqnc2FMEGBRJ5ycmDqvzCP4RaKbA5pfE/DbXHkKF5Z7BiAeateZHgm4jA==} - engines: {node: '>=12'} - peerDependencies: - '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.161.1 - vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' - vite-plugin-solid: ^2.11.10 - webpack: '>=5.92.0' - peerDependenciesMeta: - '@rsbuild/core': - optional: true - '@tanstack/react-router': - optional: true - vite: - optional: true - vite-plugin-solid: - optional: true - webpack: - optional: true - - '@tanstack/router-utils@1.158.0': - resolution: {integrity: sha512-qZ76eaLKU6Ae9iI/mc5zizBX149DXXZkBVVO3/QRIll79uKLJZHQlMKR++2ba7JsciBWz1pgpIBcCJPE9S0LVg==} - engines: {node: '>=12'} - - '@tanstack/store@0.8.1': - resolution: {integrity: sha512-PtOisLjUZPz5VyPRSCGjNOlwTvabdTBQ2K80DpVL1chGVr35WRxfeavAPdNq6pm/t7F8GhoR2qtmkkqtCEtHYw==} - - '@tanstack/virtual-file-routes@1.154.7': - resolution: {integrity: sha512-cHHDnewHozgjpI+MIVp9tcib6lYEQK5MyUr0ChHpHFGBl8Xei55rohFK0I0ve/GKoHeioaK42Smd8OixPp6CTg==} - engines: {node: '>=12'} - - '@tokenizer/inflate@0.2.7': - resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} - engines: {node: '>=18'} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/http-cache-semantics@4.2.0': - resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} - - '@types/node@22.19.11': - resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} - - '@types/node@25.3.0': - resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} - - '@types/pg@8.16.0': - resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} - - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - - '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - - '@vitest/ui@4.0.18': - resolution: {integrity: sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==} - peerDependencies: - vitest: 4.0.18 - - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} - - '@xhmikosr/archive-type@7.1.0': - resolution: {integrity: sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg==} - engines: {node: '>=18'} - - '@xhmikosr/bin-check@7.1.0': - resolution: {integrity: sha512-y1O95J4mnl+6MpVmKfMYXec17hMEwE/yeCglFNdx+QvLLtP0yN4rSYcbkXnth+lElBuKKek2NbvOfOGPpUXCvw==} - engines: {node: '>=18'} - - '@xhmikosr/bin-wrapper@13.2.0': - resolution: {integrity: sha512-t9U9X0sDPRGDk5TGx4dv5xiOvniVJpXnfTuynVKwHgtib95NYEw4MkZdJqhoSiz820D9m0o6PCqOPMXz0N9fIw==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-tar@8.1.0': - resolution: {integrity: sha512-m0q8x6lwxenh1CrsTby0Jrjq4vzW/QU1OLhTHMQLEdHpmjR1lgahGz++seZI0bXF3XcZw3U3xHfqZSz+JPP2Gg==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-tarbz2@8.1.0': - resolution: {integrity: sha512-aCLfr3A/FWZnOu5eqnJfme1Z1aumai/WRw55pCvBP+hCGnTFrcpsuiaVN5zmWTR53a8umxncY2JuYsD42QQEbw==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-targz@8.1.0': - resolution: {integrity: sha512-fhClQ2wTmzxzdz2OhSQNo9ExefrAagw93qaG1YggoIz/QpI7atSRa7eOHv4JZkpHWs91XNn8Hry3CwUlBQhfPA==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-unzip@7.1.0': - resolution: {integrity: sha512-oqTYAcObqTlg8owulxFTqiaJkfv2SHsxxxz9Wg4krJAHVzGWlZsU8tAB30R6ow+aHrfv4Kub6WQ8u04NWVPUpA==} - engines: {node: '>=18'} - - '@xhmikosr/decompress@10.2.0': - resolution: {integrity: sha512-MmDBvu0+GmADyQWHolcZuIWffgfnuTo4xpr2I/Qw5Ox0gt+e1Be7oYqJM4te5ylL6mzlcoicnHVDvP27zft8tg==} - engines: {node: '>=18'} - - '@xhmikosr/downloader@15.2.0': - resolution: {integrity: sha512-lAqbig3uRGTt0sHNIM4vUG9HoM+mRl8K28WuYxyXLCUT6pyzl4Y4i0LZ3jMEsCYZ6zjPZbO9XkG91OSTd4si7g==} - engines: {node: '>=18'} - - '@xhmikosr/os-filter-obj@3.0.0': - resolution: {integrity: sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==} - engines: {node: ^14.14.0 || >=16.0.0} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} - engines: {node: '>=14'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arch@3.0.0: - resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - ast-types@0.16.1: - resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} - engines: {node: '>=4'} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - b4a@1.8.0: - resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - - babel-dead-code-elimination@1.0.12: - resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - balanced-match@4.0.3: - resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} - engines: {node: 20 || >=22} - - bare-events@2.8.2: - resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} - engines: {node: '>=6.0.0'} - hasBin: true - - better-auth@1.4.18: - resolution: {integrity: sha512-bnyifLWBPcYVltH3RhS7CM62MoelEqC6Q+GnZwfiDWNfepXoQZBjEvn4urcERC7NTKgKq5zNBM8rvPvRBa6xcg==} - peerDependencies: - '@lynx-js/react': '*' - '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 - '@sveltejs/kit': ^2.0.0 - '@tanstack/react-start': ^1.0.0 - '@tanstack/solid-start': ^1.0.0 - better-sqlite3: ^12.0.0 - drizzle-kit: '>=0.31.4' - drizzle-orm: '>=0.41.0' - mongodb: ^6.0.0 || ^7.0.0 - mysql2: ^3.0.0 - next: ^14.0.0 || ^15.0.0 || ^16.0.0 - pg: ^8.0.0 - prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - solid-js: ^1.0.0 - svelte: ^4.0.0 || ^5.0.0 - vitest: ^2.0.0 || ^3.0.0 || ^4.0.0 - vue: ^3.0.0 - peerDependenciesMeta: - '@lynx-js/react': - optional: true - '@prisma/client': - optional: true - '@sveltejs/kit': - optional: true - '@tanstack/react-start': - optional: true - '@tanstack/solid-start': - optional: true - better-sqlite3: - optional: true - drizzle-kit: - optional: true - drizzle-orm: - optional: true - mongodb: - optional: true - mysql2: - optional: true - next: - optional: true - pg: - optional: true - prisma: - optional: true - react: - optional: true - react-dom: - optional: true - solid-js: - optional: true - svelte: - optional: true - vitest: - optional: true - vue: - optional: true - - better-call@1.1.8: - resolution: {integrity: sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==} - peerDependencies: - zod: ^4.0.0 - peerDependenciesMeta: - zod: - optional: true - - bin-version-check@5.1.0: - resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==} - engines: {node: '>=12'} - - bin-version@6.0.0: - resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==} - engines: {node: '>=12'} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - bowser@2.14.1: - resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - brace-expansion@5.0.2: - resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} - engines: {node: 20 || >=22} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - bullmq@5.69.3: - resolution: {integrity: sha512-P9uLsR7fDvejH/1m6uur6j7U9mqY6nNt+XvhlhStOUe7jdwbZoP/c2oWNtE+8ljOlubw4pRUKymtRqkyvloc4A==} - - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} - - caniuse-lite@1.0.30001770: - resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} - - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - cookie-es@2.0.0: - resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} - - cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} - - cross-env@7.0.3: - resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} - engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} - hasBin: true - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - defaults@2.0.2: - resolution: {integrity: sha512-cuIw0PImdp76AOfgkjbW4VhQODRmNNcKR73vdCH5cLd/ifj7aamfoXvYgfGkEAjNJZ3ozMIy9Gu2LutUkGEPbA==} - engines: {node: '>=16'} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - - denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} - engines: {node: '>=0.10'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - diff@8.0.3: - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} - engines: {node: '>=0.3.1'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dotenv-expand@12.0.3: - resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} - engines: {node: '>=12'} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - drizzle-kit@0.31.9: - resolution: {integrity: sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg==} - hasBin: true - - drizzle-orm@0.44.7: - resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=4' - '@electric-sql/pglite': '>=0.2.0' - '@libsql/client': '>=0.10.0' - '@libsql/client-wasm': '>=0.10.0' - '@neondatabase/serverless': '>=0.10.0' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1.13' - '@prisma/client': '*' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/sql.js': '*' - '@upstash/redis': '>=1.34.7' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=14.0.0' - gel: '>=2' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - prisma: '*' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@libsql/client-wasm': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@prisma/client': - optional: true - '@tidbcloud/serverless': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/sql.js': - optional: true - '@upstash/redis': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - gel: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - prisma: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - - drizzle-zod@0.8.3: - resolution: {integrity: sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==} - peerDependencies: - drizzle-orm: '>=0.36.0' - zod: ^3.25.0 || ^4.0.0 - - electron-to-chromium@1.5.286: - resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - enhanced-resolve@5.19.0: - resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} - engines: {node: '>=10.13.0'} - - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - - esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} - peerDependencies: - esbuild: '>=0.12 <1' - - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - ext-list@2.2.2: - resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} - engines: {node: '>=0.10.0'} - - ext-name@5.0.0: - resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} - engines: {node: '>=4'} - - fast-copy@4.0.2: - resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-sha256@1.3.0: - resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - - fast-xml-parser@5.3.6: - resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} - hasBin: true - - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fetch-retry@6.0.0: - resolution: {integrity: sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==} - - fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - - file-type@20.5.0: - resolution: {integrity: sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==} - engines: {node: '>=18'} - - filename-reserved-regex@3.0.0: - resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - filenamify@6.0.0: - resolution: {integrity: sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==} - engines: {node: '>=16'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-versions@5.1.0: - resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==} - engines: {node: '>=12'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-tsconfig@4.13.6: - resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@13.0.6: - resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} - engines: {node: 18 || 20 || >=22} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - - goober@2.1.18: - resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} - peerDependencies: - csstype: ^3.0.10 - - got@13.0.0: - resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} - engines: {node: '>=16'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - - hono-pino@0.7.2: - resolution: {integrity: sha512-uLJOngId4Ia2eHXnCPE8xpyMVkh+AGxAkHZKgvZk8YkmuTbcVDDUMe7aHMEz+YLqCDgd/Hk9ytVmmoQ8QTUXgQ==} - engines: {node: '>=18'} - peerDependencies: - hono: '>=4.0.0' - pino: '>=7.1.0' - - hono@4.12.0: - resolution: {integrity: sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==} - engines: {node: '>=16.9.0'} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - inspect-with-kind@1.0.5: - resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} - - ioredis@5.9.2: - resolution: {integrity: sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==} - engines: {node: '>=12.22.0'} - - ioredis@5.9.3: - resolution: {integrity: sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==} - engines: {node: '>=12.22.0'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - isbot@5.1.35: - resolution: {integrity: sha512-waFfC72ZNfwLLuJ2iLaoVaqcNo+CAaLR7xCpAn0Y5WfGzkNHv7ZN39Vbi1y+kb+Zs46XHOX3tZNExroFUPX+Kg==} - engines: {node: '>=18'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} - hasBin: true - - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} - - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kysely@0.28.11: - resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==} - engines: {node: '>=20.0.0'} - - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} - engines: {node: '>= 12.0.0'} - - lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - - lodash.isarguments@3.1.0: - resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lucide-react@0.553.0: - resolution: {integrity: sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} - engines: {node: '>=12'} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - minimatch@10.2.2: - resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} - engines: {node: 18 || 20 || >=22} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} - hasBin: true - - msgpackr@1.11.5: - resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} - - mylas@2.1.14: - resolution: {integrity: sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==} - engines: {node: '>=16.0.0'} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@5.1.6: - resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} - engines: {node: ^18 || >=20} - hasBin: true - - nanostores@1.1.0: - resolution: {integrity: sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==} - engines: {node: ^20.0.0 || >=22.0.0} - - neverthrow@8.2.0: - resolution: {integrity: sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ==} - engines: {node: '>=18'} - - node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@8.1.1: - resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} - engines: {node: '>=14.16'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - openapi3-ts@4.5.0: - resolution: {integrity: sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==} - - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - pg-cloudflare@1.3.0: - resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} - - pg-connection-string@2.11.0: - resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==} - - pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - - pg-pool@3.11.0: - resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==} - peerDependencies: - pg: '>=8.0' - - pg-protocol@1.11.0: - resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} - - pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - - pg@8.18.0: - resolution: {integrity: sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==} - engines: {node: '>= 16.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - - pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} - - pino-abstract-transport@2.0.0: - resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} - - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} - hasBin: true - - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - - pino@9.14.0: - resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} - hasBin: true - - piscina@4.9.2: - resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==} - - plimit-lit@1.6.1: - resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} - engines: {node: '>=12'} - - postal-mime@2.7.3: - resolution: {integrity: sha512-MjhXadAJaWgYzevi46+3kLak8y6gbg0ku14O1gO/LNOuay8dO+1PtcSGvAdgDR0DoIsSaiIA8y/Ddw6MnrO0Tw==} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - - postgres-bytea@1.0.1: - resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} - engines: {node: '>=0.10.0'} - - postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - - postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - - postgres@3.4.8: - resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} - engines: {node: '>=12'} - - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} - hasBin: true - - process-warning@5.0.0: - resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - queue-lit@1.5.2: - resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} - engines: {node: '>=12'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - react-dom@19.2.4: - resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} - peerDependencies: - react: ^19.2.4 - - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} - - react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} - engines: {node: '>=0.10.0'} - - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - recast@0.23.11: - resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} - engines: {node: '>= 4'} - - redis-errors@1.2.0: - resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} - engines: {node: '>=4'} - - redis-parser@3.0.0: - resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} - engines: {node: '>=4'} - - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - - resend@6.9.2: - resolution: {integrity: sha512-uIM6CQ08tS+hTCRuKBFbOBvHIGaEhqZe8s4FOgqsVXSbQLAhmNWpmUhG3UAtRnmcwTWFUqnHa/+Vux8YGPyDBA==} - engines: {node: '>=20'} - peerDependencies: - '@react-email/render': '*' - peerDependenciesMeta: - '@react-email/render': - optional: true - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@6.1.3: - resolution: {integrity: sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==} - engines: {node: 20 || >=22} - hasBin: true - - rollup@4.57.1: - resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rou3@0.7.12: - resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - - seek-bzip@2.0.0: - resolution: {integrity: sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==} - hasBin: true - - semver-regex@4.0.5: - resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} - engines: {node: '>=12'} - - semver-truncate@3.0.0: - resolution: {integrity: sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==} - engines: {node: '>=12'} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - seroval-plugins@1.5.0: - resolution: {integrity: sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - - seroval@1.5.0: - resolution: {integrity: sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==} - engines: {node: '>=10'} - - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} - - sonner@2.0.7: - resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - - sort-keys-length@1.0.1: - resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} - engines: {node: '>=0.10.0'} - - sort-keys@1.1.2: - resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} - engines: {node: '>=0.10.0'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - standard-as-callback@2.1.0: - resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - - standardwebhooks@1.0.0: - resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} - - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - - stoker@2.0.1: - resolution: {integrity: sha512-liSQNnJmn8fWSEan7sVaFe6iSHuN3X02fDGLS6snwW+FUuKi5HmKUHm3P+Kzr5xiDPqRpmSTtmGEBbSL9H2zkQ==} - peerDependencies: - '@asteasolutions/zod-to-openapi': ^8.0.0 - '@hono/zod-openapi': '>=1.0.0' - hono: ^4.0.0 - openapi3-ts: ^4.5.0 - peerDependenciesMeta: - '@asteasolutions/zod-to-openapi': - optional: true - '@hono/zod-openapi': - optional: true - openapi3-ts: - optional: true - - streamx@2.23.0: - resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-dirs@3.0.0: - resolution: {integrity: sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@5.0.3: - resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} - engines: {node: '>=14.16'} - - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} - - strtok3@10.3.4: - resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} - engines: {node: '>=18'} - - svix@1.84.1: - resolution: {integrity: sha512-K8DPPSZaW/XqXiz1kEyzSHYgmGLnhB43nQCMeKjWGCUpLIpAMMM8kx3rVVOSm6Bo6EHyK1RQLPT4R06skM/MlQ==} - - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} - - tagged-tag@1.0.0: - resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} - engines: {node: '>=20'} - - tailwind-merge@3.5.0: - resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} - - tailwindcss@4.2.0: - resolution: {integrity: sha512-yYzTZ4++b7fNYxFfpnberEEKu43w44aqDMNM9MHMmcKuCH7lL8jJ4yJ7LGHv7rSwiqM0nkiobF9I6cLlpS2P7Q==} - - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} - engines: {node: '>=6'} - - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - - thread-stream@3.1.0: - resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - tiny-warning@1.0.3: - resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} - engines: {node: '>=14.16'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - tsc-alias@1.8.16: - resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==} - engines: {node: '>=16.20.2'} - hasBin: true - - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} - engines: {node: '>=18.0.0'} - hasBin: true - - tw-animate-css@1.4.0: - resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} - - type-fest@5.4.4: - resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} - engines: {node: '>=20'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} - engines: {node: '>=18'} - - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - - unplugin@2.3.11: - resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} - engines: {node: '>=18.12.0'} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - vite-tsconfig-paths@5.1.4: - resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true - - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - 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 - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} - engines: {node: '>= 14.6'} - hasBin: true - - yauzl@3.2.0: - resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} - engines: {node: '>=12'} - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - - zod@4.2.1: - resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} - - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - - zustand@5.0.11: - resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - -snapshots: - - '@asteasolutions/zod-to-openapi@8.0.0(zod@4.2.1)': - dependencies: - openapi3-ts: 4.5.0 - zod: 4.2.1 - - '@asteasolutions/zod-to-openapi@8.4.1(zod@4.2.1)': - dependencies: - openapi3-ts: 4.5.0 - zod: 4.2.1 - - '@aws-crypto/crc32@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.1 - tslib: 2.8.1 - - '@aws-crypto/crc32c@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.1 - tslib: 2.8.1 - - '@aws-crypto/sha1-browser@5.2.0': - dependencies: - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-locate-window': 3.965.4 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-crypto/sha256-browser@5.2.0': - dependencies: - '@aws-crypto/sha256-js': 5.2.0 - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-locate-window': 3.965.4 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-crypto/sha256-js@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.1 - tslib: 2.8.1 - - '@aws-crypto/supports-web-crypto@5.2.0': - dependencies: - tslib: 2.8.1 - - '@aws-crypto/util@5.2.0': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-sdk/client-s3@3.994.0': - dependencies: - '@aws-crypto/sha1-browser': 5.2.0 - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.11 - '@aws-sdk/credential-provider-node': 3.972.10 - '@aws-sdk/middleware-bucket-endpoint': 3.972.3 - '@aws-sdk/middleware-expect-continue': 3.972.3 - '@aws-sdk/middleware-flexible-checksums': 3.972.9 - '@aws-sdk/middleware-host-header': 3.972.3 - '@aws-sdk/middleware-location-constraint': 3.972.3 - '@aws-sdk/middleware-logger': 3.972.3 - '@aws-sdk/middleware-recursion-detection': 3.972.3 - '@aws-sdk/middleware-sdk-s3': 3.972.11 - '@aws-sdk/middleware-ssec': 3.972.3 - '@aws-sdk/middleware-user-agent': 3.972.11 - '@aws-sdk/region-config-resolver': 3.972.3 - '@aws-sdk/signature-v4-multi-region': 3.994.0 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-endpoints': 3.994.0 - '@aws-sdk/util-user-agent-browser': 3.972.3 - '@aws-sdk/util-user-agent-node': 3.972.9 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.23.2 - '@smithy/eventstream-serde-browser': 4.2.8 - '@smithy/eventstream-serde-config-resolver': 4.3.8 - '@smithy/eventstream-serde-node': 4.2.8 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-blob-browser': 4.2.9 - '@smithy/hash-node': 4.2.8 - '@smithy/hash-stream-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/md5-js': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.16 - '@smithy/middleware-retry': 4.4.33 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.10 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.32 - '@smithy/util-defaults-mode-node': 4.2.35 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-stream': 4.5.12 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.8 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sso@3.993.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.11 - '@aws-sdk/middleware-host-header': 3.972.3 - '@aws-sdk/middleware-logger': 3.972.3 - '@aws-sdk/middleware-recursion-detection': 3.972.3 - '@aws-sdk/middleware-user-agent': 3.972.11 - '@aws-sdk/region-config-resolver': 3.972.3 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-endpoints': 3.993.0 - '@aws-sdk/util-user-agent-browser': 3.972.3 - '@aws-sdk/util-user-agent-node': 3.972.9 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.23.2 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.16 - '@smithy/middleware-retry': 4.4.33 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.10 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.32 - '@smithy/util-defaults-mode-node': 4.2.35 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.973.11': - dependencies: - '@aws-sdk/types': 3.973.1 - '@aws-sdk/xml-builder': 3.972.5 - '@smithy/core': 3.23.2 - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/crc64-nvme@3.972.0': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.972.9': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.972.11': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/types': 3.973.1 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/node-http-handler': 4.4.10 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/util-stream': 4.5.12 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-ini@3.972.9': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/credential-provider-env': 3.972.9 - '@aws-sdk/credential-provider-http': 3.972.11 - '@aws-sdk/credential-provider-login': 3.972.9 - '@aws-sdk/credential-provider-process': 3.972.9 - '@aws-sdk/credential-provider-sso': 3.972.9 - '@aws-sdk/credential-provider-web-identity': 3.972.9 - '@aws-sdk/nested-clients': 3.993.0 - '@aws-sdk/types': 3.973.1 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-login@3.972.9': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/nested-clients': 3.993.0 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-node@3.972.10': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.9 - '@aws-sdk/credential-provider-http': 3.972.11 - '@aws-sdk/credential-provider-ini': 3.972.9 - '@aws-sdk/credential-provider-process': 3.972.9 - '@aws-sdk/credential-provider-sso': 3.972.9 - '@aws-sdk/credential-provider-web-identity': 3.972.9 - '@aws-sdk/types': 3.973.1 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-process@3.972.9': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-sso@3.972.9': - dependencies: - '@aws-sdk/client-sso': 3.993.0 - '@aws-sdk/core': 3.973.11 - '@aws-sdk/token-providers': 3.993.0 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-web-identity@3.972.9': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/nested-clients': 3.993.0 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/middleware-bucket-endpoint@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-arn-parser': 3.972.2 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-expect-continue@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-flexible-checksums@3.972.9': - dependencies: - '@aws-crypto/crc32': 5.2.0 - '@aws-crypto/crc32c': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.11 - '@aws-sdk/crc64-nvme': 3.972.0 - '@aws-sdk/types': 3.973.1 - '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.12 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-host-header@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-location-constraint@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-logger@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-recursion-detection@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@aws/lambda-invoke-store': 0.2.3 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-s3@3.972.11': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-arn-parser': 3.972.2 - '@smithy/core': 3.23.2 - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.12 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-ssec@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-user-agent@3.972.11': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-endpoints': 3.993.0 - '@smithy/core': 3.23.2 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.993.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.11 - '@aws-sdk/middleware-host-header': 3.972.3 - '@aws-sdk/middleware-logger': 3.972.3 - '@aws-sdk/middleware-recursion-detection': 3.972.3 - '@aws-sdk/middleware-user-agent': 3.972.11 - '@aws-sdk/region-config-resolver': 3.972.3 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-endpoints': 3.993.0 - '@aws-sdk/util-user-agent-browser': 3.972.3 - '@aws-sdk/util-user-agent-node': 3.972.9 - '@smithy/config-resolver': 4.4.6 - '@smithy/core': 3.23.2 - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/hash-node': 4.2.8 - '@smithy/invalid-dependency': 4.2.8 - '@smithy/middleware-content-length': 4.2.8 - '@smithy/middleware-endpoint': 4.4.16 - '@smithy/middleware-retry': 4.4.33 - '@smithy/middleware-serde': 4.2.9 - '@smithy/middleware-stack': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/node-http-handler': 4.4.10 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.32 - '@smithy/util-defaults-mode-node': 4.2.35 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/region-config-resolver@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/config-resolver': 4.4.6 - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/s3-request-presigner@3.994.0': - dependencies: - '@aws-sdk/signature-v4-multi-region': 3.994.0 - '@aws-sdk/types': 3.973.1 - '@aws-sdk/util-format-url': 3.972.3 - '@smithy/middleware-endpoint': 4.4.16 - '@smithy/protocol-http': 5.3.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/signature-v4-multi-region@3.994.0': - dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.11 - '@aws-sdk/types': 3.973.1 - '@smithy/protocol-http': 5.3.8 - '@smithy/signature-v4': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/token-providers@3.993.0': - dependencies: - '@aws-sdk/core': 3.973.11 - '@aws-sdk/nested-clients': 3.993.0 - '@aws-sdk/types': 3.973.1 - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/types@3.973.1': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/util-arn-parser@3.972.2': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-endpoints@3.993.0': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-endpoints': 3.2.8 - tslib: 2.8.1 - - '@aws-sdk/util-endpoints@3.994.0': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-endpoints': 3.2.8 - tslib: 2.8.1 - - '@aws-sdk/util-format-url@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/querystring-builder': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/util-locate-window@3.965.4': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-browser@3.972.3': - dependencies: - '@aws-sdk/types': 3.973.1 - '@smithy/types': 4.12.0 - bowser: 2.14.1 - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-node@3.972.9': - dependencies: - '@aws-sdk/middleware-user-agent': 3.972.11 - '@aws-sdk/types': 3.973.1 - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.972.5': - dependencies: - '@smithy/types': 4.12.0 - fast-xml-parser: 5.3.6 - tslib: 2.8.1 - - '@aws/lambda-invoke-store@0.2.3': {} - - '@axiomhq/js@1.4.0': - dependencies: - fetch-retry: 6.0.0 - - '@axiomhq/pino@1.4.0': - dependencies: - '@axiomhq/js': 1.4.0 - pino-abstract-transport: 1.2.0 - - '@babel/code-frame@7.29.0': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.29.0': {} - - '@babel/core@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.1': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.28.6': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.28.6': {} - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.6': - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - - '@babel/parser@7.29.0': - dependencies: - '@babel/types': 7.29.0 - - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/runtime@7.28.6': {} - - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - - '@babel/traverse@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@base-ui/react@1.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@babel/runtime': 7.28.6 - '@base-ui/utils': 0.2.5(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@floating-ui/react-dom': 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@floating-ui/utils': 0.2.10 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - - '@base-ui/utils@0.2.5(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@babel/runtime': 7.28.6 - '@floating-ui/utils': 0.2.10 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.4) - optionalDependencies: - '@types/react': 19.2.14 - - '@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.2.1))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)': - dependencies: - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - '@standard-schema/spec': 1.1.0 - better-call: 1.1.8(zod@4.3.6) - jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.0 - zod: 4.3.6 - - '@better-auth/telemetry@1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.2.1))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))': - dependencies: - '@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.2.1))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - - '@better-auth/utils@0.3.0': {} - - '@better-fetch/fetch@1.1.21': {} - - '@biomejs/biome@2.3.7': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.3.7 - '@biomejs/cli-darwin-x64': 2.3.7 - '@biomejs/cli-linux-arm64': 2.3.7 - '@biomejs/cli-linux-arm64-musl': 2.3.7 - '@biomejs/cli-linux-x64': 2.3.7 - '@biomejs/cli-linux-x64-musl': 2.3.7 - '@biomejs/cli-win32-arm64': 2.3.7 - '@biomejs/cli-win32-x64': 2.3.7 - - '@biomejs/cli-darwin-arm64@2.3.7': - optional: true - - '@biomejs/cli-darwin-x64@2.3.7': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.3.7': - optional: true - - '@biomejs/cli-linux-arm64@2.3.7': - optional: true - - '@biomejs/cli-linux-x64-musl@2.3.7': - optional: true - - '@biomejs/cli-linux-x64@2.3.7': - optional: true - - '@biomejs/cli-win32-arm64@2.3.7': - optional: true - - '@biomejs/cli-win32-x64@2.3.7': - optional: true - - '@borewit/text-codec@0.2.1': {} - - '@drizzle-team/brocli@0.10.2': {} - - '@esbuild-kit/core-utils@3.3.2': - dependencies: - esbuild: 0.18.20 - source-map-support: 0.5.21 - - '@esbuild-kit/esm-loader@2.6.5': - dependencies: - '@esbuild-kit/core-utils': 3.3.2 - get-tsconfig: 4.13.6 - - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/aix-ppc64@0.27.3': - optional: true - - '@esbuild/android-arm64@0.18.20': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.27.3': - optional: true - - '@esbuild/android-arm@0.18.20': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-arm@0.27.3': - optional: true - - '@esbuild/android-x64@0.18.20': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/android-x64@0.27.3': - optional: true - - '@esbuild/darwin-arm64@0.18.20': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.27.3': - optional: true - - '@esbuild/darwin-x64@0.18.20': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.27.3': - optional: true - - '@esbuild/freebsd-arm64@0.18.20': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.27.3': - optional: true - - '@esbuild/freebsd-x64@0.18.20': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.27.3': - optional: true - - '@esbuild/linux-arm64@0.18.20': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.27.3': - optional: true - - '@esbuild/linux-arm@0.18.20': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-arm@0.27.3': - optional: true - - '@esbuild/linux-ia32@0.18.20': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.27.3': - optional: true - - '@esbuild/linux-loong64@0.18.20': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.27.3': - optional: true - - '@esbuild/linux-mips64el@0.18.20': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.27.3': - optional: true - - '@esbuild/linux-ppc64@0.18.20': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.27.3': - optional: true - - '@esbuild/linux-riscv64@0.18.20': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.27.3': - optional: true - - '@esbuild/linux-s390x@0.18.20': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.27.3': - optional: true - - '@esbuild/linux-x64@0.18.20': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/linux-x64@0.27.3': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.27.3': - optional: true - - '@esbuild/netbsd-x64@0.18.20': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.27.3': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.27.3': - optional: true - - '@esbuild/openbsd-x64@0.18.20': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.27.3': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/openharmony-arm64@0.27.3': - optional: true - - '@esbuild/sunos-x64@0.18.20': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.27.3': - optional: true - - '@esbuild/win32-arm64@0.18.20': - optional: true - - '@esbuild/win32-arm64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.27.3': - optional: true - - '@esbuild/win32-ia32@0.18.20': - optional: true - - '@esbuild/win32-ia32@0.25.12': - optional: true - - '@esbuild/win32-ia32@0.27.3': - optional: true - - '@esbuild/win32-x64@0.18.20': - optional: true - - '@esbuild/win32-x64@0.25.12': - optional: true - - '@esbuild/win32-x64@0.27.3': - optional: true - - '@floating-ui/core@1.7.4': - dependencies: - '@floating-ui/utils': 0.2.10 - - '@floating-ui/dom@1.7.5': - dependencies: - '@floating-ui/core': 1.7.4 - '@floating-ui/utils': 0.2.10 - - '@floating-ui/react-dom@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@floating-ui/dom': 1.7.5 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - - '@floating-ui/utils@0.2.10': {} - - '@hono/node-server@1.19.9(hono@4.12.0)': - dependencies: - hono: 4.12.0 - - '@hono/node-ws@1.3.0(@hono/node-server@1.19.9(hono@4.12.0))(hono@4.12.0)': - dependencies: - '@hono/node-server': 1.19.9(hono@4.12.0) - hono: 4.12.0 - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@hono/swagger-ui@0.5.3(hono@4.12.0)': - dependencies: - hono: 4.12.0 - - '@hono/zod-openapi@1.2.2(hono@4.12.0)(zod@4.2.1)': - dependencies: - '@asteasolutions/zod-to-openapi': 8.4.1(zod@4.2.1) - '@hono/zod-validator': 0.7.6(hono@4.12.0)(zod@4.2.1) - hono: 4.12.0 - openapi3-ts: 4.5.0 - zod: 4.2.1 - - '@hono/zod-validator@0.7.6(hono@4.12.0)(zod@4.2.1)': - dependencies: - hono: 4.12.0 - zod: 4.2.1 - - '@ioredis/commands@1.5.0': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - optional: true - - '@napi-rs/nice-android-arm-eabi@1.1.1': - optional: true - - '@napi-rs/nice-android-arm64@1.1.1': - optional: true - - '@napi-rs/nice-darwin-arm64@1.1.1': - optional: true - - '@napi-rs/nice-darwin-x64@1.1.1': - optional: true - - '@napi-rs/nice-freebsd-x64@1.1.1': - optional: true - - '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': - optional: true - - '@napi-rs/nice-linux-arm64-gnu@1.1.1': - optional: true - - '@napi-rs/nice-linux-arm64-musl@1.1.1': - optional: true - - '@napi-rs/nice-linux-ppc64-gnu@1.1.1': - optional: true - - '@napi-rs/nice-linux-riscv64-gnu@1.1.1': - optional: true - - '@napi-rs/nice-linux-s390x-gnu@1.1.1': - optional: true - - '@napi-rs/nice-linux-x64-gnu@1.1.1': - optional: true - - '@napi-rs/nice-linux-x64-musl@1.1.1': - optional: true - - '@napi-rs/nice-openharmony-arm64@1.1.1': - optional: true - - '@napi-rs/nice-win32-arm64-msvc@1.1.1': - optional: true - - '@napi-rs/nice-win32-ia32-msvc@1.1.1': - optional: true - - '@napi-rs/nice-win32-x64-msvc@1.1.1': - optional: true - - '@napi-rs/nice@1.1.1': - optionalDependencies: - '@napi-rs/nice-android-arm-eabi': 1.1.1 - '@napi-rs/nice-android-arm64': 1.1.1 - '@napi-rs/nice-darwin-arm64': 1.1.1 - '@napi-rs/nice-darwin-x64': 1.1.1 - '@napi-rs/nice-freebsd-x64': 1.1.1 - '@napi-rs/nice-linux-arm-gnueabihf': 1.1.1 - '@napi-rs/nice-linux-arm64-gnu': 1.1.1 - '@napi-rs/nice-linux-arm64-musl': 1.1.1 - '@napi-rs/nice-linux-ppc64-gnu': 1.1.1 - '@napi-rs/nice-linux-riscv64-gnu': 1.1.1 - '@napi-rs/nice-linux-s390x-gnu': 1.1.1 - '@napi-rs/nice-linux-x64-gnu': 1.1.1 - '@napi-rs/nice-linux-x64-musl': 1.1.1 - '@napi-rs/nice-openharmony-arm64': 1.1.1 - '@napi-rs/nice-win32-arm64-msvc': 1.1.1 - '@napi-rs/nice-win32-ia32-msvc': 1.1.1 - '@napi-rs/nice-win32-x64-msvc': 1.1.1 - optional: true - - '@noble/ciphers@2.1.1': {} - - '@noble/hashes@2.0.1': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@pinojs/redact@0.4.0': {} - - '@polka/url@1.0.0-next.29': {} - - '@rolldown/pluginutils@1.0.0-rc.3': {} - - '@rollup/rollup-android-arm-eabi@4.57.1': - optional: true - - '@rollup/rollup-android-arm64@4.57.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.57.1': - optional: true - - '@rollup/rollup-darwin-x64@4.57.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.57.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.57.1': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.57.1': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.57.1': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.57.1': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.57.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.57.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.57.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.57.1': - optional: true - - '@rollup/rollup-openbsd-x64@4.57.1': - optional: true - - '@rollup/rollup-openharmony-arm64@4.57.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.57.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.57.1': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.57.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.57.1': - optional: true - - '@scalar/core@0.3.41': - dependencies: - '@scalar/types': 0.6.6 - - '@scalar/helpers@0.2.15': {} - - '@scalar/hono-api-reference@0.9.44(hono@4.12.0)': - dependencies: - '@scalar/core': 0.3.41 - hono: 4.12.0 - - '@scalar/types@0.6.6': - dependencies: - '@scalar/helpers': 0.2.15 - nanoid: 5.1.6 - type-fest: 5.4.4 - zod: 4.3.6 - - '@sindresorhus/is@5.6.0': {} - - '@smithy/abort-controller@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader-native@4.2.1': - dependencies: - '@smithy/util-base64': 4.3.0 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader@5.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/config-resolver@4.4.6': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.8 - '@smithy/util-middleware': 4.2.8 - tslib: 2.8.1 - - '@smithy/core@3.23.2': - dependencies: - '@smithy/middleware-serde': 4.2.9 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-stream': 4.5.12 - '@smithy/util-utf8': 4.2.0 - '@smithy/uuid': 1.1.0 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.2.8': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - tslib: 2.8.1 - - '@smithy/eventstream-codec@4.2.8': - dependencies: - '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.12.0 - '@smithy/util-hex-encoding': 4.2.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-browser@4.2.8': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-config-resolver@4.3.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-node@4.2.8': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-universal@4.2.8': - dependencies: - '@smithy/eventstream-codec': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.3.9': - dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/querystring-builder': 4.2.8 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - tslib: 2.8.1 - - '@smithy/hash-blob-browser@4.2.9': - dependencies: - '@smithy/chunked-blob-reader': 5.2.0 - '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/hash-node@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/hash-stream-node@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/invalid-dependency@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/is-array-buffer@2.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/is-array-buffer@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/md5-js@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/middleware-content-length@4.2.8': - dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/middleware-endpoint@4.4.16': - dependencies: - '@smithy/core': 3.23.2 - '@smithy/middleware-serde': 4.2.9 - '@smithy/node-config-provider': 4.3.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - '@smithy/url-parser': 4.2.8 - '@smithy/util-middleware': 4.2.8 - tslib: 2.8.1 - - '@smithy/middleware-retry@4.4.33': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/service-error-classification': 4.2.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-retry': 4.2.8 - '@smithy/uuid': 1.1.0 - tslib: 2.8.1 - - '@smithy/middleware-serde@4.2.9': - dependencies: - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/middleware-stack@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/node-config-provider@4.3.8': - dependencies: - '@smithy/property-provider': 4.2.8 - '@smithy/shared-ini-file-loader': 4.4.3 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.4.10': - dependencies: - '@smithy/abort-controller': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/querystring-builder': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/property-provider@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/protocol-http@5.3.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/querystring-builder@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - '@smithy/util-uri-escape': 4.2.0 - tslib: 2.8.1 - - '@smithy/querystring-parser@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/service-error-classification@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - - '@smithy/shared-ini-file-loader@4.4.3': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/signature-v4@5.3.8': - dependencies: - '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.8 - '@smithy/util-uri-escape': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/smithy-client@4.11.5': - dependencies: - '@smithy/core': 3.23.2 - '@smithy/middleware-endpoint': 4.4.16 - '@smithy/middleware-stack': 4.2.8 - '@smithy/protocol-http': 5.3.8 - '@smithy/types': 4.12.0 - '@smithy/util-stream': 4.5.12 - tslib: 2.8.1 - - '@smithy/types@4.12.0': - dependencies: - tslib: 2.8.1 - - '@smithy/url-parser@4.2.8': - dependencies: - '@smithy/querystring-parser': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-base64@4.3.0': - dependencies: - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-body-length-browser@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-body-length-node@4.2.1': - dependencies: - tslib: 2.8.1 - - '@smithy/util-buffer-from@2.2.0': - dependencies: - '@smithy/is-array-buffer': 2.2.0 - tslib: 2.8.1 - - '@smithy/util-buffer-from@4.2.0': - dependencies: - '@smithy/is-array-buffer': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-config-provider@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-defaults-mode-browser@4.3.32': - dependencies: - '@smithy/property-provider': 4.2.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-defaults-mode-node@4.2.35': - dependencies: - '@smithy/config-resolver': 4.4.6 - '@smithy/credential-provider-imds': 4.2.8 - '@smithy/node-config-provider': 4.3.8 - '@smithy/property-provider': 4.2.8 - '@smithy/smithy-client': 4.11.5 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-endpoints@3.2.8': - dependencies: - '@smithy/node-config-provider': 4.3.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-hex-encoding@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-middleware@4.2.8': - dependencies: - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-retry@4.2.8': - dependencies: - '@smithy/service-error-classification': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/util-stream@4.5.12': - dependencies: - '@smithy/fetch-http-handler': 5.3.9 - '@smithy/node-http-handler': 4.4.10 - '@smithy/types': 4.12.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-uri-escape@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-utf8@2.3.0': - dependencies: - '@smithy/util-buffer-from': 2.2.0 - tslib: 2.8.1 - - '@smithy/util-utf8@4.2.0': - dependencies: - '@smithy/util-buffer-from': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-waiter@4.2.8': - dependencies: - '@smithy/abort-controller': 4.2.8 - '@smithy/types': 4.12.0 - tslib: 2.8.1 - - '@smithy/uuid@1.1.0': - dependencies: - tslib: 2.8.1 - - '@stablelib/base64@1.0.1': {} - - '@standard-schema/spec@1.1.0': {} - - '@swc/cli@0.7.10(@swc/core@1.15.11)': - dependencies: - '@swc/core': 1.15.11 - '@swc/counter': 0.1.3 - '@xhmikosr/bin-wrapper': 13.2.0 - commander: 8.3.0 - minimatch: 9.0.5 - piscina: 4.9.2 - semver: 7.7.4 - slash: 3.0.0 - source-map: 0.7.6 - tinyglobby: 0.2.15 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@swc/core-darwin-arm64@1.15.11': - optional: true - - '@swc/core-darwin-x64@1.15.11': - optional: true - - '@swc/core-linux-arm-gnueabihf@1.15.11': - optional: true - - '@swc/core-linux-arm64-gnu@1.15.11': - optional: true - - '@swc/core-linux-arm64-musl@1.15.11': - optional: true - - '@swc/core-linux-x64-gnu@1.15.11': - optional: true - - '@swc/core-linux-x64-musl@1.15.11': - optional: true - - '@swc/core-win32-arm64-msvc@1.15.11': - optional: true - - '@swc/core-win32-ia32-msvc@1.15.11': - optional: true - - '@swc/core-win32-x64-msvc@1.15.11': - optional: true - - '@swc/core@1.15.11': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.25 - optionalDependencies: - '@swc/core-darwin-arm64': 1.15.11 - '@swc/core-darwin-x64': 1.15.11 - '@swc/core-linux-arm-gnueabihf': 1.15.11 - '@swc/core-linux-arm64-gnu': 1.15.11 - '@swc/core-linux-arm64-musl': 1.15.11 - '@swc/core-linux-x64-gnu': 1.15.11 - '@swc/core-linux-x64-musl': 1.15.11 - '@swc/core-win32-arm64-msvc': 1.15.11 - '@swc/core-win32-ia32-msvc': 1.15.11 - '@swc/core-win32-x64-msvc': 1.15.11 - - '@swc/counter@0.1.3': {} - - '@swc/types@0.1.25': - dependencies: - '@swc/counter': 0.1.3 - - '@szmarczak/http-timer@5.0.1': - dependencies: - defer-to-connect: 2.0.1 - - '@tailwindcss/node@4.2.0': - dependencies: - '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.19.0 - jiti: 2.6.1 - lightningcss: 1.31.1 - magic-string: 0.30.21 - source-map-js: 1.2.1 - tailwindcss: 4.2.0 - - '@tailwindcss/oxide-android-arm64@4.2.0': - optional: true - - '@tailwindcss/oxide-darwin-arm64@4.2.0': - optional: true - - '@tailwindcss/oxide-darwin-x64@4.2.0': - optional: true - - '@tailwindcss/oxide-freebsd-x64@4.2.0': - optional: true - - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.0': - optional: true - - '@tailwindcss/oxide-linux-arm64-gnu@4.2.0': - optional: true - - '@tailwindcss/oxide-linux-arm64-musl@4.2.0': - optional: true - - '@tailwindcss/oxide-linux-x64-gnu@4.2.0': - optional: true - - '@tailwindcss/oxide-linux-x64-musl@4.2.0': - optional: true - - '@tailwindcss/oxide-wasm32-wasi@4.2.0': - optional: true - - '@tailwindcss/oxide-win32-arm64-msvc@4.2.0': - optional: true - - '@tailwindcss/oxide-win32-x64-msvc@4.2.0': - optional: true - - '@tailwindcss/oxide@4.2.0': - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.0 - '@tailwindcss/oxide-darwin-arm64': 4.2.0 - '@tailwindcss/oxide-darwin-x64': 4.2.0 - '@tailwindcss/oxide-freebsd-x64': 4.2.0 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.0 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.0 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.0 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.0 - '@tailwindcss/oxide-linux-x64-musl': 4.2.0 - '@tailwindcss/oxide-wasm32-wasi': 4.2.0 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.0 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.0 - - '@tailwindcss/vite@4.2.0(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@tailwindcss/node': 4.2.0 - '@tailwindcss/oxide': 4.2.0 - tailwindcss: 4.2.0 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - '@tanstack/devtools-event-client@0.4.0': {} - - '@tanstack/form-core@1.28.3': - dependencies: - '@tanstack/devtools-event-client': 0.4.0 - '@tanstack/pacer-lite': 0.1.1 - '@tanstack/store': 0.8.1 - - '@tanstack/history@1.154.14': {} - - '@tanstack/pacer-lite@0.1.1': {} - - '@tanstack/query-core@5.90.20': {} - - '@tanstack/react-form@1.28.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@tanstack/form-core': 1.28.3 - '@tanstack/react-store': 0.8.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - transitivePeerDependencies: - - react-dom - - '@tanstack/react-query@5.90.21(react@19.2.4)': - dependencies: - '@tanstack/query-core': 5.90.20 - react: 19.2.4 - - '@tanstack/react-router-devtools@1.161.1(@tanstack/react-router@1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.161.1)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@tanstack/react-router': 1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-devtools-core': 1.161.1(@tanstack/router-core@1.161.1)(csstype@3.2.3) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - optionalDependencies: - '@tanstack/router-core': 1.161.1 - transitivePeerDependencies: - - csstype - - '@tanstack/react-router@1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@tanstack/history': 1.154.14 - '@tanstack/react-store': 0.8.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.161.1 - isbot: 5.1.35 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - '@tanstack/react-store@0.8.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@tanstack/store': 0.8.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - use-sync-external-store: 1.6.0(react@19.2.4) - - '@tanstack/router-core@1.161.1': - dependencies: - '@tanstack/history': 1.154.14 - '@tanstack/store': 0.8.1 - cookie-es: 2.0.0 - seroval: 1.5.0 - seroval-plugins: 1.5.0(seroval@1.5.0) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - '@tanstack/router-devtools-core@1.161.1(@tanstack/router-core@1.161.1)(csstype@3.2.3)': - dependencies: - '@tanstack/router-core': 1.161.1 - clsx: 2.1.1 - goober: 2.1.18(csstype@3.2.3) - tiny-invariant: 1.3.3 - optionalDependencies: - csstype: 3.2.3 - - '@tanstack/router-generator@1.161.1': - dependencies: - '@tanstack/router-core': 1.161.1 - '@tanstack/router-utils': 1.158.0 - '@tanstack/virtual-file-routes': 1.154.7 - prettier: 3.8.1 - recast: 0.23.11 - source-map: 0.7.6 - tsx: 4.21.0 - zod: 3.25.76 - transitivePeerDependencies: - - supports-color - - '@tanstack/router-plugin@1.161.1(@tanstack/react-router@1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@tanstack/router-core': 1.161.1 - '@tanstack/router-generator': 1.161.1 - '@tanstack/router-utils': 1.158.0 - '@tanstack/virtual-file-routes': 1.154.7 - chokidar: 3.6.0 - unplugin: 2.3.11 - zod: 3.25.76 - optionalDependencies: - '@tanstack/react-router': 1.161.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - - '@tanstack/router-utils@1.158.0': - dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - ansis: 4.2.0 - babel-dead-code-elimination: 1.0.12 - diff: 8.0.3 - pathe: 2.0.3 - tinyglobby: 0.2.15 - transitivePeerDependencies: - - supports-color - - '@tanstack/store@0.8.1': {} - - '@tanstack/virtual-file-routes@1.154.7': {} - - '@tokenizer/inflate@0.2.7': - dependencies: - debug: 4.4.3 - fflate: 0.8.2 - token-types: 6.1.2 - transitivePeerDependencies: - - supports-color - - '@tokenizer/token@0.3.0': {} - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/deep-eql@4.0.2': {} - - '@types/estree@1.0.8': {} - - '@types/http-cache-semantics@4.2.0': {} - - '@types/node@22.19.11': - dependencies: - undici-types: 6.21.0 - - '@types/node@25.3.0': - dependencies: - undici-types: 7.18.2 - - '@types/pg@8.16.0': - dependencies: - '@types/node': 25.3.0 - pg-protocol: 1.11.0 - pg-types: 2.2.0 - - '@types/react-dom@19.2.3(@types/react@19.2.14)': - dependencies: - '@types/react': 19.2.14 - - '@types/react@19.2.14': - dependencies: - csstype: 3.2.3 - - '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@rolldown/pluginutils': 1.0.0-rc.3 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@4.0.18': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - chai: 6.2.2 - tinyrainbow: 3.0.3 - - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.0.18 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.0.18 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - '@vitest/pretty-format@4.0.18': - dependencies: - tinyrainbow: 3.0.3 - - '@vitest/runner@4.0.18': - dependencies: - '@vitest/utils': 4.0.18 - pathe: 2.0.3 - - '@vitest/snapshot@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.0.18': {} - - '@vitest/ui@4.0.18(vitest@4.0.18)': - dependencies: - '@vitest/utils': 4.0.18 - fflate: 0.8.2 - flatted: 3.3.3 - pathe: 2.0.3 - sirv: 3.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.3.0)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - '@vitest/utils@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 - - '@xhmikosr/archive-type@7.1.0': - dependencies: - file-type: 20.5.0 - transitivePeerDependencies: - - supports-color - - '@xhmikosr/bin-check@7.1.0': - dependencies: - execa: 5.1.1 - isexe: 2.0.0 - - '@xhmikosr/bin-wrapper@13.2.0': - dependencies: - '@xhmikosr/bin-check': 7.1.0 - '@xhmikosr/downloader': 15.2.0 - '@xhmikosr/os-filter-obj': 3.0.0 - bin-version-check: 5.1.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/decompress-tar@8.1.0': - dependencies: - file-type: 20.5.0 - is-stream: 2.0.1 - tar-stream: 3.1.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/decompress-tarbz2@8.1.0': - dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - file-type: 20.5.0 - is-stream: 2.0.1 - seek-bzip: 2.0.0 - unbzip2-stream: 1.4.3 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/decompress-targz@8.1.0': - dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - file-type: 20.5.0 - is-stream: 2.0.1 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/decompress-unzip@7.1.0': - dependencies: - file-type: 20.5.0 - get-stream: 6.0.1 - yauzl: 3.2.0 - transitivePeerDependencies: - - supports-color - - '@xhmikosr/decompress@10.2.0': - dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - '@xhmikosr/decompress-tarbz2': 8.1.0 - '@xhmikosr/decompress-targz': 8.1.0 - '@xhmikosr/decompress-unzip': 7.1.0 - graceful-fs: 4.2.11 - strip-dirs: 3.0.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/downloader@15.2.0': - dependencies: - '@xhmikosr/archive-type': 7.1.0 - '@xhmikosr/decompress': 10.2.0 - content-disposition: 0.5.4 - defaults: 2.0.2 - ext-name: 5.0.0 - file-type: 20.5.0 - filenamify: 6.0.0 - get-stream: 6.0.1 - got: 13.0.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/os-filter-obj@3.0.0': - dependencies: - arch: 3.0.0 - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - - acorn@8.16.0: {} - - ansis@4.2.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arch@3.0.0: {} - - array-union@2.1.0: {} - - assertion-error@2.0.1: {} - - ast-types@0.16.1: - dependencies: - tslib: 2.8.1 - - atomic-sleep@1.0.0: {} - - b4a@1.8.0: {} - - babel-dead-code-elimination@1.0.12: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - balanced-match@1.0.2: {} - - balanced-match@4.0.3: {} - - bare-events@2.8.2: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.10.0: {} - - better-auth@1.4.18(drizzle-kit@0.31.9)(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(pg@8.18.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18): - dependencies: - '@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.2.1))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0) - '@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@4.2.1))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)) - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 - better-call: 1.1.8(zod@4.3.6) - defu: 6.1.4 - jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.0 - zod: 4.3.6 - optionalDependencies: - drizzle-kit: 0.31.9 - drizzle-orm: 0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8) - pg: 8.18.0 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - vitest: 4.0.18(@types/node@25.3.0)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - - better-call@1.1.8(zod@4.3.6): - dependencies: - '@better-auth/utils': 0.3.0 - '@better-fetch/fetch': 1.1.21 - rou3: 0.7.12 - set-cookie-parser: 2.7.2 - optionalDependencies: - zod: 4.3.6 - - bin-version-check@5.1.0: - dependencies: - bin-version: 6.0.0 - semver: 7.7.4 - semver-truncate: 3.0.0 - - bin-version@6.0.0: - dependencies: - execa: 5.1.1 - find-versions: 5.1.0 - - binary-extensions@2.3.0: {} - - bowser@2.14.1: {} - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - brace-expansion@5.0.2: - dependencies: - balanced-match: 4.0.3 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001770 - electron-to-chromium: 1.5.286 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - buffer-crc32@0.2.13: {} - - buffer-from@1.1.2: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bullmq@5.69.3: - dependencies: - cron-parser: 4.9.0 - ioredis: 5.9.2 - msgpackr: 1.11.5 - node-abort-controller: 3.1.1 - semver: 7.7.4 - tslib: 2.8.1 - uuid: 11.1.0 - transitivePeerDependencies: - - supports-color - - cacheable-lookup@7.0.0: {} - - cacheable-request@10.2.14: - dependencies: - '@types/http-cache-semantics': 4.2.0 - get-stream: 6.0.1 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - mimic-response: 4.0.0 - normalize-url: 8.1.1 - responselike: 3.0.0 - - caniuse-lite@1.0.30001770: {} - - chai@6.2.2: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - clsx@2.1.1: {} - - cluster-key-slot@1.1.2: {} - - colorette@2.0.20: {} - - commander@6.2.1: {} - - commander@8.3.0: {} - - commander@9.5.0: {} - - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - convert-source-map@2.0.0: {} - - cookie-es@2.0.0: {} - - cron-parser@4.9.0: - dependencies: - luxon: 3.7.2 - - cross-env@7.0.3: - dependencies: - cross-spawn: 7.0.6 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.2.3: {} - - dateformat@4.6.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - defaults@2.0.2: {} - - defer-to-connect@2.0.1: {} - - defu@6.1.4: {} - - denque@2.1.0: {} - - detect-libc@2.1.2: {} - - diff@8.0.3: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dotenv-expand@12.0.3: - dependencies: - dotenv: 16.6.1 - - dotenv@16.6.1: {} - - drizzle-kit@0.31.9: - dependencies: - '@drizzle-team/brocli': 0.10.2 - '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) - transitivePeerDependencies: - - supports-color - - drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8): - optionalDependencies: - '@types/pg': 8.16.0 - kysely: 0.28.11 - pg: 8.18.0 - postgres: 3.4.8 - - drizzle-zod@0.8.3(drizzle-orm@0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8))(zod@4.2.1): - dependencies: - drizzle-orm: 0.44.7(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8) - zod: 4.2.1 - - electron-to-chromium@1.5.286: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - enhanced-resolve@5.19.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 - - es-module-lexer@1.7.0: {} - - esbuild-register@3.6.0(esbuild@0.25.12): - dependencies: - debug: 4.4.3 - esbuild: 0.25.12 - transitivePeerDependencies: - - supports-color - - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - - esbuild@0.27.3: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 - - escalade@3.2.0: {} - - esprima@4.0.1: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - - event-target-shim@5.0.1: {} - - events-universal@1.0.1: - dependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - bare-abort-controller - - events@3.3.0: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - expect-type@1.3.0: {} - - ext-list@2.2.2: - dependencies: - mime-db: 1.54.0 - - ext-name@5.0.0: - dependencies: - ext-list: 2.2.2 - sort-keys-length: 1.0.1 - - fast-copy@4.0.2: {} - - fast-fifo@1.3.2: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-safe-stringify@2.1.1: {} - - fast-sha256@1.3.0: {} - - fast-xml-parser@5.3.6: - dependencies: - strnum: 2.1.2 - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - fetch-retry@6.0.0: {} - - fflate@0.8.2: {} - - file-type@20.5.0: - dependencies: - '@tokenizer/inflate': 0.2.7 - strtok3: 10.3.4 - token-types: 6.1.2 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color - - filename-reserved-regex@3.0.0: {} - - filenamify@6.0.0: - dependencies: - filename-reserved-regex: 3.0.0 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-versions@5.1.0: - dependencies: - semver-regex: 4.0.5 - - flatted@3.3.3: {} - - form-data-encoder@2.1.4: {} - - fsevents@2.3.3: - optional: true - - gensync@1.0.0-beta.2: {} - - get-stream@6.0.1: {} - - get-tsconfig@4.13.6: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@13.0.6: - dependencies: - minimatch: 10.2.2 - minipass: 7.1.3 - path-scurry: 2.0.2 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - globrex@0.1.2: {} - - goober@2.1.18(csstype@3.2.3): - dependencies: - csstype: 3.2.3 - - got@13.0.0: - dependencies: - '@sindresorhus/is': 5.6.0 - '@szmarczak/http-timer': 5.0.1 - cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 - decompress-response: 6.0.0 - form-data-encoder: 2.1.4 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 3.0.0 - - graceful-fs@4.2.11: {} - - help-me@5.0.0: {} - - hono-pino@0.7.2(hono@4.12.0)(pino@9.14.0): - dependencies: - defu: 6.1.4 - hono: 4.12.0 - pino: 9.14.0 - - hono@4.12.0: {} - - http-cache-semantics@4.2.0: {} - - http2-wrapper@2.2.1: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - human-signals@2.1.0: {} - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - inspect-with-kind@1.0.5: - dependencies: - kind-of: 6.0.3 - - ioredis@5.9.2: - dependencies: - '@ioredis/commands': 1.5.0 - cluster-key-slot: 1.1.2 - debug: 4.4.3 - denque: 2.1.0 - lodash.defaults: 4.2.0 - lodash.isarguments: 3.1.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - standard-as-callback: 2.1.0 - transitivePeerDependencies: - - supports-color - - ioredis@5.9.3: - dependencies: - '@ioredis/commands': 1.5.0 - cluster-key-slot: 1.1.2 - debug: 4.4.3 - denque: 2.1.0 - lodash.defaults: 4.2.0 - lodash.isarguments: 3.1.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - standard-as-callback: 2.1.0 - transitivePeerDependencies: - - supports-color - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-extglob@2.1.1: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-number@7.0.0: {} - - is-plain-obj@1.1.0: {} - - is-stream@2.0.1: {} - - isbot@5.1.35: {} - - isexe@2.0.0: {} - - jiti@2.6.1: {} - - jose@6.1.3: {} - - joycon@3.1.1: {} - - js-tokens@4.0.0: {} - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json5@2.2.3: {} - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kind-of@6.0.3: {} - - kysely@0.28.11: {} - - lightningcss-android-arm64@1.31.1: - optional: true - - lightningcss-darwin-arm64@1.31.1: - optional: true - - lightningcss-darwin-x64@1.31.1: - optional: true - - lightningcss-freebsd-x64@1.31.1: - optional: true - - lightningcss-linux-arm-gnueabihf@1.31.1: - optional: true - - lightningcss-linux-arm64-gnu@1.31.1: - optional: true - - lightningcss-linux-arm64-musl@1.31.1: - optional: true - - lightningcss-linux-x64-gnu@1.31.1: - optional: true - - lightningcss-linux-x64-musl@1.31.1: - optional: true - - lightningcss-win32-arm64-msvc@1.31.1: - optional: true - - lightningcss-win32-x64-msvc@1.31.1: - optional: true - - lightningcss@1.31.1: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - - lodash.defaults@4.2.0: {} - - lodash.isarguments@3.1.0: {} - - lowercase-keys@3.0.0: {} - - lru-cache@11.2.6: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lucide-react@0.553.0(react@19.2.4): - dependencies: - react: 19.2.4 - - luxon@3.7.2: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.54.0: {} - - mimic-fn@2.1.0: {} - - mimic-response@3.1.0: {} - - mimic-response@4.0.0: {} - - minimatch@10.2.2: - dependencies: - brace-expansion: 5.0.2 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@7.1.3: {} - - mrmime@2.0.1: {} - - ms@2.1.3: {} - - msgpackr-extract@3.0.3: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 - optional: true - - msgpackr@1.11.5: - optionalDependencies: - msgpackr-extract: 3.0.3 - - mylas@2.1.14: {} - - nanoid@3.3.11: {} - - nanoid@5.1.6: {} - - nanostores@1.1.0: {} - - neverthrow@8.2.0: - optionalDependencies: - '@rollup/rollup-linux-x64-gnu': 4.57.1 - - node-abort-controller@3.1.1: {} - - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.1.2 - optional: true - - node-releases@2.0.27: {} - - normalize-path@3.0.0: {} - - normalize-url@8.1.1: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - obug@2.1.1: {} - - on-exit-leak-free@2.1.2: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - openapi3-ts@4.5.0: - dependencies: - yaml: 2.8.2 - - p-cancelable@3.0.0: {} - - package-json-from-dist@1.0.1: {} - - path-key@3.1.1: {} - - path-scurry@2.0.2: - dependencies: - lru-cache: 11.2.6 - minipass: 7.1.3 - - path-type@4.0.0: {} - - pathe@2.0.3: {} - - pend@1.2.0: {} - - pg-cloudflare@1.3.0: - optional: true - - pg-connection-string@2.11.0: {} - - pg-int8@1.0.1: {} - - pg-pool@3.11.0(pg@8.18.0): - dependencies: - pg: 8.18.0 - - pg-protocol@1.11.0: {} - - pg-types@2.2.0: - dependencies: - pg-int8: 1.0.1 - postgres-array: 2.0.0 - postgres-bytea: 1.0.1 - postgres-date: 1.0.7 - postgres-interval: 1.2.0 - - pg@8.18.0: - dependencies: - pg-connection-string: 2.11.0 - pg-pool: 3.11.0(pg@8.18.0) - pg-protocol: 1.11.0 - pg-types: 2.2.0 - pgpass: 1.0.5 - optionalDependencies: - pg-cloudflare: 1.3.0 - - pgpass@1.0.5: - dependencies: - split2: 4.2.0 - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pino-abstract-transport@1.2.0: - dependencies: - readable-stream: 4.7.0 - split2: 4.2.0 - - pino-abstract-transport@2.0.0: - dependencies: - split2: 4.2.0 - - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@13.1.3: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 4.0.2 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pump: 3.0.3 - secure-json-parse: 4.1.0 - sonic-boom: 4.2.1 - strip-json-comments: 5.0.3 - - pino-std-serializers@7.1.0: {} - - pino@9.14.0: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 3.1.0 - - piscina@4.9.2: - optionalDependencies: - '@napi-rs/nice': 1.1.1 - - plimit-lit@1.6.1: - dependencies: - queue-lit: 1.5.2 - - postal-mime@2.7.3: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postgres-array@2.0.0: {} - - postgres-bytea@1.0.1: {} - - postgres-date@1.0.7: {} - - postgres-interval@1.2.0: - dependencies: - xtend: 4.0.2 - - postgres@3.4.8: {} - - prettier@3.8.1: {} - - process-warning@5.0.0: {} - - process@0.11.10: {} - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - queue-lit@1.5.2: {} - - queue-microtask@1.2.3: {} - - quick-format-unescaped@4.0.4: {} - - quick-lru@5.1.1: {} - - react-dom@19.2.4(react@19.2.4): - dependencies: - react: 19.2.4 - scheduler: 0.27.0 - - react-refresh@0.18.0: {} - - react@19.2.4: {} - - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - real-require@0.2.0: {} - - recast@0.23.11: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - - redis-errors@1.2.0: {} - - redis-parser@3.0.0: - dependencies: - redis-errors: 1.2.0 - - reselect@5.1.1: {} - - resend@6.9.2: - dependencies: - postal-mime: 2.7.3 - svix: 1.84.1 - - resolve-alpn@1.2.1: {} - - resolve-pkg-maps@1.0.0: {} - - responselike@3.0.0: - dependencies: - lowercase-keys: 3.0.0 - - reusify@1.1.0: {} - - rimraf@6.1.3: - dependencies: - glob: 13.0.6 - package-json-from-dist: 1.0.1 - - rollup@4.57.1: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.57.1 - '@rollup/rollup-android-arm64': 4.57.1 - '@rollup/rollup-darwin-arm64': 4.57.1 - '@rollup/rollup-darwin-x64': 4.57.1 - '@rollup/rollup-freebsd-arm64': 4.57.1 - '@rollup/rollup-freebsd-x64': 4.57.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 - '@rollup/rollup-linux-arm-musleabihf': 4.57.1 - '@rollup/rollup-linux-arm64-gnu': 4.57.1 - '@rollup/rollup-linux-arm64-musl': 4.57.1 - '@rollup/rollup-linux-loong64-gnu': 4.57.1 - '@rollup/rollup-linux-loong64-musl': 4.57.1 - '@rollup/rollup-linux-ppc64-gnu': 4.57.1 - '@rollup/rollup-linux-ppc64-musl': 4.57.1 - '@rollup/rollup-linux-riscv64-gnu': 4.57.1 - '@rollup/rollup-linux-riscv64-musl': 4.57.1 - '@rollup/rollup-linux-s390x-gnu': 4.57.1 - '@rollup/rollup-linux-x64-gnu': 4.57.1 - '@rollup/rollup-linux-x64-musl': 4.57.1 - '@rollup/rollup-openbsd-x64': 4.57.1 - '@rollup/rollup-openharmony-arm64': 4.57.1 - '@rollup/rollup-win32-arm64-msvc': 4.57.1 - '@rollup/rollup-win32-ia32-msvc': 4.57.1 - '@rollup/rollup-win32-x64-gnu': 4.57.1 - '@rollup/rollup-win32-x64-msvc': 4.57.1 - fsevents: 2.3.3 - - rou3@0.7.12: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-buffer@5.2.1: {} - - safe-stable-stringify@2.5.0: {} - - scheduler@0.27.0: {} - - secure-json-parse@4.1.0: {} - - seek-bzip@2.0.0: - dependencies: - commander: 6.2.1 - - semver-regex@4.0.5: {} - - semver-truncate@3.0.0: - dependencies: - semver: 7.7.4 - - semver@6.3.1: {} - - semver@7.7.4: {} - - seroval-plugins@1.5.0(seroval@1.5.0): - dependencies: - seroval: 1.5.0 - - seroval@1.5.0: {} - - set-cookie-parser@2.7.2: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - siginfo@2.0.0: {} - - signal-exit@3.0.7: {} - - sirv@3.0.2: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - slash@3.0.0: {} - - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - - sonner@2.0.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): - dependencies: - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - - sort-keys-length@1.0.1: - dependencies: - sort-keys: 1.1.2 - - sort-keys@1.1.2: - dependencies: - is-plain-obj: 1.1.0 - - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - source-map@0.7.6: {} - - split2@4.2.0: {} - - stackback@0.0.2: {} - - standard-as-callback@2.1.0: {} - - standardwebhooks@1.0.0: - dependencies: - '@stablelib/base64': 1.0.1 - fast-sha256: 1.3.0 - - std-env@3.10.0: {} - - stoker@2.0.1(@asteasolutions/zod-to-openapi@8.0.0(zod@4.2.1))(@hono/zod-openapi@1.2.2(hono@4.12.0)(zod@4.2.1))(hono@4.12.0)(openapi3-ts@4.5.0): - dependencies: - hono: 4.12.0 - optionalDependencies: - '@asteasolutions/zod-to-openapi': 8.0.0(zod@4.2.1) - '@hono/zod-openapi': 1.2.2(hono@4.12.0)(zod@4.2.1) - openapi3-ts: 4.5.0 - - streamx@2.23.0: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-dirs@3.0.0: - dependencies: - inspect-with-kind: 1.0.5 - is-plain-obj: 1.1.0 - - strip-final-newline@2.0.0: {} - - strip-json-comments@5.0.3: {} - - strnum@2.1.2: {} - - strtok3@10.3.4: - dependencies: - '@tokenizer/token': 0.3.0 - - svix@1.84.1: - dependencies: - standardwebhooks: 1.0.0 - uuid: 10.0.0 - - tabbable@6.4.0: {} - - tagged-tag@1.0.0: {} - - tailwind-merge@3.5.0: {} - - tailwindcss@4.2.0: {} - - tapable@2.3.0: {} - - tar-stream@3.1.7: - dependencies: - b4a: 1.8.0 - fast-fifo: 1.3.2 - streamx: 2.23.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - text-decoder@1.2.7: - dependencies: - b4a: 1.8.0 - transitivePeerDependencies: - - react-native-b4a - - thread-stream@3.1.0: - dependencies: - real-require: 0.2.0 - - through@2.3.8: {} - - tiny-invariant@1.3.3: {} - - tiny-warning@1.0.3: {} - - tinybench@2.9.0: {} - - tinyexec@1.0.2: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tinyrainbow@3.0.3: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - token-types@6.1.2: - dependencies: - '@borewit/text-codec': 0.2.1 - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - - totalist@3.0.1: {} - - tsc-alias@1.8.16: - dependencies: - chokidar: 3.6.0 - commander: 9.5.0 - get-tsconfig: 4.13.6 - globby: 11.1.0 - mylas: 2.1.14 - normalize-path: 3.0.0 - plimit-lit: 1.6.1 - - tsconfck@3.1.6(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - - tslib@2.8.1: {} - - tsx@4.21.0: - dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.13.6 - optionalDependencies: - fsevents: 2.3.3 - - tw-animate-css@1.4.0: {} - - type-fest@5.4.4: - dependencies: - tagged-tag: 1.0.0 - - typescript@5.9.3: {} - - uint8array-extras@1.5.0: {} - - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 - - undici-types@6.21.0: {} - - undici-types@7.18.2: {} - - unplugin@2.3.11: - dependencies: - '@jridgewell/remapping': 2.3.5 - acorn: 8.16.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - use-sync-external-store@1.6.0(react@19.2.4): - dependencies: - react: 19.2.4 - - uuid@10.0.0: {} - - uuid@11.1.0: {} - - vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)): - dependencies: - debug: 4.4.3 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.3) - optionalDependencies: - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - - typescript - - vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 22.19.11 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.31.1 - tsx: 4.21.0 - yaml: 2.8.2 - - vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.3.0 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.31.1 - tsx: 4.21.0 - yaml: 2.8.2 - - vitest@4.0.18(@types/node@22.19.11)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.19.11 - '@vitest/ui': 4.0.18(vitest@4.0.18) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - - vitest@4.0.18(@types/node@25.3.0)(@vitest/ui@4.0.18)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 25.3.0 - '@vitest/ui': 4.0.18(vitest@4.0.18) - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - - webpack-virtual-modules@0.6.2: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - wrappy@1.0.2: {} - - ws@8.19.0: {} - - xtend@4.0.2: {} - - yallist@3.1.1: {} - - yaml@2.8.2: {} - - yauzl@3.2.0: - dependencies: - buffer-crc32: 0.2.13 - pend: 1.2.0 - - zod@3.25.76: {} - - zod@4.2.1: {} - - zod@4.3.6: {} - - zustand@5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): - optionalDependencies: - '@types/react': 19.2.14 - react: 19.2.4 - use-sync-external-store: 1.6.0(react@19.2.4) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index eb3b003..808bfd2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,8 +1,3 @@ packages: - - apps/* - - packages/* - -onlyBuiltDependencies: - - '@swc/core' - - esbuild - - better-sqlite3 + - "packages/*" + - "apps/frontend" diff --git a/scripts/new-module.sh b/scripts/new-module.sh index 868b5f7..b8739b0 100755 --- a/scripts/new-module.sh +++ b/scripts/new-module.sh @@ -43,7 +43,6 @@ PASCAL=$(echo "$MODULE" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(su echo -e "\n${BOLD}Scaffolding module: ${MODULE}${RESET} (tags: ${PASCAL})\n" # ── Directory structure ───────────────────────────────────────────────────────── -mkdir -p "${MODULE_DIR}/usecases" mkdir -p "${MODULE_DIR}/__tests__" success "Created directory structure" @@ -117,8 +116,8 @@ export async function findAll(): Promise< EOF success "${MODULE}.repository.ts" -# ── usecases/${MODULE}.usecases.ts ───────────────────────────────────────────── -cat > "${MODULE_DIR}/usecases/${MODULE}.usecases.ts" << EOF +# ── ${MODULE}.usecases.ts ────────────────────────────────────────────────────── +cat > "${MODULE_DIR}/${MODULE}.usecases.ts" << EOF // Use-cases: functional core. // // Pure functions that receive already-loaded domain values and apply business rules. @@ -131,7 +130,7 @@ cat > "${MODULE_DIR}/usecases/${MODULE}.usecases.ts" << EOF // return ok(input); // } EOF -success "usecases/${MODULE}.usecases.ts" +success "${MODULE}.usecases.ts" # ── handlers.ts ──────────────────────────────────────────────────────────────── cat > "${MODULE_DIR}/handlers.ts" << EOF @@ -190,7 +189,7 @@ success "index.ts" # ── __tests__/handlers.test.ts ───────────────────────────────────────────────── cat > "${MODULE_DIR}/__tests__/handlers.test.ts" << EOF -import { describe, it } from "vitest"; +import { describe, it } from "@std/testing/bdd"; // Integration tests for the ${MODULE} handlers. // Import the router directly and use Hono's testClient to invoke routes @@ -202,9 +201,9 @@ describe("${MODULE} handlers", () => { EOF success "__tests__/handlers.test.ts" -# ── Auto-format with Biome ───────────────────────────────────────────────────── -if command -v pnpm >/dev/null 2>&1 && [[ -f "biome.json" ]]; then - pnpm exec biome check --write "${MODULE_DIR}" >/dev/null 2>&1 && success "Biome formatting applied" || warn "Biome check had warnings (non-fatal)" +# ── Auto-format with Deno ───────────────────────────────────────────────────── +if command -v deno >/dev/null 2>&1; then + deno fmt "${MODULE_DIR}" >/dev/null 2>&1 && success "Deno formatting applied" || warn "Deno fmt had warnings (non-fatal)" fi # ── Next steps ───────────────────────────────────────────────────────────────── @@ -225,6 +224,6 @@ echo " export const publicRoutes = [health, ${MODULE}];" echo "" echo -e "${BOLD}Then:${RESET}" echo " • Add your DB schema and table to packages/db/src/schema/" -echo " • Run pnpm db:generate && pnpm db:migrate" +echo " • Run deno task db:generate && deno task db:migrate" echo " • Flesh out ${MODULE}.repository.ts with real Drizzle queries" -echo " • Add use-cases to usecases/${MODULE}.usecases.ts as logic grows" +echo " • Add business logic to ${MODULE}.usecases.ts as needed" diff --git a/scripts/setup.sh b/scripts/setup.sh index b24336c..1e8d7c1 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -18,19 +18,19 @@ step() { echo -e "\n${BOLD}$*${RESET}"; } # ── Prerequisites ─────────────────────────────────────────────────────────────── step "Checking prerequisites..." -# Node.js — require ≥20 -if ! command -v node >/dev/null 2>&1; then - error "Node.js not found. Install v20+ from https://nodejs.org" +# Deno — require ≥2 +if ! command -v deno >/dev/null 2>&1; then + error "Deno not found. Install v2+ from https://deno.com" exit 1 fi -NODE_MAJOR=$(node -e "process.stdout.write(String(process.versions.node.split('.')[0]))") -if [[ "$NODE_MAJOR" -lt 20 ]]; then - error "Node.js v${NODE_MAJOR} found — v20 or higher required." +DENO_MAJOR=$(deno --version | head -1 | grep -oE '[0-9]+' | head -1) +if [[ "$DENO_MAJOR" -lt 2 ]]; then + error "Deno v${DENO_MAJOR} found — v2 or higher required." exit 1 fi -success "Node.js $(node --version)" +success "Deno $(deno --version | head -1)" -# pnpm +# pnpm (still needed for frontend deps) if ! command -v pnpm >/dev/null 2>&1; then error "pnpm not found. Install: npm i -g pnpm" exit 1 @@ -106,9 +106,8 @@ else fi # ── Build packages ───────────────────────────────────────────────────────────── -step "Building shared packages..." -pnpm build:packages -success "Packages built" +step "Verifying packages..." +info "Shared packages are resolved via Deno workspace — no build step needed." # ── Done ──────────────────────────────────────────────────────────────────────── echo "" @@ -116,10 +115,12 @@ echo -e "${GREEN}${BOLD}Setup complete!${RESET}" echo "" echo "Next steps:" echo " 1. Edit .env — set DATABASE_URL (and REDIS_URL if needed)" -echo " 2. Run migrations: pnpm db:migrate" -echo " 3. Start dev: pnpm dev" +echo " 2. Run migrations: deno task db:migrate" +echo " 3. Start backend: deno task dev" +echo " 4. Start frontend: deno task dev:frontend (in a separate terminal)" echo "" echo "Other useful commands:" -echo " pnpm build Build all apps" -echo " pnpm test Run all tests" -echo " pnpm db:studio Open Drizzle Studio" +echo " deno task dev:frontend Start frontend (Vite)" +echo " deno test -A Run all tests" +echo " deno lint Lint backend" +echo " deno task db:studio Open Drizzle Studio" diff --git a/tsconfig.base.json b/tsconfig.base.json deleted file mode 100644 index 070cfaf..0000000 --- a/tsconfig.base.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "node16", - "lib": ["ESNext"], - "strict": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "incremental": true, - "composite": true, - "noUncheckedIndexedAccess": true, - "useUnknownInCatchVariables": true - }, - "exclude": ["node_modules", "dist"] -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 96e7adc..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "noEmit": true, - "module": "Node16", - "moduleResolution": "Node16" - }, - "references": [ - { "path": "./packages/db" }, - { "path": "./packages/shared" }, - { "path": "./packages/email-templates" } - ] -}